| /* |
| * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.tools.internal.xjc.generator.bean; |
| |
| import static com.sun.tools.internal.xjc.outline.Aspect.EXPOSED; |
| |
| import java.io.Serializable; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.LinkedHashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import javax.xml.bind.JAXBContext; |
| import javax.xml.bind.JAXBException; |
| import javax.xml.bind.annotation.XmlAttachmentRef; |
| import javax.xml.bind.annotation.XmlID; |
| import javax.xml.bind.annotation.XmlIDREF; |
| import javax.xml.bind.annotation.XmlMimeType; |
| import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; |
| import javax.xml.namespace.QName; |
| |
| import com.sun.codemodel.internal.ClassType; |
| import com.sun.codemodel.internal.JAnnotatable; |
| import com.sun.codemodel.internal.JClass; |
| import com.sun.codemodel.internal.JClassAlreadyExistsException; |
| import com.sun.codemodel.internal.JClassContainer; |
| import com.sun.codemodel.internal.JCodeModel; |
| import com.sun.codemodel.internal.JDefinedClass; |
| import com.sun.codemodel.internal.JEnumConstant; |
| import com.sun.codemodel.internal.JExpr; |
| import com.sun.codemodel.internal.JExpression; |
| import com.sun.codemodel.internal.JFieldVar; |
| import com.sun.codemodel.internal.JForEach; |
| import com.sun.codemodel.internal.JInvocation; |
| import com.sun.codemodel.internal.JJavaName; |
| import com.sun.codemodel.internal.JMethod; |
| import com.sun.codemodel.internal.JMod; |
| import com.sun.codemodel.internal.JPackage; |
| import com.sun.codemodel.internal.JType; |
| import com.sun.codemodel.internal.JVar; |
| import com.sun.codemodel.internal.fmt.JStaticJavaFile; |
| import com.sun.tools.internal.xjc.AbortException; |
| import com.sun.tools.internal.xjc.ErrorReceiver; |
| import com.sun.tools.internal.xjc.api.SpecVersion; |
| import com.sun.tools.internal.xjc.generator.annotation.spec.XmlAnyAttributeWriter; |
| import com.sun.tools.internal.xjc.generator.annotation.spec.XmlEnumValueWriter; |
| import com.sun.tools.internal.xjc.generator.annotation.spec.XmlEnumWriter; |
| import com.sun.tools.internal.xjc.generator.annotation.spec.XmlJavaTypeAdapterWriter; |
| import com.sun.tools.internal.xjc.generator.annotation.spec.XmlMimeTypeWriter; |
| import com.sun.tools.internal.xjc.generator.annotation.spec.XmlRootElementWriter; |
| import com.sun.tools.internal.xjc.generator.annotation.spec.XmlSeeAlsoWriter; |
| import com.sun.tools.internal.xjc.generator.annotation.spec.XmlTypeWriter; |
| import com.sun.tools.internal.xjc.generator.bean.field.FieldRenderer; |
| import com.sun.tools.internal.xjc.model.CAdapter; |
| import com.sun.tools.internal.xjc.model.CAttributePropertyInfo; |
| import com.sun.tools.internal.xjc.model.CClassInfo; |
| import com.sun.tools.internal.xjc.model.CClassInfoParent; |
| import com.sun.tools.internal.xjc.model.CElementInfo; |
| import com.sun.tools.internal.xjc.model.CEnumConstant; |
| import com.sun.tools.internal.xjc.model.CEnumLeafInfo; |
| import com.sun.tools.internal.xjc.model.CPropertyInfo; |
| import com.sun.tools.internal.xjc.model.CTypeRef; |
| import com.sun.tools.internal.xjc.model.Model; |
| import com.sun.tools.internal.xjc.model.CClassRef; |
| import com.sun.tools.internal.xjc.outline.Aspect; |
| import com.sun.tools.internal.xjc.outline.ClassOutline; |
| import com.sun.tools.internal.xjc.outline.EnumConstantOutline; |
| import com.sun.tools.internal.xjc.outline.EnumOutline; |
| import com.sun.tools.internal.xjc.outline.FieldOutline; |
| import com.sun.tools.internal.xjc.outline.Outline; |
| import com.sun.tools.internal.xjc.outline.PackageOutline; |
| import com.sun.tools.internal.xjc.util.CodeModelClassFactory; |
| import com.sun.xml.internal.bind.v2.model.core.PropertyInfo; |
| import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapterMarker; |
| import com.sun.xml.internal.xsom.XmlString; |
| import com.sun.istack.internal.NotNull; |
| import com.sun.tools.internal.xjc.model.CReferencePropertyInfo; |
| |
| /** |
| * Generates fields and accessors. |
| */ |
| public final class BeanGenerator implements Outline { |
| |
| /** JAXB module name. JAXB dependency is mandatory in generated Java module. */ |
| private static final String JAXB_PACKAGE = "java.xml.bind"; |
| |
| /** Simplifies class/interface creation and collision detection. */ |
| private final CodeModelClassFactory codeModelClassFactory; |
| private final ErrorReceiver errorReceiver; |
| /** all {@link PackageOutline}s keyed by their {@link PackageOutline#_package}. */ |
| private final Map<JPackage, PackageOutlineImpl> packageContexts = new LinkedHashMap<JPackage, PackageOutlineImpl>(); |
| /** all {@link ClassOutline}s keyed by their {@link ClassOutline#target}. */ |
| private final Map<CClassInfo, ClassOutlineImpl> classes = new LinkedHashMap<CClassInfo, ClassOutlineImpl>(); |
| /** all {@link EnumOutline}s keyed by their {@link EnumOutline#target}. */ |
| private final Map<CEnumLeafInfo, EnumOutline> enums = new LinkedHashMap<CEnumLeafInfo, EnumOutline>(); |
| /** |
| * Generated runtime classes. |
| */ |
| private final Map<Class, JClass> generatedRuntime = new LinkedHashMap<Class, JClass>(); |
| /** the model object which we are processing. */ |
| private final Model model; |
| private final JCodeModel codeModel; |
| /** |
| * for each property, the information about the generated field. |
| */ |
| private final Map<CPropertyInfo, FieldOutline> fields = new LinkedHashMap<CPropertyInfo, FieldOutline>(); |
| /** |
| * elements that generate classes to the generated classes. |
| */ |
| /*package*/ final Map<CElementInfo, ElementOutlineImpl> elements = new LinkedHashMap<CElementInfo, ElementOutlineImpl>(); |
| |
| /** |
| * Generates beans into code model according to the BGM, |
| * and produces the reflection model. |
| * |
| * @param _errorReceiver |
| * This object will receive all the errors discovered |
| * during the back-end stage. |
| * |
| * @return |
| * returns a {@link Outline} which will in turn |
| * be used to further generate marshaller/unmarshaller, |
| * or null if the processing fails (errors should have been |
| * reported to the error recevier.) |
| */ |
| public static Outline generate(Model model, ErrorReceiver _errorReceiver) { |
| |
| try { |
| return new BeanGenerator(model, _errorReceiver); |
| } catch (AbortException e) { |
| return null; |
| } |
| } |
| |
| private BeanGenerator(Model _model, ErrorReceiver _errorReceiver) { |
| |
| this.model = _model; |
| this.codeModel = model.codeModel; |
| this.errorReceiver = _errorReceiver; |
| this.codeModelClassFactory = new CodeModelClassFactory(errorReceiver); |
| |
| // build enum classes |
| for (CEnumLeafInfo p : model.enums().values()) { |
| enums.put(p, generateEnumDef(p)); |
| } |
| |
| JPackage[] packages = getUsedPackages(EXPOSED); |
| |
| // generates per-package code and remember the results as contexts. |
| for (JPackage pkg : packages) { |
| getPackageContext(pkg); |
| } |
| |
| // create the class definitions for all the beans first. |
| // this should also fill in PackageContext#getClasses |
| for (CClassInfo bean : model.beans().values()) { |
| getClazz(bean); |
| } |
| |
| // compute the package-level setting |
| for (PackageOutlineImpl p : packageContexts.values()) { |
| p.calcDefaultValues(); |
| } |
| |
| JClass OBJECT = codeModel.ref(Object.class); |
| |
| // inheritance relationship needs to be set before we generate fields, or otherwise |
| // we'll fail to compute the correct type signature (namely the common base type computation) |
| for (ClassOutlineImpl cc : getClasses()) { |
| |
| // setup inheritance between implementation hierarchy. |
| CClassInfo superClass = cc.target.getBaseClass(); |
| if (superClass != null) { |
| // use the specified super class |
| model.strategy._extends(cc, getClazz(superClass)); |
| } else { |
| CClassRef refSuperClass = cc.target.getRefBaseClass(); |
| if (refSuperClass != null) { |
| cc.implClass._extends(refSuperClass.toType(this, EXPOSED)); |
| } else { |
| // use the default one, if any |
| if (model.rootClass != null && cc.implClass._extends().equals(OBJECT)) { |
| cc.implClass._extends(model.rootClass); |
| } |
| if (model.rootInterface != null) { |
| cc.ref._implements(model.rootInterface); |
| } |
| } |
| } |
| |
| // if serialization support is turned on, generate |
| // [RESULT] |
| // class ... implements Serializable { |
| // private static final long serialVersionUID = <id>; |
| // .... |
| // } |
| if (model.serializable) { |
| cc.implClass._implements(Serializable.class); |
| if (model.serialVersionUID != null) { |
| cc.implClass.field( |
| JMod.PRIVATE | JMod.STATIC | JMod.FINAL, |
| codeModel.LONG, |
| "serialVersionUID", |
| JExpr.lit(model.serialVersionUID)); |
| } |
| } |
| |
| CClassInfoParent base = cc.target.parent(); |
| if ((base != null) && (base instanceof CClassInfo)) { |
| String pkg = base.getOwnerPackage().name(); |
| String shortName = base.fullName().substring(base.fullName().indexOf(pkg)+pkg.length()+1); |
| if (cc.target.shortName.equals(shortName)) { |
| getErrorReceiver().error(cc.target.getLocator(), Messages.ERR_KEYNAME_COLLISION.format(shortName)); |
| } |
| } |
| |
| } |
| |
| // fill in implementation classes |
| for (ClassOutlineImpl co : getClasses()) { |
| generateClassBody(co); |
| } |
| |
| for (EnumOutline eo : enums.values()) { |
| generateEnumBody(eo); |
| } |
| |
| // create factories for the impl-less elements |
| for (CElementInfo ei : model.getAllElements()) { |
| getPackageContext(ei._package()).objectFactoryGenerator().populate(ei); |
| } |
| |
| if (model.options.getModuleName() != null) { |
| codeModel._prepareModuleInfo(model.options.getModuleName(), JAXB_PACKAGE); |
| } |
| |
| if (model.options.debugMode) { |
| generateClassList(); |
| } |
| } |
| |
| /** |
| * Generates a class that knows how to create an instance of JAXBContext |
| * |
| * <p> |
| * This is used in the debug mode so that a new properly configured |
| * {@link JAXBContext} object can be used. |
| */ |
| @SuppressWarnings("CallToThreadDumpStack") |
| private void generateClassList() { |
| try { |
| JDefinedClass jc = codeModel.rootPackage()._class("JAXBDebug"); |
| JMethod m = jc.method(JMod.PUBLIC | JMod.STATIC, JAXBContext.class, "createContext"); |
| JVar $classLoader = m.param(ClassLoader.class, "classLoader"); |
| m._throws(JAXBException.class); |
| JInvocation inv = codeModel.ref(JAXBContext.class).staticInvoke("newInstance"); |
| m.body()._return(inv); |
| |
| switch (model.strategy) { |
| case INTF_AND_IMPL: { |
| StringBuilder buf = new StringBuilder(); |
| for (PackageOutlineImpl po : packageContexts.values()) { |
| if (buf.length() > 0) { |
| buf.append(':'); |
| } |
| buf.append(po._package().name()); |
| } |
| inv.arg(buf.toString()).arg($classLoader); |
| break; |
| } |
| case BEAN_ONLY: |
| for (ClassOutlineImpl cc : getClasses()) { |
| inv.arg(cc.implRef.dotclass()); |
| } |
| for (PackageOutlineImpl po : packageContexts.values()) { |
| inv.arg(po.objectFactory().dotclass()); |
| } |
| break; |
| default: |
| throw new IllegalStateException(); |
| } |
| } catch (JClassAlreadyExistsException e) { |
| e.printStackTrace(); |
| // after all, we are in the debug mode. a little sloppiness is OK. |
| // this error is not fatal. just continue. |
| } |
| } |
| |
| public Model getModel() { |
| return model; |
| } |
| |
| public JCodeModel getCodeModel() { |
| return codeModel; |
| } |
| |
| public JClassContainer getContainer(CClassInfoParent parent, Aspect aspect) { |
| CClassInfoParent.Visitor<JClassContainer> v; |
| switch (aspect) { |
| case EXPOSED: |
| v = exposedContainerBuilder; |
| break; |
| case IMPLEMENTATION: |
| v = implContainerBuilder; |
| break; |
| default: |
| assert false; |
| throw new IllegalStateException(); |
| } |
| return parent.accept(v); |
| } |
| |
| public final JType resolve(CTypeRef ref, Aspect a) { |
| return ref.getTarget().getType().toType(this, a); |
| } |
| private final CClassInfoParent.Visitor<JClassContainer> exposedContainerBuilder = |
| new CClassInfoParent.Visitor<JClassContainer>() { |
| |
| public JClassContainer onBean(CClassInfo bean) { |
| return getClazz(bean).ref; |
| } |
| |
| public JClassContainer onElement(CElementInfo element) { |
| // hmm... |
| return getElement(element).implClass; |
| } |
| |
| public JClassContainer onPackage(JPackage pkg) { |
| return model.strategy.getPackage(pkg, EXPOSED); |
| } |
| }; |
| private final CClassInfoParent.Visitor<JClassContainer> implContainerBuilder = |
| new CClassInfoParent.Visitor<JClassContainer>() { |
| |
| public JClassContainer onBean(CClassInfo bean) { |
| return getClazz(bean).implClass; |
| } |
| |
| public JClassContainer onElement(CElementInfo element) { |
| return getElement(element).implClass; |
| } |
| |
| public JClassContainer onPackage(JPackage pkg) { |
| return model.strategy.getPackage(pkg, Aspect.IMPLEMENTATION); |
| } |
| }; |
| |
| /** |
| * Returns all <i>used</i> JPackages. |
| * |
| * A JPackage is considered as "used" if a ClassItem or |
| * a InterfaceItem resides in that package. |
| * |
| * This value is dynamically calculated every time because |
| * one can freely remove ClassItem/InterfaceItem. |
| * |
| * @return |
| * Given the same input, the order of packages in the array |
| * is always the same regardless of the environment. |
| */ |
| public final JPackage[] getUsedPackages(Aspect aspect) { |
| Set<JPackage> s = new TreeSet<JPackage>(); |
| |
| for (CClassInfo bean : model.beans().values()) { |
| JClassContainer cont = getContainer(bean.parent(), aspect); |
| if (cont.isPackage()) { |
| s.add((JPackage) cont); |
| } |
| } |
| |
| for (CElementInfo e : model.getElementMappings(null).values()) { |
| // at the first glance you might think we should be iterating all elements, |
| // not just global ones, but if you think about it, local ones live inside |
| // another class, so those packages are already enumerated when we were |
| // walking over CClassInfos. |
| s.add(e._package()); |
| } |
| |
| return s.toArray(new JPackage[s.size()]); |
| } |
| |
| public ErrorReceiver getErrorReceiver() { |
| return errorReceiver; |
| } |
| |
| public CodeModelClassFactory getClassFactory() { |
| return codeModelClassFactory; |
| } |
| |
| public PackageOutlineImpl getPackageContext(JPackage p) { |
| PackageOutlineImpl r = packageContexts.get(p); |
| if (r == null) { |
| r = new PackageOutlineImpl(this, model, p); |
| packageContexts.put(p, r); |
| } |
| return r; |
| } |
| |
| /** |
| * Generates the minimum {@link JDefinedClass} skeleton |
| * without filling in its body. |
| */ |
| private ClassOutlineImpl generateClassDef(CClassInfo bean) { |
| ImplStructureStrategy.Result r = model.strategy.createClasses(this, bean); |
| JClass implRef; |
| |
| if (bean.getUserSpecifiedImplClass() != null) { |
| // create a place holder for a user-specified class. |
| JDefinedClass usr; |
| try { |
| usr = codeModel._class(bean.getUserSpecifiedImplClass()); |
| // but hide that file so that it won't be generated. |
| usr.hide(); |
| } catch (JClassAlreadyExistsException e) { |
| // it's OK for this to collide. |
| usr = e.getExistingClass(); |
| } |
| usr._extends(r.implementation); |
| implRef = usr; |
| } else { |
| implRef = r.implementation; |
| } |
| |
| return new ClassOutlineImpl(this, bean, r.exposed, r.implementation, implRef); |
| } |
| |
| public Collection<ClassOutlineImpl> getClasses() { |
| // make sure that classes are fully populated |
| assert model.beans().size() == classes.size(); |
| return classes.values(); |
| } |
| |
| public ClassOutlineImpl getClazz(CClassInfo bean) { |
| ClassOutlineImpl r = classes.get(bean); |
| if (r == null) { |
| classes.put(bean, r = generateClassDef(bean)); |
| } |
| return r; |
| } |
| |
| public ElementOutlineImpl getElement(CElementInfo ei) { |
| ElementOutlineImpl def = elements.get(ei); |
| if (def == null && ei.hasClass()) { |
| // create one. in the constructor it adds itself to the elements. |
| def = new ElementOutlineImpl(this, ei); |
| } |
| return def; |
| } |
| |
| public EnumOutline getEnum(CEnumLeafInfo eli) { |
| return enums.get(eli); |
| } |
| |
| public Collection<EnumOutline> getEnums() { |
| return enums.values(); |
| } |
| |
| public Iterable<? extends PackageOutline> getAllPackageContexts() { |
| return packageContexts.values(); |
| } |
| |
| public FieldOutline getField(CPropertyInfo prop) { |
| return fields.get(prop); |
| } |
| |
| /** |
| * Generates the body of a class. |
| * |
| */ |
| private void generateClassBody(ClassOutlineImpl cc) { |
| CClassInfo target = cc.target; |
| |
| // used to simplify the generated annotations |
| String mostUsedNamespaceURI = cc._package().getMostUsedNamespaceURI(); |
| |
| // [RESULT] |
| // @XmlType(name="foo", targetNamespace="bar://baz") |
| XmlTypeWriter xtw = cc.implClass.annotate2(XmlTypeWriter.class); |
| writeTypeName(cc.target.getTypeName(), xtw, mostUsedNamespaceURI); |
| |
| if (model.options.target.isLaterThan(SpecVersion.V2_1)) { |
| // @XmlSeeAlso |
| Iterator<CClassInfo> subclasses = cc.target.listSubclasses(); |
| if (subclasses.hasNext()) { |
| XmlSeeAlsoWriter saw = cc.implClass.annotate2(XmlSeeAlsoWriter.class); |
| while (subclasses.hasNext()) { |
| CClassInfo s = subclasses.next(); |
| saw.value(getClazz(s).implRef); |
| } |
| } |
| } |
| |
| if (target.isElement()) { |
| String namespaceURI = target.getElementName().getNamespaceURI(); |
| String localPart = target.getElementName().getLocalPart(); |
| |
| // [RESULT] |
| // @XmlRootElement(name="foo", targetNamespace="bar://baz") |
| XmlRootElementWriter xrew = cc.implClass.annotate2(XmlRootElementWriter.class); |
| xrew.name(localPart); |
| if (!namespaceURI.equals(mostUsedNamespaceURI)) // only generate if necessary |
| { |
| xrew.namespace(namespaceURI); |
| } |
| } |
| |
| if (target.isOrdered()) { |
| for (CPropertyInfo p : target.getProperties()) { |
| if (!(p instanceof CAttributePropertyInfo)) { |
| if (!((p instanceof CReferencePropertyInfo) |
| && ((CReferencePropertyInfo) p).isDummy())) { |
| xtw.propOrder(p.getName(false)); |
| } |
| } |
| } |
| } else { |
| // produce empty array |
| xtw.getAnnotationUse().paramArray("propOrder"); |
| } |
| |
| for (CPropertyInfo prop : target.getProperties()) { |
| generateFieldDecl(cc, prop); |
| } |
| |
| if (target.declaresAttributeWildcard()) { |
| generateAttributeWildcard(cc); |
| } |
| |
| // generate some class level javadoc |
| cc.ref.javadoc().append(target.javadoc); |
| |
| cc._package().objectFactoryGenerator().populate(cc); |
| } |
| |
| private void writeTypeName(QName typeName, XmlTypeWriter xtw, String mostUsedNamespaceURI) { |
| if (typeName == null) { |
| xtw.name(""); |
| } else { |
| xtw.name(typeName.getLocalPart()); |
| final String typeNameURI = typeName.getNamespaceURI(); |
| if (!typeNameURI.equals(mostUsedNamespaceURI)) // only generate if necessary |
| { |
| xtw.namespace(typeNameURI); |
| } |
| } |
| } |
| |
| /** |
| * Generates an attribute wildcard property on a class. |
| */ |
| private void generateAttributeWildcard(ClassOutlineImpl cc) { |
| String FIELD_NAME = "otherAttributes"; |
| String METHOD_SEED = model.getNameConverter().toClassName(FIELD_NAME); |
| |
| JClass mapType = codeModel.ref(Map.class).narrow(QName.class, String.class); |
| JClass mapImpl = codeModel.ref(HashMap.class).narrow(QName.class, String.class); |
| |
| // [RESULT] |
| // Map<QName,String> m = new HashMap<QName,String>(); |
| JFieldVar $ref = cc.implClass.field(JMod.PRIVATE, |
| mapType, FIELD_NAME, JExpr._new(mapImpl)); |
| $ref.annotate2(XmlAnyAttributeWriter.class); |
| |
| MethodWriter writer = cc.createMethodWriter(); |
| |
| JMethod $get = writer.declareMethod(mapType, "get" + METHOD_SEED); |
| $get.javadoc().append( |
| "Gets a map that contains attributes that aren't bound to any typed property on this class.\n\n" |
| + "<p>\n" |
| + "the map is keyed by the name of the attribute and \n" |
| + "the value is the string value of the attribute.\n" |
| + "\n" |
| + "the map returned by this method is live, and you can add new attribute\n" |
| + "by updating the map directly. Because of this design, there's no setter.\n"); |
| $get.javadoc().addReturn().append("always non-null"); |
| |
| $get.body()._return($ref); |
| } |
| |
| /** |
| * Generates the minimum {@link JDefinedClass} skeleton |
| * without filling in its body. |
| */ |
| private EnumOutline generateEnumDef(CEnumLeafInfo e) { |
| JDefinedClass type; |
| |
| type = getClassFactory().createClass( |
| getContainer(e.parent, EXPOSED), e.shortName, e.getLocator(), ClassType.ENUM); |
| type.javadoc().append(e.javadoc); |
| |
| return new EnumOutline(e, type) { |
| |
| @Override |
| public |
| @NotNull |
| Outline parent() { |
| return BeanGenerator.this; |
| } |
| }; |
| } |
| |
| private void generateEnumBody(EnumOutline eo) { |
| JDefinedClass type = eo.clazz; |
| CEnumLeafInfo e = eo.target; |
| |
| XmlTypeWriter xtw = type.annotate2(XmlTypeWriter.class); |
| writeTypeName(e.getTypeName(), xtw, |
| eo._package().getMostUsedNamespaceURI()); |
| |
| JCodeModel cModel = model.codeModel; |
| |
| // since constant values are never null, no point in using the boxed types. |
| JType baseExposedType = e.base.toType(this, EXPOSED).unboxify(); |
| JType baseImplType = e.base.toType(this, Aspect.IMPLEMENTATION).unboxify(); |
| |
| |
| XmlEnumWriter xew = type.annotate2(XmlEnumWriter.class); |
| xew.value(baseExposedType); |
| |
| |
| boolean needsValue = e.needsValueField(); |
| |
| // for each member <m>, |
| // [RESULT] |
| // <EnumName>(<deserializer of m>(<value>)); |
| |
| Set<String> enumFieldNames = new HashSet<String>(); // record generated field names to detect collision |
| |
| for (CEnumConstant mem : e.members) { |
| String constName = mem.getName(); |
| |
| if (!JJavaName.isJavaIdentifier(constName)) { |
| // didn't produce a name. |
| getErrorReceiver().error(e.getLocator(), |
| Messages.ERR_UNUSABLE_NAME.format(mem.getLexicalValue(), constName)); |
| } |
| |
| if (!enumFieldNames.add(constName)) { |
| getErrorReceiver().error(e.getLocator(), Messages.ERR_NAME_COLLISION.format(constName)); |
| } |
| |
| // [RESULT] |
| // <Const>(...) |
| // ASSUMPTION: datatype is outline-independent |
| JEnumConstant constRef = type.enumConstant(constName); |
| if (needsValue) { |
| constRef.arg(e.base.createConstant(this, new XmlString(mem.getLexicalValue()))); |
| } |
| |
| if (!mem.getLexicalValue().equals(constName)) { |
| constRef.annotate2(XmlEnumValueWriter.class).value(mem.getLexicalValue()); |
| } |
| |
| // set javadoc |
| if (mem.javadoc != null) { |
| constRef.javadoc().append(mem.javadoc); |
| } |
| |
| eo.constants.add(new EnumConstantOutline(mem, constRef) { |
| }); |
| } |
| |
| |
| if (needsValue) { |
| // [RESULT] |
| // final <valueType> value; |
| JFieldVar $value = type.field(JMod.PRIVATE | JMod.FINAL, baseExposedType, "value"); |
| |
| // [RESULT] |
| // public <valuetype> value() { return value; } |
| type.method(JMod.PUBLIC, baseExposedType, "value").body()._return($value); |
| |
| // [RESULT] |
| // <constructor>(<valueType> v) { |
| // this.value=v; |
| // } |
| { |
| JMethod m = type.constructor(0); |
| m.body().assign($value, m.param(baseImplType, "v")); |
| } |
| |
| // [RESULT] |
| // public static <Const> fromValue(<valueType> v) { |
| // for( <Const> c : <Const>.values() ) { |
| // if(c.value == v) // or equals |
| // return c; |
| // } |
| // throw new IllegalArgumentException(...); |
| // } |
| { |
| JMethod m = type.method(JMod.PUBLIC | JMod.STATIC, type, "fromValue"); |
| JVar $v = m.param(baseExposedType, "v"); |
| JForEach fe = m.body().forEach(type, "c", type.staticInvoke("values")); |
| JExpression eq; |
| if (baseExposedType.isPrimitive()) { |
| eq = fe.var().ref($value).eq($v); |
| } else { |
| eq = fe.var().ref($value).invoke("equals").arg($v); |
| } |
| |
| fe.body()._if(eq)._then()._return(fe.var()); |
| |
| JInvocation ex = JExpr._new(cModel.ref(IllegalArgumentException.class)); |
| |
| JExpression strForm; |
| if (baseExposedType.isPrimitive()) { |
| strForm = cModel.ref(String.class).staticInvoke("valueOf").arg($v); |
| } else if (baseExposedType == cModel.ref(String.class)) { |
| strForm = $v; |
| } else { |
| strForm = $v.invoke("toString"); |
| } |
| m.body()._throw(ex.arg(strForm)); |
| } |
| } else { |
| // [RESULT] |
| // public String value() { return name(); } |
| type.method(JMod.PUBLIC, String.class, "value").body()._return(JExpr.invoke("name")); |
| |
| // [RESULT] |
| // public <Const> fromValue(String v) { return valueOf(v); } |
| JMethod m = type.method(JMod.PUBLIC | JMod.STATIC, type, "fromValue"); |
| m.body()._return(JExpr.invoke("valueOf").arg(m.param(String.class, "v"))); |
| } |
| } |
| |
| /** |
| * Determines the FieldRenderer used for the given FieldUse, |
| * then generates the field declaration and accessor methods. |
| * |
| * The <code>fields</code> map will be updated with the newly |
| * created FieldRenderer. |
| */ |
| private FieldOutline generateFieldDecl(ClassOutlineImpl cc, CPropertyInfo prop) { |
| FieldRenderer fr = prop.realization; |
| if (fr == null) // none is specified. use the default factory |
| { |
| fr = model.options.getFieldRendererFactory().getDefault(); |
| } |
| |
| FieldOutline field = fr.generate(cc, prop); |
| fields.put(prop, field); |
| |
| return field; |
| } |
| |
| /** |
| * Generates {@link XmlJavaTypeAdapter} from {@link PropertyInfo} if necessary. |
| * Also generates other per-property annotations |
| * (such as {@link XmlID}, {@link XmlIDREF}, and {@link XmlMimeType} if necessary. |
| */ |
| public final void generateAdapterIfNecessary(CPropertyInfo prop, JAnnotatable field) { |
| CAdapter adapter = prop.getAdapter(); |
| if (adapter != null) { |
| if (adapter.getAdapterIfKnown() == SwaRefAdapterMarker.class) { |
| field.annotate(XmlAttachmentRef.class); |
| } else { |
| // [RESULT] |
| // @XmlJavaTypeAdapter( Foo.class ) |
| XmlJavaTypeAdapterWriter xjtw = field.annotate2(XmlJavaTypeAdapterWriter.class); |
| xjtw.value(adapter.adapterType.toType(this, EXPOSED)); |
| } |
| } |
| |
| switch (prop.id()) { |
| case ID: |
| field.annotate(XmlID.class); |
| break; |
| case IDREF: |
| field.annotate(XmlIDREF.class); |
| break; |
| } |
| |
| if (prop.getExpectedMimeType() != null) { |
| field.annotate2(XmlMimeTypeWriter.class).value(prop.getExpectedMimeType().toString()); |
| } |
| } |
| |
| public final JClass addRuntime(Class clazz) { |
| JClass g = generatedRuntime.get(clazz); |
| if (g == null) { |
| // put code into a separate package to avoid name conflicts. |
| JPackage implPkg = getUsedPackages(Aspect.IMPLEMENTATION)[0].subPackage("runtime"); |
| g = generateStaticClass(clazz, implPkg); |
| generatedRuntime.put(clazz, g); |
| } |
| return g; |
| } |
| |
| public JClass generateStaticClass(Class src, JPackage out) { |
| JStaticJavaFile sjf = new JStaticJavaFile(out, getShortName(src), src, null); |
| out.addResourceFile(sjf); |
| return sjf.getJClass(); |
| } |
| |
| private String getShortName(Class src) { |
| String name = src.getName(); |
| return name.substring(name.lastIndexOf('.') + 1); |
| } |
| |
| } |