blob: 4227e8080e0bff8ffcc27021ca7826de051b41b0 [file] [log] [blame]
/*
* Copyright 2005-2006 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.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.net.URL;
import java.util.Collection;
import java.util.HashMap;
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.SwaRefAdapter;
import com.sun.xml.internal.xsom.XmlString;
import com.sun.istack.internal.NotNull;
/**
* Generates fields and accessors.
*/
public final class BeanGenerator implements Outline
{
/** 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 HashMap<JPackage,PackageOutlineImpl>();
/** all {@link ClassOutline}s keyed by their {@link ClassOutline#target}. */
private final Map<CClassInfo,ClassOutlineImpl> classes = new HashMap<CClassInfo,ClassOutlineImpl>();
/** all {@link EnumOutline}s keyed by their {@link EnumOutline#target}. */
private final Map<CEnumLeafInfo,EnumOutline> enums = new HashMap<CEnumLeafInfo,EnumOutline>();
/**
* Generated runtime classes.
*/
private final Map<Class,JClass> generatedRuntime = new HashMap<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 HashMap<CPropertyInfo,FieldOutline>();
/**
* elements that generate classes to the generated classes.
*/
/*package*/ final Map<CElementInfo,ElementOutlineImpl> elements = new HashMap<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);
}
}
}
// 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.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.
*/
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;
// 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));
}
}
// 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 )) {
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 codeModel = 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(codeModel.ref(IllegalArgumentException.class));
JExpression strForm;
if(baseExposedType.isPrimitive()) {
strForm = codeModel.ref(String.class).staticInvoke("valueOf").arg($v);
} else
if(baseExposedType==codeModel.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()==SwaRefAdapter.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) {
String shortName = getShortName(src.getName());
// some people didn't like our jars to contain files with .java extension,
// so when we build jars, we'' use ".java_". But when we run from the workspace,
// we want the original source code to be used, so we check both here.
// see bug 6211503.
URL res = src.getResource(shortName+".java");
if(res==null)
res = src.getResource(shortName+".java_");
if(res==null)
throw new InternalError("Unable to load source code of "+src.getName()+" as a resource");
JStaticJavaFile sjf = new JStaticJavaFile(out,shortName, res, null );
out.addResourceFile(sjf);
return sjf.getJClass();
}
private String getShortName( String name ) {
return name.substring(name.lastIndexOf('.')+1);
}
}