package com.sun.xml.internal.bind.v2.model.impl;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.AbstractList;
import javax.xml.bind.annotation.XmlAccessOrder;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorOrder;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyAttribute;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlAttachmentRef;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.XmlInlineBinaryData;
import javax.xml.bind.annotation.XmlList;
import javax.xml.bind.annotation.XmlMimeType;
import javax.xml.bind.annotation.XmlMixed;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.namespace.QName;
import com.sun.istack.internal.FinalArrayList;
import com.sun.xml.internal.bind.annotation.OverrideAnnotationOf;
import com.sun.xml.internal.bind.v2.model.annotation.Locatable;
import com.sun.xml.internal.bind.v2.model.annotation.MethodLocatable;
import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
import com.sun.xml.internal.bind.v2.model.core.Element;
import com.sun.xml.internal.bind.v2.model.core.ID;
import com.sun.xml.internal.bind.v2.model.core.NonElement;
import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
import com.sun.xml.internal.bind.v2.model.core.ValuePropertyInfo;
import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
import com.sun.xml.internal.bind.v2.runtime.Location;
import com.sun.xml.internal.bind.v2.util.EditDistance;
public class ClassInfoImpl<T,C,F,M> extends TypeInfoImpl<T,C,F,M>
implements ClassInfo<T,C>, Element<T,C> {
protected final C clazz;
private final QName elementName;
private final QName typeName;
private FinalArrayList<PropertyInfoImpl<T,C,F,M>> properties;
private String[] propOrder;
private ClassInfoImpl<T,C,F,M> baseClass;
private boolean baseClassComputed = false;
private boolean hasSubClasses = false;
protected PropertySeed<T,C,F,M> attributeWildcard;
private M factoryMethod = null;
ClassInfoImpl(ModelBuilder<T,C,F,M> builder, Locatable upstream, C clazz) {
super(builder,upstream);
this.clazz = clazz;
assert clazz!=null;
elementName = parseElementName(clazz);
XmlType t = reader().getClassAnnotation(XmlType.class,clazz,this);
typeName = parseTypeName(clazz,t);
if(t!=null) {
String[] propOrder = t.propOrder();
if(propOrder.length==0)
this.propOrder = null;
else {
if(propOrder[0].length()==0)
this.propOrder = DEFAULT_ORDER;
else
this.propOrder = propOrder;
}
} else {
propOrder = DEFAULT_ORDER;
}
XmlAccessorOrder xao = reader().getPackageAnnotation(XmlAccessorOrder.class, clazz, this);
if((xao != null) && (xao.value() == XmlAccessOrder.UNDEFINED)) {
propOrder = null;
}
xao = reader().getClassAnnotation(XmlAccessorOrder.class, clazz, this);
if((xao != null) && (xao.value() == XmlAccessOrder.UNDEFINED)) {
propOrder = null;
}
if(nav().isInterface(clazz)) {
builder.reportError(new IllegalAnnotationException(
Messages.CANT_HANDLE_INTERFACE.format(nav().getClassName(clazz)), this ));
}
if (!hasFactoryConstructor(t)){
if(!nav().hasDefaultConstructor(clazz)){
if(nav().isInnerClass(clazz)) {
builder.reportError(new IllegalAnnotationException(
Messages.CANT_HANDLE_INNER_CLASS.format(nav().getClassName(clazz)), this ));
} else if (elementName != null) {
builder.reportError(new IllegalAnnotationException(
Messages.NO_DEFAULT_CONSTRUCTOR.format(nav().getClassName(clazz)), this ));
}
}
}
}
public ClassInfoImpl<T,C,F,M> getBaseClass() {
if (!baseClassComputed) {
C s = nav().getSuperClass(clazz);
if(s==null || s==nav().asDecl(Object.class)) {
baseClass = null;
} else {
NonElement<T,C> b = builder.getClassInfo(s, true, this);
if(b instanceof ClassInfoImpl) {
baseClass = (ClassInfoImpl<T,C,F,M>) b;
baseClass.hasSubClasses = true;
} else {
baseClass = null;
}
}
baseClassComputed = true;
}
return baseClass;
}
public final Element<T,C> getSubstitutionHead() {
ClassInfoImpl<T,C,F,M> c = getBaseClass();
while(c!=null && !c.isElement())
c = c.getBaseClass();
return c;
}
public final C getClazz() {
return clazz;
}
public ClassInfoImpl<T,C,F,M> getScope() {
return null;
}
public final T getType() {
return nav().use(clazz);
}
public boolean canBeReferencedByIDREF() {
for (PropertyInfo<T,C> p : getProperties()) {
if(p.id()== ID.ID)
return true;
}
ClassInfoImpl<T,C,F,M> base = getBaseClass();
if(base!=null)
return base.canBeReferencedByIDREF();
else
return false;
}
public final String getName() {
return nav().getClassName(clazz);
}
public <A extends Annotation> A readAnnotation(Class<A> a) {
return reader().getClassAnnotation(a,clazz,this);
}
public Element<T,C> asElement() {
if(isElement())
return this;
else
return null;
}
public List<? extends PropertyInfo<T,C>> getProperties() {
if(properties!=null) return properties;
XmlAccessType at = getAccessType();
properties = new FinalArrayList<PropertyInfoImpl<T,C,F,M>>();
findFieldProperties(clazz,at);
findGetterSetterProperties(at);
if(propOrder==DEFAULT_ORDER || propOrder==null) {
XmlAccessOrder ao = getAccessorOrder();
if(ao==XmlAccessOrder.ALPHABETICAL)
Collections.sort(properties);
} else {
PropertySorter sorter = new PropertySorter();
for (PropertyInfoImpl p : properties) {
sorter.checkedGet(p);
}
Collections.sort(properties,sorter);
sorter.checkUnusedProperties();
}
{
PropertyInfoImpl vp=null;
PropertyInfoImpl ep=null;
for (PropertyInfoImpl p : properties) {
switch(p.kind()) {
case ELEMENT:
case REFERENCE:
case MAP:
ep = p;
break;
case VALUE:
if(vp!=null) {
builder.reportError(new IllegalAnnotationException(
Messages.MULTIPLE_VALUE_PROPERTY.format(),
vp, p ));
}
if(getBaseClass()!=null) {
builder.reportError(new IllegalAnnotationException(
Messages.XMLVALUE_IN_DERIVED_TYPE.format(), p ));
}
vp = p;
break;
case ATTRIBUTE:
break;
default:
assert false;
}
}
if(ep!=null && vp!=null) {
builder.reportError(new IllegalAnnotationException(
Messages.ELEMENT_AND_VALUE_PROPERTY.format(),
vp, ep
));
}
}
return properties;
}
private void findFieldProperties(C c, XmlAccessType at) {
C sc = nav().getSuperClass(c);
if (shouldRecurseSuperClass(sc)) {
findFieldProperties(sc,at);
}
for( F f : nav().getDeclaredFields(c) ) {
Annotation[] annotations = reader().getAllFieldAnnotations(f,this);
boolean isDummy = reader().hasFieldAnnotation(OverrideAnnotationOf.class, f);
if( nav().isTransient(f) ) {
if(hasJAXBAnnotation(annotations))
builder.reportError(new IllegalAnnotationException(
Messages.TRANSIENT_FIELD_NOT_BINDABLE.format(nav().getFieldName(f)),
getSomeJAXBAnnotation(annotations)));
} else
if( nav().isStaticField(f) ) {
if(hasJAXBAnnotation(annotations))
addProperty(createFieldSeed(f),annotations, false);
} else {
if(at==XmlAccessType.FIELD
||(at==XmlAccessType.PUBLIC_MEMBER && nav().isPublicField(f))
|| hasJAXBAnnotation(annotations)) {
if (isDummy) {
ClassInfo<T, C> top = getBaseClass();
while ((top != null) && (top.getProperty("content") == null)) {
top = top.getBaseClass();
}
DummyPropertyInfo prop = (DummyPropertyInfo) top.getProperty("content");
PropertySeed seed = createFieldSeed(f);
((DummyPropertyInfo)prop).addType(createReferenceProperty(seed));
} else {
addProperty(createFieldSeed(f), annotations, false);
}
}
checkFieldXmlLocation(f);
}
}
}
public final boolean hasValueProperty() {
ClassInfoImpl<T, C, F, M> bc = getBaseClass();
if(bc!=null && bc.hasValueProperty())
return true;
for (PropertyInfo p : getProperties()) {
if (p instanceof ValuePropertyInfo) return true;
}
return false;
}
public PropertyInfo<T,C> getProperty(String name) {
for( PropertyInfo<T,C> p: getProperties() ) {
if(p.getName().equals(name))
return p;
}
return null;
}
protected void checkFieldXmlLocation(F f) {
}
private <T extends Annotation> T getClassOrPackageAnnotation(Class<T> type) {
T t = reader().getClassAnnotation(type,clazz,this);
if(t!=null)
return t;
return reader().getPackageAnnotation(type,clazz,this);
}
private XmlAccessType getAccessType() {
XmlAccessorType xat = getClassOrPackageAnnotation(XmlAccessorType.class);
if(xat!=null)
return xat.value();
else
return XmlAccessType.PUBLIC_MEMBER;
}
private XmlAccessOrder getAccessorOrder() {
XmlAccessorOrder xao = getClassOrPackageAnnotation(XmlAccessorOrder.class);
if(xao!=null)
return xao.value();
else
return XmlAccessOrder.UNDEFINED;
}
private final class PropertySorter extends HashMap<String,Integer> implements Comparator<PropertyInfoImpl> {
PropertyInfoImpl[] used = new PropertyInfoImpl[propOrder.length];
private Set<String> collidedNames;
PropertySorter() {
super(propOrder.length);
for( String name : propOrder )
if(put(name,size())!=null) {
builder.reportError(new IllegalAnnotationException(
Messages.DUPLICATE_ENTRY_IN_PROP_ORDER.format(name),ClassInfoImpl.this));
}
}
public int compare(PropertyInfoImpl o1, PropertyInfoImpl o2) {
int lhs = checkedGet(o1);
int rhs = checkedGet(o2);
return lhs-rhs;
}
private int checkedGet(PropertyInfoImpl p) {
Integer i = get(p.getName());
if(i==null) {
if (p.kind().isOrdered)
builder.reportError(new IllegalAnnotationException(
Messages.PROPERTY_MISSING_FROM_ORDER.format(p.getName()),p));
i = size();
put(p.getName(),i);
}
int ii = i;
if(ii<used.length) {
if(used[ii]!=null && used[ii]!=p) {
if(collidedNames==null) collidedNames = new HashSet<String>();
if(collidedNames.add(p.getName()))
builder.reportError(new IllegalAnnotationException(
Messages.DUPLICATE_PROPERTIES.format(p.getName()),p,used[ii]));
}
used[ii] = p;
}
return i;
}
public void checkUnusedProperties() {
for( int i=0; i<used.length; i++ )
if(used[i]==null) {
String unusedName = propOrder[i];
String nearest = EditDistance.findNearest(unusedName, new AbstractList<String>() {
public String get(int index) {
return properties.get(index).getName();
}
public int size() {
return properties.size();
}
});
boolean isOverriding = (i > (properties.size()-1)) ? false : properties.get(i).hasAnnotation(OverrideAnnotationOf.class);
if (!isOverriding) {
builder.reportError(new IllegalAnnotationException(
Messages.PROPERTY_ORDER_CONTAINS_UNUSED_ENTRY.format(unusedName,nearest),ClassInfoImpl.this));
}
}
}
}
public boolean hasProperties() {
return !properties.isEmpty();
}
private static <T> T pickOne( T... args ) {
for( T arg : args )
if(arg!=null)
return arg;
return null;
}
private static <T> List<T> makeSet( T... args ) {
List<T> l = new FinalArrayList<T>();
for( T arg : args )
if(arg!=null) l.add(arg);
return l;
}
private static final class ConflictException extends Exception {
final List<Annotation> annotations;
public ConflictException(List<Annotation> one) {
this.annotations = one;
}
}
private static final class DuplicateException extends Exception {
final Annotation a1,a2;
public DuplicateException(Annotation a1, Annotation a2) {
this.a1 = a1;
this.a2 = a2;
}
}
private static enum SecondaryAnnotation {
JAVA_TYPE (0x01, XmlJavaTypeAdapter.class),
ID_IDREF (0x02, XmlID.class, XmlIDREF.class),
BINARY (0x04, XmlInlineBinaryData.class, XmlMimeType.class, XmlAttachmentRef.class),
ELEMENT_WRAPPER (0x08, XmlElementWrapper.class),
LIST (0x10, XmlList.class),
SCHEMA_TYPE (0x20, XmlSchemaType.class);
final int bitMask;
final Class<? extends Annotation>[] members;
SecondaryAnnotation(int bitMask, Class<? extends Annotation>... members) {
this.bitMask = bitMask;
this.members = members;
}
}
private static final SecondaryAnnotation[] SECONDARY_ANNOTATIONS = SecondaryAnnotation.values();
private static enum PropertyGroup {
TRANSIENT (false,false,false,false,false,false),
ANY_ATTRIBUTE (true, false,false,false,false,false),
ATTRIBUTE (true, true, true, false,true, true ),
VALUE (true, true, true, false,true, true ),
ELEMENT (true, true, true, true, true, true ),
ELEMENT_REF (true, false,false,true, false,false),
MAP (false,false,false,true, false,false);
final int allowedsecondaryAnnotations;
PropertyGroup(boolean... bits) {
int mask = 0;
assert bits.length==SECONDARY_ANNOTATIONS.length;
for( int i=0; i<bits.length; i++ ) {
if(bits[i])
mask |= SECONDARY_ANNOTATIONS[i].bitMask;
}
allowedsecondaryAnnotations = ~mask;
}
boolean allows(SecondaryAnnotation a) {
return (allowedsecondaryAnnotations&a.bitMask)==0;
}
}
private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
private static final HashMap<Class,Integer> ANNOTATION_NUMBER_MAP = new HashMap<Class,Integer>();
static {
Class[] annotations = {
XmlTransient.class,
XmlAnyAttribute.class,
XmlAttribute.class,
XmlValue.class,
XmlElement.class,
XmlElements.class,
XmlElementRef.class,
XmlElementRefs.class,
XmlAnyElement.class,
XmlMixed.class,
OverrideAnnotationOf.class,
};
HashMap<Class,Integer> m = ANNOTATION_NUMBER_MAP;
for( Class c : annotations )
m.put(c, m.size() );
int index = 20;
for( SecondaryAnnotation sa : SECONDARY_ANNOTATIONS ) {
for( Class member : sa.members )
m.put(member,index);
index++;
}
}
private void checkConflict(Annotation a, Annotation b) throws DuplicateException {
assert b!=null;
if(a!=null)
throw new DuplicateException(a,b);
}
private void addProperty( PropertySeed<T,C,F,M> seed, Annotation[] annotations, boolean dummy ) {
XmlTransient t = null;
XmlAnyAttribute aa = null;
XmlAttribute a = null;
XmlValue v = null;
XmlElement e1 = null;
XmlElements e2 = null;
XmlElementRef r1 = null;
XmlElementRefs r2 = null;
XmlAnyElement xae = null;
XmlMixed mx = null;
OverrideAnnotationOf ov = null;
int secondaryAnnotations = 0;
try {
for( Annotation ann : annotations ) {
Integer index = ANNOTATION_NUMBER_MAP.get(ann.annotationType());
if(index==null) continue;
switch(index) {
case 0: checkConflict(t ,ann); t = (XmlTransient) ann; break;
case 1: checkConflict(aa ,ann); aa = (XmlAnyAttribute) ann; break;
case 2: checkConflict(a ,ann); a = (XmlAttribute) ann; break;
case 3: checkConflict(v ,ann); v = (XmlValue) ann; break;
case 4: checkConflict(e1 ,ann); e1 = (XmlElement) ann; break;
case 5: checkConflict(e2 ,ann); e2 = (XmlElements) ann; break;
case 6: checkConflict(r1 ,ann); r1 = (XmlElementRef) ann; break;
case 7: checkConflict(r2 ,ann); r2 = (XmlElementRefs) ann; break;
case 8: checkConflict(xae,ann); xae = (XmlAnyElement) ann; break;
case 9: checkConflict(mx, ann); mx = (XmlMixed) ann; break;
case 10: checkConflict(ov, ann); ov = (OverrideAnnotationOf) ann; break;
default:
secondaryAnnotations |= (1<<(index-20));
break;
}
}
PropertyGroup group = null;
int groupCount = 0;
if(t!=null) {
group = PropertyGroup.TRANSIENT;
groupCount++;
}
if(aa!=null) {
group = PropertyGroup.ANY_ATTRIBUTE;
groupCount++;
}
if(a!=null) {
group = PropertyGroup.ATTRIBUTE;
groupCount++;
}
if(v!=null) {
group = PropertyGroup.VALUE;
groupCount++;
}
if(e1!=null || e2!=null) {
group = PropertyGroup.ELEMENT;
groupCount++;
}
if(r1!=null || r2!=null || xae!=null || mx!=null || ov != null) {
group = PropertyGroup.ELEMENT_REF;
groupCount++;
}
if(groupCount>1) {
List<Annotation> err = makeSet(t,aa,a,v,pickOne(e1,e2),pickOne(r1,r2,xae));
throw new ConflictException(err);
}
if(group==null) {
assert groupCount==0;
if(nav().isSubClassOf( seed.getRawType(), nav().ref(Map.class) )
&& !seed.hasAnnotation(XmlJavaTypeAdapter.class))
group = PropertyGroup.MAP;
else
group = PropertyGroup.ELEMENT;
} else if (group.equals(PropertyGroup.ELEMENT)) {
if (nav().isSubClassOf( seed.getRawType(), nav().ref(Map.class)) && !seed.hasAnnotation(XmlJavaTypeAdapter.class)) {
group = PropertyGroup.MAP;
}
}
if( (secondaryAnnotations&group.allowedsecondaryAnnotations)!=0 ) {
for( SecondaryAnnotation sa : SECONDARY_ANNOTATIONS ) {
if(group.allows(sa))
continue;
for( Class<? extends Annotation> m : sa.members ) {
Annotation offender = seed.readAnnotation(m);
if(offender!=null) {
builder.reportError(new IllegalAnnotationException(
Messages.ANNOTATION_NOT_ALLOWED.format(m.getSimpleName()),offender));
return;
}
}
}
assert false;
}
switch(group) {
case TRANSIENT:
return;
case ANY_ATTRIBUTE:
if(attributeWildcard!=null) {
builder.reportError(new IllegalAnnotationException(
Messages.TWO_ATTRIBUTE_WILDCARDS.format(
nav().getClassName(getClazz())),aa,attributeWildcard));
return;
}
attributeWildcard = seed;
if(inheritsAttributeWildcard()) {
builder.reportError(new IllegalAnnotationException(
Messages.SUPER_CLASS_HAS_WILDCARD.format(),
aa,getInheritedAttributeWildcard()));
return;
}
if(!nav().isSubClassOf(seed.getRawType(),nav().ref(Map.class))) {
builder.reportError(new IllegalAnnotationException(
Messages.INVALID_ATTRIBUTE_WILDCARD_TYPE.format(nav().getTypeName(seed.getRawType())),
aa,getInheritedAttributeWildcard()));
return;
}
return;
case ATTRIBUTE:
properties.add(createAttributeProperty(seed));
return;
case VALUE:
properties.add(createValueProperty(seed));
return;
case ELEMENT:
properties.add(createElementProperty(seed));
return;
case ELEMENT_REF:
properties.add(createReferenceProperty(seed));
return;
case MAP:
properties.add(createMapProperty(seed));
return;
default:
assert false;
}
} catch( ConflictException x ) {
List<Annotation> err = x.annotations;
builder.reportError(new IllegalAnnotationException(
Messages.MUTUALLY_EXCLUSIVE_ANNOTATIONS.format(
nav().getClassName(getClazz())+'#'+seed.getName(),
err.get(0).annotationType().getName(), err.get(1).annotationType().getName()),
err.get(0), err.get(1) ));
} catch( DuplicateException e ) {
builder.reportError(new IllegalAnnotationException(
Messages.DUPLICATE_ANNOTATIONS.format(e.a1.annotationType().getName()),
e.a1, e.a2 ));
}
}
protected ReferencePropertyInfoImpl<T,C,F,M> createReferenceProperty(PropertySeed<T,C,F,M> seed) {
return new ReferencePropertyInfoImpl<T,C,F,M>(this,seed);
}
protected AttributePropertyInfoImpl<T,C,F,M> createAttributeProperty(PropertySeed<T,C,F,M> seed) {
return new AttributePropertyInfoImpl<T,C,F,M>(this,seed);
}
protected ValuePropertyInfoImpl<T,C,F,M> createValueProperty(PropertySeed<T,C,F,M> seed) {
return new ValuePropertyInfoImpl<T,C,F,M>(this,seed);
}
protected ElementPropertyInfoImpl<T,C,F,M> createElementProperty(PropertySeed<T,C,F,M> seed) {
return new ElementPropertyInfoImpl<T,C,F,M>(this,seed);
}
protected MapPropertyInfoImpl<T,C,F,M> createMapProperty(PropertySeed<T,C,F,M> seed) {
return new MapPropertyInfoImpl<T,C,F,M>(this,seed);
}
private void findGetterSetterProperties(XmlAccessType at) {
Map<String,M> getters = new LinkedHashMap<String,M>();
Map<String,M> setters = new LinkedHashMap<String,M>();
C c = clazz;
do {
collectGetterSetters(clazz, getters, setters);
c = nav().getSuperClass(c);
} while(shouldRecurseSuperClass(c));
Set<String> complete = new TreeSet<String>(getters.keySet());
complete.retainAll(setters.keySet());
resurrect(getters, complete);
resurrect(setters, complete);
for (String name : complete) {
M getter = getters.get(name);
M setter = setters.get(name);
Annotation[] ga = getter!=null ? reader().getAllMethodAnnotations(getter,new MethodLocatable<M>(this,getter,nav())) : EMPTY_ANNOTATIONS;
Annotation[] sa = setter!=null ? reader().getAllMethodAnnotations(setter,new MethodLocatable<M>(this,setter,nav())) : EMPTY_ANNOTATIONS;
boolean hasAnnotation = hasJAXBAnnotation(ga) || hasJAXBAnnotation(sa);
boolean isOverriding = false;
if(!hasAnnotation) {
isOverriding = (getter!=null && nav().isOverriding(getter,c))
&& (setter!=null && nav().isOverriding(setter,c));
}
if((at==XmlAccessType.PROPERTY && !isOverriding)
|| (at==XmlAccessType.PUBLIC_MEMBER && isConsideredPublic(getter) && isConsideredPublic(setter) && !isOverriding)
|| hasAnnotation) {
if(getter!=null && setter!=null
&& !nav().isSameType(nav().getReturnType(getter), nav().getMethodParameters(setter)[0])) {
builder.reportError(new IllegalAnnotationException(
Messages.GETTER_SETTER_INCOMPATIBLE_TYPE.format(
nav().getTypeName(nav().getReturnType(getter)),
nav().getTypeName(nav().getMethodParameters(setter)[0])
),
new MethodLocatable<M>( this, getter, nav()),
new MethodLocatable<M>( this, setter, nav())));
continue;
}
Annotation[] r;
if(ga.length==0) {
r = sa;
} else
if(sa.length==0) {
r = ga;
} else {
r = new Annotation[ga.length+sa.length];
System.arraycopy(ga,0,r,0,ga.length);
System.arraycopy(sa,0,r,ga.length,sa.length);
}
addProperty(createAccessorSeed(getter, setter), r, false);
}
}
getters.keySet().removeAll(complete);
setters.keySet().removeAll(complete);
}
private void collectGetterSetters(C c, Map<String,M> getters, Map<String,M> setters) {
C sc = nav().getSuperClass(c);
if(shouldRecurseSuperClass(sc))
collectGetterSetters(sc,getters,setters);
Collection<? extends M> methods = nav().getDeclaredMethods(c);
Map<String,List<M>> allSetters = new LinkedHashMap<String,List<M>>();
for( M method : methods ) {
boolean used = false;
if(nav().isBridgeMethod(method))
continue;
String name = nav().getMethodName(method);
int arity = nav().getMethodParameters(method).length;
if(nav().isStaticMethod(method)) {
ensureNoAnnotation(method);
continue;
}
String propName = getPropertyNameFromGetMethod(name);
if(propName!=null && arity==0) {
getters.put(propName,method);
used = true;
}
propName = getPropertyNameFromSetMethod(name);
if(propName!=null && arity==1) {
List<M> propSetters = allSetters.get(propName);
if(null == propSetters){
propSetters = new ArrayList<M>();
allSetters.put(propName, propSetters);
}
propSetters.add(method);
used = true;
}
if(!used)
ensureNoAnnotation(method);
}
for (Map.Entry<String,M> entry : getters.entrySet()) {
String propName = entry.getKey();
M getter = entry.getValue();
List<M> propSetters = allSetters.remove(propName);
if (null == propSetters) {
continue;
}
T getterType = nav().getReturnType(getter);
for (M setter : propSetters) {
T setterType = nav().getMethodParameters(setter)[0];
if (nav().isSameType(setterType, getterType)) {
setters.put(propName, setter);
break;
}
}
}
for (Map.Entry<String,List<M>> e : allSetters.entrySet()) {
setters.put(e.getKey(),e.getValue().get(0));
}
}
private boolean shouldRecurseSuperClass(C sc) {
return sc!=null
&& (builder.isReplaced(sc) || reader().hasClassAnnotation(sc, XmlTransient.class));
}
private boolean isConsideredPublic(M m) {
return m ==null || nav().isPublicMethod(m);
}
private void resurrect(Map<String, M> methods, Set<String> complete) {
for (Map.Entry<String, M> e : methods.entrySet()) {
if(complete.contains(e.getKey()))
continue;
if(hasJAXBAnnotation(reader().getAllMethodAnnotations(e.getValue(),this)))
complete.add(e.getKey());
}
}
private void ensureNoAnnotation(M method) {
Annotation[] annotations = reader().getAllMethodAnnotations(method,this);
for( Annotation a : annotations ) {
if(isJAXBAnnotation(a)) {
builder.reportError(new IllegalAnnotationException(
Messages.ANNOTATION_ON_WRONG_METHOD.format(),
a));
return;
}
}
}
private static boolean isJAXBAnnotation(Annotation a) {
return ANNOTATION_NUMBER_MAP.containsKey(a.annotationType());
}
private static boolean hasJAXBAnnotation(Annotation[] annotations) {
return getSomeJAXBAnnotation(annotations)!=null;
}
private static Annotation getSomeJAXBAnnotation(Annotation[] annotations) {
for( Annotation a : annotations )
if(isJAXBAnnotation(a))
return a;
return null;
}
private static String getPropertyNameFromGetMethod(String name) {
if(name.startsWith("get") && name.length()>3)
return name.substring(3);
if(name.startsWith("is") && name.length()>2)
return name.substring(2);
return null;
}
private static String getPropertyNameFromSetMethod(String name) {
if(name.startsWith("set") && name.length()>3)
return name.substring(3);
return null;
}
protected PropertySeed<T,C,F,M> createFieldSeed(F f) {
return new FieldPropertySeed<T,C,F,M>(this, f);
}
protected PropertySeed<T,C,F,M> createAccessorSeed(M getter, M setter) {
return new GetterSetterPropertySeed<T,C,F,M>(this, getter,setter);
}
public final boolean isElement() {
return elementName!=null;
}
public boolean isAbstract() {
return nav().isAbstract(clazz);
}
public boolean isOrdered() {
return propOrder!=null;
}
public final boolean isFinal() {
return nav().isFinal(clazz);
}
public final boolean hasSubClasses() {
return hasSubClasses;
}
public final boolean hasAttributeWildcard() {
return declaresAttributeWildcard() || inheritsAttributeWildcard();
}
public final boolean inheritsAttributeWildcard() {
return getInheritedAttributeWildcard()!=null;
}
public final boolean declaresAttributeWildcard() {
return attributeWildcard!=null;
}
private PropertySeed<T,C,F,M> getInheritedAttributeWildcard() {
for( ClassInfoImpl<T,C,F,M> c=getBaseClass(); c!=null; c=c.getBaseClass() )
if(c.attributeWildcard!=null)
return c.attributeWildcard;
return null;
}
public final QName getElementName() {
return elementName;
}
public final QName getTypeName() {
return typeName;
}
public final boolean isSimpleType() {
List<? extends PropertyInfo> props = getProperties();
if(props.size()!=1) return false;
return props.get(0).kind()==PropertyKind.VALUE;
}
@Override
void link() {
getProperties();
Map<String,PropertyInfoImpl> names = new HashMap<String,PropertyInfoImpl>();
for( PropertyInfoImpl<T,C,F,M> p : properties ) {
p.link();
PropertyInfoImpl old = names.put(p.getName(),p);
if(old!=null) {
builder.reportError(new IllegalAnnotationException(
Messages.PROPERTY_COLLISION.format(p.getName()),
p, old ));
}
}
super.link();
}
public Location getLocation() {
return nav().getClassLocation(clazz);
}
private boolean hasFactoryConstructor(XmlType t){
if (t == null) return false;
String method = t.factoryMethod();
T fClass = reader().getClassValue(t, "factoryClass");
if (method.length() > 0){
if(nav().isSameType(fClass, nav().ref(XmlType.DEFAULT.class))){
fClass = nav().use(clazz);
}
for(M m: nav().getDeclaredMethods(nav().asDecl(fClass))){
if (nav().getMethodName(m).equals(method) &&
nav().isSameType(nav().getReturnType(m), nav().use(clazz)) &&
nav().getMethodParameters(m).length == 0 &&
nav().isStaticMethod(m)){
factoryMethod = m;
break;
}
}
if (factoryMethod == null){
builder.reportError(new IllegalAnnotationException(
Messages.NO_FACTORY_METHOD.format(nav().getClassName(nav().asDecl(fClass)), method), this ));
}
} else if(!nav().isSameType(fClass, nav().ref(XmlType.DEFAULT.class))){
builder.reportError(new IllegalAnnotationException(
Messages.FACTORY_CLASS_NEEDS_FACTORY_METHOD.format(nav().getClassName(nav().asDecl(fClass))), this ));
}
return factoryMethod != null;
}
public Method getFactoryMethod(){
return (Method) factoryMethod;
}
@Override
public String toString() {
return "ClassInfo("+clazz+')';
}
private static final String[] DEFAULT_ORDER = new String[0];
}