--- old/src/java.xml.bind/share/classes/com/sun/xml/internal/bind/v2/model/impl/ClassInfoImpl.java 2018-01-30 20:32:05.000000000 -0500 +++ /dev/null 2018-01-30 20:32:05.000000000 -0500 @@ -1,1320 +0,0 @@ -/* - * Copyright (c) 1997, 2015, 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.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; - - -/** - * A part of the {@link ClassInfo} that doesn't depend on a particular - * reflection library. - * - * @author Kohsuke Kawaguchi (kk@kohsuke.org) - */ -public class ClassInfoImpl extends TypeInfoImpl - implements ClassInfo, Element { - - protected final C clazz; - - /** - * @see #getElementName() - */ - private final QName elementName; - - /** - * @see #getTypeName() - */ - private final QName typeName; - - /** - * Lazily created. - * - * @see #getProperties() - */ - private FinalArrayList> properties; - - /** - * The property order. - * - * null if unordered. {@link #DEFAULT_ORDER} if ordered but the order is defaulted - * - * @see #isOrdered() - */ - private /*final*/ String[] propOrder; - - /** - * Lazily computed. - * - * To avoid the cyclic references of the form C1 --base--> C2 --property--> C1. - */ - private ClassInfoImpl baseClass; - - private boolean baseClassComputed = false; - - private boolean hasSubClasses = false; - - /** - * If this class has a declared (not inherited) attribute wildcard, keep the reference - * to it. - * - * This parameter is initialized at the construction time and never change. - */ - protected /*final*/ PropertySeed attributeWildcard; - - - /** - * @see #getFactoryMethod() - */ - private M factoryMethod = null; - - ClassInfoImpl(ModelBuilder builder, Locatable upstream, C clazz) { - super(builder,upstream); - this.clazz = clazz; - assert clazz!=null; - - // compute the element name - elementName = parseElementName(clazz); - - // compute the type name - 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; // unordered - else { - if(propOrder[0].length()==0) - this.propOrder = DEFAULT_ORDER; - else - this.propOrder = propOrder; - } - } else { - propOrder = DEFAULT_ORDER; - } - - // obtain XmlAccessorOrder and set proporder (XmlAccessorOrder can be defined for whole package) - // ( vs ) - XmlAccessorOrder xao = reader().getPackageAnnotation(XmlAccessorOrder.class, clazz, this); - if((xao != null) && (xao.value() == XmlAccessOrder.UNDEFINED)) { - propOrder = null; - } - - // obtain XmlAccessorOrder and set proporder ( vs ) - 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 )); - } - - // the class must have the default constructor - 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 getBaseClass() { - if (!baseClassComputed) { - // compute the base class - C s = nav().getSuperClass(clazz); - if(s==null || s==nav().asDecl(Object.class)) { - baseClass = null; - } else { - NonElement b = builder.getClassInfo(s, true, this); - if(b instanceof ClassInfoImpl) { - baseClass = (ClassInfoImpl) b; - baseClass.hasSubClasses = true; - } else { - baseClass = null; - } - } - baseClassComputed = true; - } - return baseClass; - } - - /** - * {@inheritDoc} - * - * The substitution hierarchy is the same as the inheritance hierarchy. - */ - public final Element getSubstitutionHead() { - ClassInfoImpl c = getBaseClass(); - while(c!=null && !c.isElement()) - c = c.getBaseClass(); - return c; - } - - public final C getClazz() { - return clazz; - } - - /** - * When a bean binds to an element, it's always through {@link XmlRootElement}, - * so this method always return null. - * - * @deprecated - * you shouldn't be invoking this method on {@link ClassInfoImpl}. - */ - public ClassInfoImpl getScope() { - return null; - } - - public final T getType() { - return nav().use(clazz); - } - - /** - * A {@link ClassInfo} can be referenced by {@link XmlIDREF} if - * it has an ID property. - */ - public boolean canBeReferencedByIDREF() { - for (PropertyInfo p : getProperties()) { - if(p.id()== ID.ID) - return true; - } - ClassInfoImpl base = getBaseClass(); - if(base!=null) - return base.canBeReferencedByIDREF(); - else - return false; - } - - public final String getName() { - return nav().getClassName(clazz); - } - - public A readAnnotation(Class a) { - return reader().getClassAnnotation(a,clazz,this); - } - - public Element asElement() { - if(isElement()) - return this; - else - return null; - } - - public List> getProperties() { - if(properties!=null) return properties; - - // check the access type first - XmlAccessType at = getAccessType(); - - properties = new FinalArrayList>(); - - findFieldProperties(clazz,at); - - findGetterSetterProperties(at); - - if(propOrder==DEFAULT_ORDER || propOrder==null) { - XmlAccessOrder ao = getAccessorOrder(); - if(ao==XmlAccessOrder.ALPHABETICAL) - Collections.sort(properties); - } else { - //sort them as specified - PropertySorter sorter = new PropertySorter(); - for (PropertyInfoImpl p : properties) { - sorter.checkedGet(p); // have it check for errors - } - Collections.sort(properties,sorter); - sorter.checkUnusedProperties(); - } - - {// additional error checks - PropertyInfoImpl vp=null; // existing value property - PropertyInfoImpl ep=null; // existing element property - - for (PropertyInfoImpl p : properties) { - switch(p.kind()) { - case ELEMENT: - case REFERENCE: - case MAP: - ep = p; - break; - case VALUE: - if(vp!=null) { - // can't have multiple value properties. - 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; // noop - default: - assert false; - } - } - - if(ep!=null && vp!=null) { - // can't have element and value property at the same time - builder.reportError(new IllegalAnnotationException( - Messages.ELEMENT_AND_VALUE_PROPERTY.format(), - vp, ep - )); - } - } - - return properties; - } - - private void findFieldProperties(C c, XmlAccessType at) { - - // always find properties from the super class first - 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) ) { - // it's an error for transient field to have any binding annotation - if(hasJAXBAnnotation(annotations)) - builder.reportError(new IllegalAnnotationException( - Messages.TRANSIENT_FIELD_NOT_BINDABLE.format(nav().getFieldName(f)), - getSomeJAXBAnnotation(annotations))); - } else - if( nav().isStaticField(f) ) { - // static fields are bound only when there's explicit annotation. - 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 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 bc = getBaseClass(); - if(bc!=null && bc.hasValueProperty()) - return true; - - for (PropertyInfo p : getProperties()) { - if (p instanceof ValuePropertyInfo) return true; - } - - return false; - } - - public PropertyInfo getProperty(String name) { - for( PropertyInfo p: getProperties() ) { - if(p.getName().equals(name)) - return p; - } - return null; - } - - /** - * This hook is used by {@link RuntimeClassInfoImpl} to look for {@link com.sun.xml.internal.bind.annotation.XmlLocation}. - */ - protected void checkFieldXmlLocation(F f) { - } - - /** - * Gets an annotation that are allowed on both class and type. - */ - private T getClassOrPackageAnnotation(Class type) { - T t = reader().getClassAnnotation(type,clazz,this); - if(t!=null) - return t; - // defaults to the package level - return reader().getPackageAnnotation(type,clazz,this); - } - - /** - * Computes the {@link XmlAccessType} on this class by looking at {@link XmlAccessorType} - * annotations. - */ - private XmlAccessType getAccessType() { - XmlAccessorType xat = getClassOrPackageAnnotation(XmlAccessorType.class); - if(xat!=null) - return xat.value(); - else - return XmlAccessType.PUBLIC_MEMBER; - } - - /** - * Gets the accessor order for this class by consulting {@link XmlAccessorOrder}. - */ - private XmlAccessOrder getAccessorOrder() { - XmlAccessorOrder xao = getClassOrPackageAnnotation(XmlAccessorOrder.class); - if(xao!=null) - return xao.value(); - else - return XmlAccessOrder.UNDEFINED; - } - - /** - * Compares orders among {@link PropertyInfoImpl} according to {@link ClassInfoImpl#propOrder}. - * - *

- * extends {@link HashMap} to save memory. - */ - private final class PropertySorter extends HashMap implements Comparator { - /** - * Mark property names that are used, so that we can report unused property names in the propOrder array. - */ - PropertyInfoImpl[] used = new PropertyInfoImpl[propOrder.length]; - - /** - * If any name collides, it will be added to this set. - * This is used to avoid repeating the same error message. - */ - private Set collidedNames; - - PropertySorter() { - super(propOrder.length); - for( String name : propOrder ) - if(put(name,size())!=null) { - // two properties with the same name - 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) { - // missing - if (p.kind().isOrdered) - builder.reportError(new IllegalAnnotationException( - Messages.PROPERTY_MISSING_FROM_ORDER.format(p.getName()),p)); - - // give it an order to recover from an error - i = size(); - put(p.getName(),i); - } - - // mark the used field - int ii = i; - if(ii(); - - if(collidedNames.add(p.getName())) - // report the error only on the first time - builder.reportError(new IllegalAnnotationException( - Messages.DUPLICATE_PROPERTIES.format(p.getName()),p,used[ii])); - } - used[ii] = p; - } - - return i; - } - - /** - * Report errors for unused propOrder entries. - */ - public void checkUnusedProperties() { - for( int i=0; i() { - 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(); - } - - - /** - * Picks the first non-null argument, or null if all arguments are null. - */ - private static T pickOne( T... args ) { - for( T arg : args ) - if(arg!=null) - return arg; - return null; - } - - private static List makeSet( T... args ) { - List l = new FinalArrayList(); - for( T arg : args ) - if(arg!=null) l.add(arg); - return l; - } - - private static final class ConflictException extends Exception { - final List annotations; - - public ConflictException(List 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; - } - } - - /** - * Represents 6 groups of secondary annotations - */ - 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); - - /** - * Each constant gets an unique bit mask so that the presence/absence - * of them can be represented in a single byte. - */ - final int bitMask; - /** - * List of annotations that belong to this member. - */ - final Class[] members; - - SecondaryAnnotation(int bitMask, Class... members) { - this.bitMask = bitMask; - this.members = members; - } - } - - private static final SecondaryAnnotation[] SECONDARY_ANNOTATIONS = SecondaryAnnotation.values(); - - /** - * Represents 7 groups of properties. - * - * Each instance is also responsible for rejecting annotations - * that are not allowed on that kind. - */ - 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); - - /** - * Bit mask that represents secondary annotations that are allowed on this group. - * - * T = not allowed, F = allowed - */ - final int allowedsecondaryAnnotations; - - PropertyGroup(boolean... bits) { - int mask = 0; - assert bits.length==SECONDARY_ANNOTATIONS.length; - for( int i=0; i ANNOTATION_NUMBER_MAP = new HashMap(); - static { - Class[] annotations = { - XmlTransient.class, // 0 - XmlAnyAttribute.class, // 1 - XmlAttribute.class, // 2 - XmlValue.class, // 3 - XmlElement.class, // 4 - XmlElements.class, // 5 - XmlElementRef.class, // 6 - XmlElementRefs.class, // 7 - XmlAnyElement.class, // 8 - XmlMixed.class, // 9 - OverrideAnnotationOf.class,// 10 - }; - - HashMap m = ANNOTATION_NUMBER_MAP; - - // characterizing annotations - for( Class c : annotations ) - m.put(c, m.size() ); - - // secondary annotations - 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); - } - - /** - * Called only from {@link #getProperties()}. - * - *

- * This is where we decide the type of the property and checks for annotations - * that are not allowed. - * - * @param annotations - * all annotations on this property. It's the same as - * {@code seed.readAllAnnotation()}, but taken as a parameter - * because the caller should know it already. - */ - private void addProperty( PropertySeed seed, Annotation[] annotations, boolean dummy ) { - // since typically there's a very few annotations on a method, - // this runs faster than checking for each annotation via readAnnotation(A) - - - // characterizing annotations. these annotations (or lack thereof) decides - // the kind of the property it goes to. - // I wish I could use an array... - 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; - - // encountered secondary annotations are accumulated into a bit mask - 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: - // secondary annotations - secondaryAnnotations |= (1<<(index-20)); - break; - } - } - - // determine the group kind, and also count the numbers, since - // characterizing annotations are mutually exclusive. - 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) { - // collision between groups - List err = makeSet(t,aa,a,v,pickOne(e1,e2),pickOne(r1,r2,xae)); - throw new ConflictException(err); - } - - if(group==null) { - // if no characterizing annotation was found, it's either element or map - // sniff the signature and then decide. - assert groupCount==0; - - // UGLY: the presence of XmlJavaTypeAdapter makes it an element property. ARGH. - 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)) { // see issue 791 - make sure @XmlElement annotated map property is mapped to map - if (nav().isSubClassOf( seed.getRawType(), nav().ref(Map.class)) && !seed.hasAnnotation(XmlJavaTypeAdapter.class)) { - group = PropertyGroup.MAP; - } - } - - // group determined by now - // make sure that there are no prohibited secondary annotations - if( (secondaryAnnotations&group.allowedsecondaryAnnotations)!=0 ) { - // uh oh. find the offending annotation - for( SecondaryAnnotation sa : SECONDARY_ANNOTATIONS ) { - if(group.allows(sa)) - continue; - for( Class m : sa.members ) { - Annotation offender = seed.readAnnotation(m); - if(offender!=null) { - // found it - builder.reportError(new IllegalAnnotationException( - Messages.ANNOTATION_NOT_ALLOWED.format(m.getSimpleName()),offender)); - return; - } - } - } - // there must have been an offender - assert false; - } - - // actually create annotations - switch(group) { - case TRANSIENT: - return; - case ANY_ATTRIBUTE: - // an attribute wildcard property - if(attributeWildcard!=null) { - builder.reportError(new IllegalAnnotationException( - Messages.TWO_ATTRIBUTE_WILDCARDS.format( - nav().getClassName(getClazz())),aa,attributeWildcard)); - return; // recover by ignore - } - attributeWildcard = seed; - - if(inheritsAttributeWildcard()) { - builder.reportError(new IllegalAnnotationException( - Messages.SUPER_CLASS_HAS_WILDCARD.format(), - aa,getInheritedAttributeWildcard())); - return; - } - - // check the signature and make sure it's assignable to Map - 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 ) { - // report a conflicting annotation - List 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) )); - - // recover by ignoring this property - } catch( DuplicateException e ) { - // both are present - builder.reportError(new IllegalAnnotationException( - Messages.DUPLICATE_ANNOTATIONS.format(e.a1.annotationType().getName()), - e.a1, e.a2 )); - // recover by ignoring this property - - } - } - - protected ReferencePropertyInfoImpl createReferenceProperty(PropertySeed seed) { - return new ReferencePropertyInfoImpl(this,seed); - } - - protected AttributePropertyInfoImpl createAttributeProperty(PropertySeed seed) { - return new AttributePropertyInfoImpl(this,seed); - } - - protected ValuePropertyInfoImpl createValueProperty(PropertySeed seed) { - return new ValuePropertyInfoImpl(this,seed); - } - - protected ElementPropertyInfoImpl createElementProperty(PropertySeed seed) { - return new ElementPropertyInfoImpl(this,seed); - } - - protected MapPropertyInfoImpl createMapProperty(PropertySeed seed) { - return new MapPropertyInfoImpl(this,seed); - } - - - /** - * Adds properties that consists of accessors. - */ - private void findGetterSetterProperties(XmlAccessType at) { - // in the first step we accumulate getters and setters - // into this map keyed by the property name. - Map getters = new LinkedHashMap(); - Map setters = new LinkedHashMap(); - - C c = clazz; - do { - collectGetterSetters(clazz, getters, setters); - - // take super classes into account if they have @XmlTransient - c = nav().getSuperClass(c); - } while(shouldRecurseSuperClass(c)); - - - // compute the intersection - Set complete = new TreeSet(getters.keySet()); - complete.retainAll(setters.keySet()); - - resurrect(getters, complete); - resurrect(setters, complete); - - // then look for read/write properties. - for (String name : complete) { - M getter = getters.get(name); - M setter = setters.get(name); - - Annotation[] ga = getter!=null ? reader().getAllMethodAnnotations(getter,new MethodLocatable(this,getter,nav())) : EMPTY_ANNOTATIONS; - Annotation[] sa = setter!=null ? reader().getAllMethodAnnotations(setter,new MethodLocatable(this,setter,nav())) : EMPTY_ANNOTATIONS; - - boolean hasAnnotation = hasJAXBAnnotation(ga) || hasJAXBAnnotation(sa); - boolean isOverriding = false; - if(!hasAnnotation) { - // checking if the method is overriding others isn't free, - // so we don't compute it if it's not necessary. - 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) { - // make sure that the type is consistent - if(getter!=null && setter!=null - && !nav().isSameType(nav().getReturnType(getter), nav().getMethodParameters(setter)[0])) { - // inconsistent - builder.reportError(new IllegalAnnotationException( - Messages.GETTER_SETTER_INCOMPATIBLE_TYPE.format( - nav().getTypeName(nav().getReturnType(getter)), - nav().getTypeName(nav().getMethodParameters(setter)[0]) - ), - new MethodLocatable( this, getter, nav()), - new MethodLocatable( this, setter, nav()))); - continue; - } - - // merge annotations from two list - 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); - } - } - // done with complete pairs - getters.keySet().removeAll(complete); - setters.keySet().removeAll(complete); - - // TODO: think about - // class Foo { - // int getFoo(); - // } - // class Bar extends Foo { - // void setFoo(int x); - // } - // and how it will be XML-ized. - } - - private void collectGetterSetters(C c, Map getters, Map setters) { - // take super classes into account if they have @XmlTransient. - // always visit them first so that - // 1) order is right - // 2) overriden properties are handled accordingly - C sc = nav().getSuperClass(c); - if(shouldRecurseSuperClass(sc)) - collectGetterSetters(sc,getters,setters); - - Collection methods = nav().getDeclaredMethods(c); - Map> allSetters = new LinkedHashMap>(); - for( M method : methods ) { - boolean used = false; // if this method is added to getters or setters - - if(nav().isBridgeMethod(method)) - continue; // ignore - - String name = nav().getMethodName(method); - int arity = nav().getMethodParameters(method).length; - - if(nav().isStaticMethod(method)) { - ensureNoAnnotation(method); - continue; - } - - // is this a get method? - String propName = getPropertyNameFromGetMethod(name); - if(propName!=null && arity==0) { - getters.put(propName,method); - used = true; - } - - // is this a set method? - propName = getPropertyNameFromSetMethod(name); - if(propName!=null && arity==1) { - List propSetters = allSetters.get(propName); - if(null == propSetters){ - propSetters = new ArrayList(); - allSetters.put(propName, propSetters); - } - propSetters.add(method); - used = true; // used check performed later - } - - if(!used) - ensureNoAnnotation(method); - } - - // Match getter with setters by comparing getter return type to setter param - for (Map.Entry entry : getters.entrySet()) { - String propName = entry.getKey(); - M getter = entry.getValue(); - List propSetters = allSetters.remove(propName); - if (null == propSetters) { - //no matching setter - 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; - } - } - } - - // also allow set-only properties - for (Map.Entry> e : allSetters.entrySet()) { - setters.put(e.getKey(),e.getValue().get(0)); - } - } - - /** - * Checks if the properties in this given super class should be aggregated into this class. - */ - private boolean shouldRecurseSuperClass(C sc) { - return sc!=null - && (builder.isReplaced(sc) || reader().hasClassAnnotation(sc, XmlTransient.class)); - } - - /** - * Returns true if the method is considered 'public'. - */ - private boolean isConsideredPublic(M m) { - return m ==null || nav().isPublicMethod(m); - } - - /** - * If the method has an explicit annotation, allow it to participate - * to the processing even if it lacks the setter or the getter. - */ - private void resurrect(Map methods, Set complete) { - for (Map.Entry e : methods.entrySet()) { - if(complete.contains(e.getKey())) - continue; - if(hasJAXBAnnotation(reader().getAllMethodAnnotations(e.getValue(),this))) - complete.add(e.getKey()); - } - } - - /** - * Makes sure that the method doesn't have any annotation, if it does, - * report it as an error - */ - 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; - } - } - } - - /** - * Returns true if a given annotation is a JAXB annotation. - */ - private static boolean isJAXBAnnotation(Annotation a) { - return ANNOTATION_NUMBER_MAP.containsKey(a.annotationType()); - } - - /** - * Returns true if the array contains a JAXB annotation. - */ - 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; - } - - - /** - * Returns "Foo" from "getFoo" or "isFoo". - * - * @return null - * if the method name doesn't look like a getter. - */ - 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; - } - - /** - * Returns "Foo" from "setFoo". - * - * @return null - * if the method name doesn't look like a setter. - */ - private static String getPropertyNameFromSetMethod(String name) { - if(name.startsWith("set") && name.length()>3) - return name.substring(3); - return null; - } - - /** - * Creates a new {@link FieldPropertySeed} object. - * - *

- * Derived class can override this method to create a sub-class. - */ - protected PropertySeed createFieldSeed(F f) { - return new FieldPropertySeed(this, f); - } - - /** - * Creates a new {@link GetterSetterPropertySeed} object. - */ - protected PropertySeed createAccessorSeed(M getter, M setter) { - return new GetterSetterPropertySeed(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; - } - - /** - * Gets the {@link PropertySeed} object for the inherited attribute wildcard. - */ - private PropertySeed getInheritedAttributeWildcard() { - for( ClassInfoImpl 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 props = getProperties(); - if(props.size()!=1) return false; - return props.get(0).kind()==PropertyKind.VALUE; - } - - /** - * Called after all the {@link com.sun.xml.internal.bind.v2.model.core.TypeInfo}s are collected into the {@link #owner}. - */ - @Override - /*package*/ void link() { - getProperties(); // make sure properties!=null - - // property name collision cehck - Map names = new HashMap(); - for( PropertyInfoImpl 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); - } - - /** - * XmlType allows specification of factoryClass and - * factoryMethod. There are to be used if no default - * constructor is found. - * - * @return - * true if the factory method was found. False if not. - */ - 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))){ - //- Find the zero-arg public static method with the required return type - 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]; -}