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