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
26package com.sun.tools.internal.xjc.generator.bean;
27
28import static com.sun.tools.internal.xjc.outline.Aspect.EXPOSED;
29
30import java.io.Serializable;
31import java.util.Collection;
32import java.util.HashMap;
33import java.util.LinkedHashMap;
34import java.util.HashSet;
35import java.util.Iterator;
36import java.util.Map;
37import java.util.Set;
38import java.util.TreeSet;
39
40import javax.xml.bind.JAXBContext;
41import javax.xml.bind.JAXBException;
42import javax.xml.bind.annotation.XmlAttachmentRef;
43import javax.xml.bind.annotation.XmlID;
44import javax.xml.bind.annotation.XmlIDREF;
45import javax.xml.bind.annotation.XmlMimeType;
46import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
47import javax.xml.namespace.QName;
48
49import com.sun.codemodel.internal.ClassType;
50import com.sun.codemodel.internal.JAnnotatable;
51import com.sun.codemodel.internal.JClass;
52import com.sun.codemodel.internal.JClassAlreadyExistsException;
53import com.sun.codemodel.internal.JClassContainer;
54import com.sun.codemodel.internal.JCodeModel;
55import com.sun.codemodel.internal.JDefinedClass;
56import com.sun.codemodel.internal.JEnumConstant;
57import com.sun.codemodel.internal.JExpr;
58import com.sun.codemodel.internal.JExpression;
59import com.sun.codemodel.internal.JFieldVar;
60import com.sun.codemodel.internal.JForEach;
61import com.sun.codemodel.internal.JInvocation;
62import com.sun.codemodel.internal.JJavaName;
63import com.sun.codemodel.internal.JMethod;
64import com.sun.codemodel.internal.JMod;
65import com.sun.codemodel.internal.JPackage;
66import com.sun.codemodel.internal.JType;
67import com.sun.codemodel.internal.JVar;
68import com.sun.codemodel.internal.fmt.JStaticJavaFile;
69import com.sun.tools.internal.xjc.AbortException;
70import com.sun.tools.internal.xjc.ErrorReceiver;
71import com.sun.tools.internal.xjc.api.SpecVersion;
72import com.sun.tools.internal.xjc.generator.annotation.spec.XmlAnyAttributeWriter;
73import com.sun.tools.internal.xjc.generator.annotation.spec.XmlEnumValueWriter;
74import com.sun.tools.internal.xjc.generator.annotation.spec.XmlEnumWriter;
75import com.sun.tools.internal.xjc.generator.annotation.spec.XmlJavaTypeAdapterWriter;
76import com.sun.tools.internal.xjc.generator.annotation.spec.XmlMimeTypeWriter;
77import com.sun.tools.internal.xjc.generator.annotation.spec.XmlRootElementWriter;
78import com.sun.tools.internal.xjc.generator.annotation.spec.XmlSeeAlsoWriter;
79import com.sun.tools.internal.xjc.generator.annotation.spec.XmlTypeWriter;
80import com.sun.tools.internal.xjc.generator.bean.field.FieldRenderer;
81import com.sun.tools.internal.xjc.model.CAdapter;
82import com.sun.tools.internal.xjc.model.CAttributePropertyInfo;
83import com.sun.tools.internal.xjc.model.CClassInfo;
84import com.sun.tools.internal.xjc.model.CClassInfoParent;
85import com.sun.tools.internal.xjc.model.CElementInfo;
86import com.sun.tools.internal.xjc.model.CEnumConstant;
87import com.sun.tools.internal.xjc.model.CEnumLeafInfo;
88import com.sun.tools.internal.xjc.model.CPropertyInfo;
89import com.sun.tools.internal.xjc.model.CTypeRef;
90import com.sun.tools.internal.xjc.model.Model;
91import com.sun.tools.internal.xjc.model.CClassRef;
92import com.sun.tools.internal.xjc.outline.Aspect;
93import com.sun.tools.internal.xjc.outline.ClassOutline;
94import com.sun.tools.internal.xjc.outline.EnumConstantOutline;
95import com.sun.tools.internal.xjc.outline.EnumOutline;
96import com.sun.tools.internal.xjc.outline.FieldOutline;
97import com.sun.tools.internal.xjc.outline.Outline;
98import com.sun.tools.internal.xjc.outline.PackageOutline;
99import com.sun.tools.internal.xjc.util.CodeModelClassFactory;
100import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
101import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapterMarker;
102import com.sun.xml.internal.xsom.XmlString;
103import com.sun.istack.internal.NotNull;
104import com.sun.tools.internal.xjc.model.CReferencePropertyInfo;
105
106/**
107 * Generates fields and accessors.
108 */
109public 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}
829