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