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.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      */
generate(Model model, ErrorReceiver _errorReceiver)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 
BeanGenerator(Model _model, ErrorReceiver _errorReceiver)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")
generateClassList()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 
getModel()310     public Model getModel() {
311         return model;
312     }
313 
getCodeModel()314     public JCodeModel getCodeModel() {
315         return codeModel;
316     }
317 
getContainer(CClassInfoParent parent, Aspect aspect)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 
resolve(CTypeRef ref, Aspect a)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      */
getUsedPackages(Aspect aspect)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 
getErrorReceiver()403     public ErrorReceiver getErrorReceiver() {
404         return errorReceiver;
405     }
406 
getClassFactory()407     public CodeModelClassFactory getClassFactory() {
408         return codeModelClassFactory;
409     }
410 
getPackageContext(JPackage p)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      */
generateClassDef(CClassInfo bean)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 
getClasses()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 
getClazz(CClassInfo bean)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 
getElement(CElementInfo ei)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 
getEnum(CEnumLeafInfo eli)471     public EnumOutline getEnum(CEnumLeafInfo eli) {
472         return enums.get(eli);
473     }
474 
getEnums()475     public Collection<EnumOutline> getEnums() {
476         return enums.values();
477     }
478 
getAllPackageContexts()479     public Iterable<? extends PackageOutline> getAllPackageContexts() {
480         return packageContexts.values();
481     }
482 
getField(CPropertyInfo prop)483     public FieldOutline getField(CPropertyInfo prop) {
484         return fields.get(prop);
485     }
486 
487     /**
488      * Generates the body of a class.
489      *
490      */
generateClassBody(ClassOutlineImpl cc)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 
writeTypeName(QName typeName, XmlTypeWriter xtw, String mostUsedNamespaceURI)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      */
generateAttributeWildcard(ClassOutlineImpl cc)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      */
generateEnumDef(CEnumLeafInfo e)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 }
835