1 /*
   2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.internal.xjc.generator.bean;
  27 
  28 import static com.sun.tools.internal.xjc.outline.Aspect.EXPOSED;
  29 
  30 import java.io.Serializable;
  31 import java.net.URL;
  32 import java.util.Collection;
  33 import java.util.HashMap;
  34 import java.util.LinkedHashMap;
  35 import java.util.HashSet;
  36 import java.util.Iterator;
  37 import java.util.Map;
  38 import java.util.Set;
  39 import java.util.TreeSet;
  40 
  41 import javax.xml.bind.JAXBContext;
  42 import javax.xml.bind.JAXBException;
  43 import javax.xml.bind.annotation.XmlAttachmentRef;
  44 import javax.xml.bind.annotation.XmlID;
  45 import javax.xml.bind.annotation.XmlIDREF;
  46 import javax.xml.bind.annotation.XmlMimeType;
  47 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
  48 import javax.xml.namespace.QName;
  49 
  50 import com.sun.codemodel.internal.ClassType;
  51 import com.sun.codemodel.internal.JAnnotatable;
  52 import com.sun.codemodel.internal.JClass;
  53 import com.sun.codemodel.internal.JClassAlreadyExistsException;
  54 import com.sun.codemodel.internal.JClassContainer;
  55 import com.sun.codemodel.internal.JCodeModel;
  56 import com.sun.codemodel.internal.JDefinedClass;
  57 import com.sun.codemodel.internal.JEnumConstant;
  58 import com.sun.codemodel.internal.JExpr;
  59 import com.sun.codemodel.internal.JExpression;
  60 import com.sun.codemodel.internal.JFieldVar;
  61 import com.sun.codemodel.internal.JForEach;
  62 import com.sun.codemodel.internal.JInvocation;
  63 import com.sun.codemodel.internal.JJavaName;
  64 import com.sun.codemodel.internal.JMethod;
  65 import com.sun.codemodel.internal.JMod;
  66 import com.sun.codemodel.internal.JPackage;
  67 import com.sun.codemodel.internal.JType;
  68 import com.sun.codemodel.internal.JVar;
  69 import com.sun.codemodel.internal.fmt.JStaticJavaFile;
  70 import com.sun.tools.internal.xjc.AbortException;
  71 import com.sun.tools.internal.xjc.ErrorReceiver;
  72 import com.sun.tools.internal.xjc.api.SpecVersion;
  73 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlAnyAttributeWriter;
  74 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlEnumValueWriter;
  75 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlEnumWriter;
  76 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlJavaTypeAdapterWriter;
  77 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlMimeTypeWriter;
  78 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlRootElementWriter;
  79 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlSeeAlsoWriter;
  80 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlTypeWriter;
  81 import com.sun.tools.internal.xjc.generator.bean.field.FieldRenderer;
  82 import com.sun.tools.internal.xjc.model.CAdapter;
  83 import com.sun.tools.internal.xjc.model.CAttributePropertyInfo;
  84 import com.sun.tools.internal.xjc.model.CClassInfo;
  85 import com.sun.tools.internal.xjc.model.CClassInfoParent;
  86 import com.sun.tools.internal.xjc.model.CElementInfo;
  87 import com.sun.tools.internal.xjc.model.CEnumConstant;
  88 import com.sun.tools.internal.xjc.model.CEnumLeafInfo;
  89 import com.sun.tools.internal.xjc.model.CPropertyInfo;
  90 import com.sun.tools.internal.xjc.model.CTypeRef;
  91 import com.sun.tools.internal.xjc.model.Model;
  92 import com.sun.tools.internal.xjc.model.CClassRef;
  93 import com.sun.tools.internal.xjc.outline.Aspect;
  94 import com.sun.tools.internal.xjc.outline.ClassOutline;
  95 import com.sun.tools.internal.xjc.outline.EnumConstantOutline;
  96 import com.sun.tools.internal.xjc.outline.EnumOutline;
  97 import com.sun.tools.internal.xjc.outline.FieldOutline;
  98 import com.sun.tools.internal.xjc.outline.Outline;
  99 import com.sun.tools.internal.xjc.outline.PackageOutline;
 100 import com.sun.tools.internal.xjc.util.CodeModelClassFactory;
 101 import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
 102 import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapterMarker;
 103 import com.sun.xml.internal.xsom.XmlString;
 104 import com.sun.istack.internal.NotNull;
 105 import com.sun.tools.internal.xjc.model.CReferencePropertyInfo;
 106 
 107 /**
 108  * Generates fields and accessors.
 109  */
 110 public final class BeanGenerator implements Outline {
 111 
 112     /** Simplifies class/interface creation and collision detection. */
 113     private final CodeModelClassFactory codeModelClassFactory;
 114     private final ErrorReceiver errorReceiver;
 115     /** all {@link PackageOutline}s keyed by their {@link PackageOutline#_package}. */
 116     private final Map<JPackage, PackageOutlineImpl> packageContexts = new LinkedHashMap<JPackage, PackageOutlineImpl>();
 117     /** all {@link ClassOutline}s keyed by their {@link ClassOutline#target}. */
 118     private final Map<CClassInfo, ClassOutlineImpl> classes = new LinkedHashMap<CClassInfo, ClassOutlineImpl>();
 119     /** all {@link EnumOutline}s keyed by their {@link EnumOutline#target}. */
 120     private final Map<CEnumLeafInfo, EnumOutline> enums = new LinkedHashMap<CEnumLeafInfo, EnumOutline>();
 121     /**
 122      * Generated runtime classes.
 123      */
 124     private final Map<Class, JClass> generatedRuntime = new LinkedHashMap<Class, JClass>();
 125     /** the model object which we are processing. */
 126     private final Model model;
 127     private final JCodeModel codeModel;
 128     /**
 129      * for each property, the information about the generated field.
 130      */
 131     private final Map<CPropertyInfo, FieldOutline> fields = new LinkedHashMap<CPropertyInfo, FieldOutline>();
 132     /**
 133      * elements that generate classes to the generated classes.
 134      */
 135     /*package*/ final Map<CElementInfo, ElementOutlineImpl> elements = new LinkedHashMap<CElementInfo, ElementOutlineImpl>();
 136 
 137     /**
 138      * Generates beans into code model according to the BGM,
 139      * and produces the reflection model.
 140      *
 141      * @param _errorReceiver
 142      *      This object will receive all the errors discovered
 143      *      during the back-end stage.
 144      *
 145      * @return
 146      *      returns a {@link Outline} which will in turn
 147      *      be used to further generate marshaller/unmarshaller,
 148      *      or null if the processing fails (errors should have been
 149      *      reported to the error recevier.)
 150      */
 151     public static Outline generate(Model model, ErrorReceiver _errorReceiver) {
 152 
 153         try {
 154             return new BeanGenerator(model, _errorReceiver);
 155         } catch (AbortException e) {
 156             return null;
 157         }
 158     }
 159 
 160     private BeanGenerator(Model _model, ErrorReceiver _errorReceiver) {
 161 
 162         this.model = _model;
 163         this.codeModel = model.codeModel;
 164         this.errorReceiver = _errorReceiver;
 165         this.codeModelClassFactory = new CodeModelClassFactory(errorReceiver);
 166 
 167         // build enum classes
 168         for (CEnumLeafInfo p : model.enums().values()) {
 169             enums.put(p, generateEnumDef(p));
 170         }
 171 
 172         JPackage[] packages = getUsedPackages(EXPOSED);
 173 
 174         // generates per-package code and remember the results as contexts.
 175         for (JPackage pkg : packages) {
 176             getPackageContext(pkg);
 177         }
 178 
 179         // create the class definitions for all the beans first.
 180         // this should also fill in PackageContext#getClasses
 181         for (CClassInfo bean : model.beans().values()) {
 182             getClazz(bean);
 183         }
 184 
 185         // compute the package-level setting
 186         for (PackageOutlineImpl p : packageContexts.values()) {
 187             p.calcDefaultValues();
 188         }
 189 
 190         JClass OBJECT = codeModel.ref(Object.class);
 191 
 192         // inheritance relationship needs to be set before we generate fields, or otherwise
 193         // we'll fail to compute the correct type signature (namely the common base type computation)
 194         for (ClassOutlineImpl cc : getClasses()) {
 195 
 196             // setup inheritance between implementation hierarchy.
 197             CClassInfo superClass = cc.target.getBaseClass();
 198             if (superClass != null) {
 199                 // use the specified super class
 200                 model.strategy._extends(cc, getClazz(superClass));
 201             } else {
 202                 CClassRef refSuperClass = cc.target.getRefBaseClass();
 203                 if (refSuperClass != null) {
 204                     cc.implClass._extends(refSuperClass.toType(this, EXPOSED));
 205                 } else {
 206                     // use the default one, if any
 207                     if (model.rootClass != null && cc.implClass._extends().equals(OBJECT)) {
 208                         cc.implClass._extends(model.rootClass);
 209                     }
 210                     if (model.rootInterface != null) {
 211                         cc.ref._implements(model.rootInterface);
 212                     }
 213                 }
 214             }
 215 
 216             // if serialization support is turned on, generate
 217             // [RESULT]
 218             // class ... implements Serializable {
 219             //     private static final long serialVersionUID = <id>;
 220             //     ....
 221             // }
 222             if (model.serializable) {
 223                 cc.implClass._implements(Serializable.class);
 224                 if (model.serialVersionUID != null) {
 225                     cc.implClass.field(
 226                             JMod.PRIVATE | JMod.STATIC | JMod.FINAL,
 227                             codeModel.LONG,
 228                             "serialVersionUID",
 229                             JExpr.lit(model.serialVersionUID));
 230                 }
 231             }
 232 
 233             CClassInfoParent base = cc.target.parent();
 234             if ((base != null) && (base instanceof CClassInfo)) {
 235                 String pkg = base.getOwnerPackage().name();
 236                 String shortName = base.fullName().substring(base.fullName().indexOf(pkg)+pkg.length()+1);
 237                 if (cc.target.shortName.equals(shortName)) {
 238                     getErrorReceiver().error(cc.target.getLocator(), Messages.ERR_KEYNAME_COLLISION.format(shortName));
 239                 }
 240             }
 241 
 242         }
 243 
 244         // fill in implementation classes
 245         for (ClassOutlineImpl co : getClasses()) {
 246             generateClassBody(co);
 247         }
 248 
 249         for (EnumOutline eo : enums.values()) {
 250             generateEnumBody(eo);
 251         }
 252 
 253         // create factories for the impl-less elements
 254         for (CElementInfo ei : model.getAllElements()) {
 255             getPackageContext(ei._package()).objectFactoryGenerator().populate(ei);
 256         }
 257 
 258         if (model.options.debugMode) {
 259             generateClassList();
 260         }
 261     }
 262 
 263     /**
 264      * Generates a class that knows how to create an instance of JAXBContext
 265      *
 266      * <p>
 267      * This is used in the debug mode so that a new properly configured
 268      * {@link JAXBContext} object can be used.
 269      */
 270     @SuppressWarnings("CallToThreadDumpStack")
 271     private void generateClassList() {
 272         try {
 273             JDefinedClass jc = codeModel.rootPackage()._class("JAXBDebug");
 274             JMethod m = jc.method(JMod.PUBLIC | JMod.STATIC, JAXBContext.class, "createContext");
 275             JVar $classLoader = m.param(ClassLoader.class, "classLoader");
 276             m._throws(JAXBException.class);
 277             JInvocation inv = codeModel.ref(JAXBContext.class).staticInvoke("newInstance");
 278             m.body()._return(inv);
 279 
 280             switch (model.strategy) {
 281                 case INTF_AND_IMPL: {
 282                     StringBuilder buf = new StringBuilder();
 283                     for (PackageOutlineImpl po : packageContexts.values()) {
 284                         if (buf.length() > 0) {
 285                             buf.append(':');
 286                         }
 287                         buf.append(po._package().name());
 288                     }
 289                     inv.arg(buf.toString()).arg($classLoader);
 290                     break;
 291                 }
 292                 case BEAN_ONLY:
 293                     for (ClassOutlineImpl cc : getClasses()) {
 294                         inv.arg(cc.implRef.dotclass());
 295                     }
 296                     for (PackageOutlineImpl po : packageContexts.values()) {
 297                         inv.arg(po.objectFactory().dotclass());
 298                     }
 299                     break;
 300                 default:
 301                     throw new IllegalStateException();
 302             }
 303         } catch (JClassAlreadyExistsException e) {
 304             e.printStackTrace();
 305             // after all, we are in the debug mode. a little sloppiness is OK.
 306             // this error is not fatal. just continue.
 307         }
 308     }
 309 
 310     public Model getModel() {
 311         return model;
 312     }
 313 
 314     public JCodeModel getCodeModel() {
 315         return codeModel;
 316     }
 317 
 318     public JClassContainer getContainer(CClassInfoParent parent, Aspect aspect) {
 319         CClassInfoParent.Visitor<JClassContainer> v;
 320         switch (aspect) {
 321             case EXPOSED:
 322                 v = exposedContainerBuilder;
 323                 break;
 324             case IMPLEMENTATION:
 325                 v = implContainerBuilder;
 326                 break;
 327             default:
 328                 assert false;
 329                 throw new IllegalStateException();
 330         }
 331         return parent.accept(v);
 332     }
 333 
 334     public final JType resolve(CTypeRef ref, Aspect a) {
 335         return ref.getTarget().getType().toType(this, a);
 336     }
 337     private final CClassInfoParent.Visitor<JClassContainer> exposedContainerBuilder =
 338             new CClassInfoParent.Visitor<JClassContainer>() {
 339 
 340                 public JClassContainer onBean(CClassInfo bean) {
 341                     return getClazz(bean).ref;
 342                 }
 343 
 344                 public JClassContainer onElement(CElementInfo element) {
 345                     // hmm...
 346                     return getElement(element).implClass;
 347                 }
 348 
 349                 public JClassContainer onPackage(JPackage pkg) {
 350                     return model.strategy.getPackage(pkg, EXPOSED);
 351                 }
 352             };
 353     private final CClassInfoParent.Visitor<JClassContainer> implContainerBuilder =
 354             new CClassInfoParent.Visitor<JClassContainer>() {
 355 
 356                 public JClassContainer onBean(CClassInfo bean) {
 357                     return getClazz(bean).implClass;
 358                 }
 359 
 360                 public JClassContainer onElement(CElementInfo element) {
 361                     return getElement(element).implClass;
 362                 }
 363 
 364                 public JClassContainer onPackage(JPackage pkg) {
 365                     return model.strategy.getPackage(pkg, Aspect.IMPLEMENTATION);
 366                 }
 367             };
 368 
 369     /**
 370      * Returns all <i>used</i> JPackages.
 371      *
 372      * A JPackage is considered as "used" if a ClassItem or
 373      * a InterfaceItem resides in that package.
 374      *
 375      * This value is dynamically calculated every time because
 376      * one can freely remove ClassItem/InterfaceItem.
 377      *
 378      * @return
 379      *         Given the same input, the order of packages in the array
 380      *         is always the same regardless of the environment.
 381      */
 382     public final JPackage[] getUsedPackages(Aspect aspect) {
 383         Set<JPackage> s = new TreeSet<JPackage>();
 384 
 385         for (CClassInfo bean : model.beans().values()) {
 386             JClassContainer cont = getContainer(bean.parent(), aspect);
 387             if (cont.isPackage()) {
 388                 s.add((JPackage) cont);
 389             }
 390         }
 391 
 392         for (CElementInfo e : model.getElementMappings(null).values()) {
 393             // at the first glance you might think we should be iterating all elements,
 394             // not just global ones, but if you think about it, local ones live inside
 395             // another class, so those packages are already enumerated when we were
 396             // walking over CClassInfos.
 397             s.add(e._package());
 398         }
 399 
 400         return s.toArray(new JPackage[s.size()]);
 401     }
 402 
 403     public ErrorReceiver getErrorReceiver() {
 404         return errorReceiver;
 405     }
 406 
 407     public CodeModelClassFactory getClassFactory() {
 408         return codeModelClassFactory;
 409     }
 410 
 411     public PackageOutlineImpl getPackageContext(JPackage p) {
 412         PackageOutlineImpl r = packageContexts.get(p);
 413         if (r == null) {
 414             r = new PackageOutlineImpl(this, model, p);
 415             packageContexts.put(p, r);
 416         }
 417         return r;
 418     }
 419 
 420     /**
 421      * Generates the minimum {@link JDefinedClass} skeleton
 422      * without filling in its body.
 423      */
 424     private ClassOutlineImpl generateClassDef(CClassInfo bean) {
 425         ImplStructureStrategy.Result r = model.strategy.createClasses(this, bean);
 426         JClass implRef;
 427 
 428         if (bean.getUserSpecifiedImplClass() != null) {
 429             // create a place holder for a user-specified class.
 430             JDefinedClass usr;
 431             try {
 432                 usr = codeModel._class(bean.getUserSpecifiedImplClass());
 433                 // but hide that file so that it won't be generated.
 434                 usr.hide();
 435             } catch (JClassAlreadyExistsException e) {
 436                 // it's OK for this to collide.
 437                 usr = e.getExistingClass();
 438             }
 439             usr._extends(r.implementation);
 440             implRef = usr;
 441         } else {
 442             implRef = r.implementation;
 443         }
 444 
 445         return new ClassOutlineImpl(this, bean, r.exposed, r.implementation, implRef);
 446     }
 447 
 448     public Collection<ClassOutlineImpl> getClasses() {
 449         // make sure that classes are fully populated
 450         assert model.beans().size() == classes.size();
 451         return classes.values();
 452     }
 453 
 454     public ClassOutlineImpl getClazz(CClassInfo bean) {
 455         ClassOutlineImpl r = classes.get(bean);
 456         if (r == null) {
 457             classes.put(bean, r = generateClassDef(bean));
 458         }
 459         return r;
 460     }
 461 
 462     public ElementOutlineImpl getElement(CElementInfo ei) {
 463         ElementOutlineImpl def = elements.get(ei);
 464         if (def == null && ei.hasClass()) {
 465             // create one. in the constructor it adds itself to the elements.
 466             def = new ElementOutlineImpl(this, ei);
 467         }
 468         return def;
 469     }
 470 
 471     public EnumOutline getEnum(CEnumLeafInfo eli) {
 472         return enums.get(eli);
 473     }
 474 
 475     public Collection<EnumOutline> getEnums() {
 476         return enums.values();
 477     }
 478 
 479     public Iterable<? extends PackageOutline> getAllPackageContexts() {
 480         return packageContexts.values();
 481     }
 482 
 483     public FieldOutline getField(CPropertyInfo prop) {
 484         return fields.get(prop);
 485     }
 486 
 487     /**
 488      * Generates the body of a class.
 489      *
 490      */
 491     private void generateClassBody(ClassOutlineImpl cc) {
 492         CClassInfo target = cc.target;
 493 
 494         // used to simplify the generated annotations
 495         String mostUsedNamespaceURI = cc._package().getMostUsedNamespaceURI();
 496 
 497         // [RESULT]
 498         // @XmlType(name="foo", targetNamespace="bar://baz")
 499         XmlTypeWriter xtw = cc.implClass.annotate2(XmlTypeWriter.class);
 500         writeTypeName(cc.target.getTypeName(), xtw, mostUsedNamespaceURI);
 501 
 502         if (model.options.target.isLaterThan(SpecVersion.V2_1)) {
 503             // @XmlSeeAlso
 504             Iterator<CClassInfo> subclasses = cc.target.listSubclasses();
 505             if (subclasses.hasNext()) {
 506                 XmlSeeAlsoWriter saw = cc.implClass.annotate2(XmlSeeAlsoWriter.class);
 507                 while (subclasses.hasNext()) {
 508                     CClassInfo s = subclasses.next();
 509                     saw.value(getClazz(s).implRef);
 510                 }
 511             }
 512         }
 513 
 514         if (target.isElement()) {
 515             String namespaceURI = target.getElementName().getNamespaceURI();
 516             String localPart = target.getElementName().getLocalPart();
 517 
 518             // [RESULT]
 519             // @XmlRootElement(name="foo", targetNamespace="bar://baz")
 520             XmlRootElementWriter xrew = cc.implClass.annotate2(XmlRootElementWriter.class);
 521             xrew.name(localPart);
 522             if (!namespaceURI.equals(mostUsedNamespaceURI)) // only generate if necessary
 523             {
 524                 xrew.namespace(namespaceURI);
 525             }
 526         }
 527 
 528         if (target.isOrdered()) {
 529             for (CPropertyInfo p : target.getProperties()) {
 530                 if (!(p instanceof CAttributePropertyInfo)) {
 531                     if (!((p instanceof CReferencePropertyInfo)
 532                             && ((CReferencePropertyInfo) p).isDummy())) {
 533                         xtw.propOrder(p.getName(false));
 534                     }
 535                 }
 536             }
 537         } else {
 538             // produce empty array
 539             xtw.getAnnotationUse().paramArray("propOrder");
 540         }
 541 
 542         for (CPropertyInfo prop : target.getProperties()) {
 543             generateFieldDecl(cc, prop);
 544         }
 545 
 546         if (target.declaresAttributeWildcard()) {
 547             generateAttributeWildcard(cc);
 548         }
 549 
 550         // generate some class level javadoc
 551         cc.ref.javadoc().append(target.javadoc);
 552 
 553         cc._package().objectFactoryGenerator().populate(cc);
 554     }
 555 
 556     private void writeTypeName(QName typeName, XmlTypeWriter xtw, String mostUsedNamespaceURI) {
 557         if (typeName == null) {
 558             xtw.name("");
 559         } else {
 560             xtw.name(typeName.getLocalPart());
 561             final String typeNameURI = typeName.getNamespaceURI();
 562             if (!typeNameURI.equals(mostUsedNamespaceURI)) // only generate if necessary
 563             {
 564                 xtw.namespace(typeNameURI);
 565             }
 566         }
 567     }
 568 
 569     /**
 570      * Generates an attribute wildcard property on a class.
 571      */
 572     private void generateAttributeWildcard(ClassOutlineImpl cc) {
 573         String FIELD_NAME = "otherAttributes";
 574         String METHOD_SEED = model.getNameConverter().toClassName(FIELD_NAME);
 575 
 576         JClass mapType = codeModel.ref(Map.class).narrow(QName.class, String.class);
 577         JClass mapImpl = codeModel.ref(HashMap.class).narrow(QName.class, String.class);
 578 
 579         // [RESULT]
 580         // Map<QName,String> m = new HashMap<QName,String>();
 581         JFieldVar $ref = cc.implClass.field(JMod.PRIVATE,
 582                 mapType, FIELD_NAME, JExpr._new(mapImpl));
 583         $ref.annotate2(XmlAnyAttributeWriter.class);
 584 
 585         MethodWriter writer = cc.createMethodWriter();
 586 
 587         JMethod $get = writer.declareMethod(mapType, "get" + METHOD_SEED);
 588         $get.javadoc().append(
 589                 "Gets a map that contains attributes that aren't bound to any typed property on this class.\n\n"
 590                 + "<p>\n"
 591                 + "the map is keyed by the name of the attribute and \n"
 592                 + "the value is the string value of the attribute.\n"
 593                 + "\n"
 594                 + "the map returned by this method is live, and you can add new attribute\n"
 595                 + "by updating the map directly. Because of this design, there's no setter.\n");
 596         $get.javadoc().addReturn().append("always non-null");
 597 
 598         $get.body()._return($ref);
 599     }
 600 
 601     /**
 602      * Generates the minimum {@link JDefinedClass} skeleton
 603      * without filling in its body.
 604      */
 605     private EnumOutline generateEnumDef(CEnumLeafInfo e) {
 606         JDefinedClass type;
 607 
 608         type = getClassFactory().createClass(
 609                 getContainer(e.parent, EXPOSED), e.shortName, e.getLocator(), ClassType.ENUM);
 610         type.javadoc().append(e.javadoc);
 611 
 612         return new EnumOutline(e, type) {
 613 
 614             @Override
 615             public
 616             @NotNull
 617             Outline parent() {
 618                 return BeanGenerator.this;
 619             }
 620         };
 621     }
 622 
 623     private void generateEnumBody(EnumOutline eo) {
 624         JDefinedClass type = eo.clazz;
 625         CEnumLeafInfo e = eo.target;
 626 
 627         XmlTypeWriter xtw = type.annotate2(XmlTypeWriter.class);
 628         writeTypeName(e.getTypeName(), xtw,
 629                 eo._package().getMostUsedNamespaceURI());
 630 
 631         JCodeModel cModel = model.codeModel;
 632 
 633         // since constant values are never null, no point in using the boxed types.
 634         JType baseExposedType = e.base.toType(this, EXPOSED).unboxify();
 635         JType baseImplType = e.base.toType(this, Aspect.IMPLEMENTATION).unboxify();
 636 
 637 
 638         XmlEnumWriter xew = type.annotate2(XmlEnumWriter.class);
 639         xew.value(baseExposedType);
 640 
 641 
 642         boolean needsValue = e.needsValueField();
 643 
 644         // for each member <m>,
 645         // [RESULT]
 646         //    <EnumName>(<deserializer of m>(<value>));
 647 
 648         Set<String> enumFieldNames = new HashSet<String>();    // record generated field names to detect collision
 649 
 650         for (CEnumConstant mem : e.members) {
 651             String constName = mem.getName();
 652 
 653             if (!JJavaName.isJavaIdentifier(constName)) {
 654                 // didn't produce a name.
 655                 getErrorReceiver().error(e.getLocator(),
 656                         Messages.ERR_UNUSABLE_NAME.format(mem.getLexicalValue(), constName));
 657             }
 658 
 659             if (!enumFieldNames.add(constName)) {
 660                 getErrorReceiver().error(e.getLocator(), Messages.ERR_NAME_COLLISION.format(constName));
 661             }
 662 
 663             // [RESULT]
 664             // <Const>(...)
 665             // ASSUMPTION: datatype is outline-independent
 666             JEnumConstant constRef = type.enumConstant(constName);
 667             if (needsValue) {
 668                 constRef.arg(e.base.createConstant(this, new XmlString(mem.getLexicalValue())));
 669             }
 670 
 671             if (!mem.getLexicalValue().equals(constName)) {
 672                 constRef.annotate2(XmlEnumValueWriter.class).value(mem.getLexicalValue());
 673             }
 674 
 675             // set javadoc
 676             if (mem.javadoc != null) {
 677                 constRef.javadoc().append(mem.javadoc);
 678             }
 679 
 680             eo.constants.add(new EnumConstantOutline(mem, constRef) {
 681             });
 682         }
 683 
 684 
 685         if (needsValue) {
 686             // [RESULT]
 687             // final <valueType> value;
 688             JFieldVar $value = type.field(JMod.PRIVATE | JMod.FINAL, baseExposedType, "value");
 689 
 690             // [RESULT]
 691             // public <valuetype> value() { return value; }
 692             type.method(JMod.PUBLIC, baseExposedType, "value").body()._return($value);
 693 
 694             // [RESULT]
 695             // <constructor>(<valueType> v) {
 696             //     this.value=v;
 697             // }
 698             {
 699                 JMethod m = type.constructor(0);
 700                 m.body().assign($value, m.param(baseImplType, "v"));
 701             }
 702 
 703             // [RESULT]
 704             // public static <Const> fromValue(<valueType> v) {
 705             //   for( <Const> c : <Const>.values() ) {
 706             //       if(c.value == v)   // or equals
 707             //           return c;
 708             //   }
 709             //   throw new IllegalArgumentException(...);
 710             // }
 711             {
 712                 JMethod m = type.method(JMod.PUBLIC | JMod.STATIC, type, "fromValue");
 713                 JVar $v = m.param(baseExposedType, "v");
 714                 JForEach fe = m.body().forEach(type, "c", type.staticInvoke("values"));
 715                 JExpression eq;
 716                 if (baseExposedType.isPrimitive()) {
 717                     eq = fe.var().ref($value).eq($v);
 718                 } else {
 719                     eq = fe.var().ref($value).invoke("equals").arg($v);
 720                 }
 721 
 722                 fe.body()._if(eq)._then()._return(fe.var());
 723 
 724                 JInvocation ex = JExpr._new(cModel.ref(IllegalArgumentException.class));
 725 
 726                 JExpression strForm;
 727                 if (baseExposedType.isPrimitive()) {
 728                     strForm = cModel.ref(String.class).staticInvoke("valueOf").arg($v);
 729                 } else if (baseExposedType == cModel.ref(String.class)) {
 730                     strForm = $v;
 731                 } else {
 732                     strForm = $v.invoke("toString");
 733                 }
 734                 m.body()._throw(ex.arg(strForm));
 735             }
 736         } else {
 737             // [RESULT]
 738             // public String value() { return name(); }
 739             type.method(JMod.PUBLIC, String.class, "value").body()._return(JExpr.invoke("name"));
 740 
 741             // [RESULT]
 742             // public <Const> fromValue(String v) { return valueOf(v); }
 743             JMethod m = type.method(JMod.PUBLIC | JMod.STATIC, type, "fromValue");
 744             m.body()._return(JExpr.invoke("valueOf").arg(m.param(String.class, "v")));
 745         }
 746     }
 747 
 748     /**
 749      * Determines the FieldRenderer used for the given FieldUse,
 750      * then generates the field declaration and accessor methods.
 751      *
 752      * The <code>fields</code> map will be updated with the newly
 753      * created FieldRenderer.
 754      */
 755     private FieldOutline generateFieldDecl(ClassOutlineImpl cc, CPropertyInfo prop) {
 756         FieldRenderer fr = prop.realization;
 757         if (fr == null) // none is specified. use the default factory
 758         {
 759             fr = model.options.getFieldRendererFactory().getDefault();
 760         }
 761 
 762         FieldOutline field = fr.generate(cc, prop);
 763         fields.put(prop, field);
 764 
 765         return field;
 766     }
 767 
 768     /**
 769      * Generates {@link XmlJavaTypeAdapter} from {@link PropertyInfo} if necessary.
 770      * Also generates other per-property annotations
 771      * (such as {@link XmlID}, {@link XmlIDREF}, and {@link XmlMimeType} if necessary.
 772      */
 773     public final void generateAdapterIfNecessary(CPropertyInfo prop, JAnnotatable field) {
 774         CAdapter adapter = prop.getAdapter();
 775         if (adapter != null) {
 776             if (adapter.getAdapterIfKnown() == SwaRefAdapterMarker.class) {
 777                 field.annotate(XmlAttachmentRef.class);
 778             } else {
 779                 // [RESULT]
 780                 // @XmlJavaTypeAdapter( Foo.class )
 781                 XmlJavaTypeAdapterWriter xjtw = field.annotate2(XmlJavaTypeAdapterWriter.class);
 782                 xjtw.value(adapter.adapterType.toType(this, EXPOSED));
 783             }
 784         }
 785 
 786         switch (prop.id()) {
 787             case ID:
 788                 field.annotate(XmlID.class);
 789                 break;
 790             case IDREF:
 791                 field.annotate(XmlIDREF.class);
 792                 break;
 793         }
 794 
 795         if (prop.getExpectedMimeType() != null) {
 796             field.annotate2(XmlMimeTypeWriter.class).value(prop.getExpectedMimeType().toString());
 797         }
 798     }
 799 
 800     public final JClass addRuntime(Class clazz) {
 801         JClass g = generatedRuntime.get(clazz);
 802         if (g == null) {
 803             // put code into a separate package to avoid name conflicts.
 804             JPackage implPkg = getUsedPackages(Aspect.IMPLEMENTATION)[0].subPackage("runtime");
 805             g = generateStaticClass(clazz, implPkg);
 806             generatedRuntime.put(clazz, g);
 807         }
 808         return g;
 809     }
 810 
 811     public JClass generateStaticClass(Class src, JPackage out) {
 812         String shortName = getShortName(src.getName());
 813 
 814         // some people didn't like our jars to contain files with .java extension,
 815         // so when we build jars, we'' use ".java_". But when we run from the workspace,
 816         // we want the original source code to be used, so we check both here.
 817         // see bug 6211503.
 818         URL res = src.getResource(shortName + ".java");
 819         if (res == null) {
 820             res = src.getResource(shortName + ".java_");
 821         }
 822         if (res == null) {
 823             throw new InternalError("Unable to load source code of " + src.getName() + " as a resource");
 824         }
 825 
 826         JStaticJavaFile sjf = new JStaticJavaFile(out, shortName, res, null);
 827         out.addResourceFile(sjf);
 828         return sjf.getJClass();
 829     }
 830 
 831     private String getShortName(String name) {
 832         return name.substring(name.lastIndexOf('.') + 1);
 833     }
 834 }