aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.bind.v2.model.impl; aoqi@0: aoqi@0: import java.lang.annotation.Annotation; aoqi@0: import java.lang.reflect.Method; aoqi@0: import java.util.ArrayList; aoqi@0: import java.util.Collection; aoqi@0: import java.util.Collections; aoqi@0: import java.util.Comparator; aoqi@0: import java.util.HashMap; aoqi@0: import java.util.HashSet; aoqi@0: import java.util.LinkedHashMap; aoqi@0: import java.util.List; aoqi@0: import java.util.Map; aoqi@0: import java.util.Set; aoqi@0: import java.util.TreeSet; aoqi@0: import java.util.AbstractList; aoqi@0: aoqi@0: import javax.xml.bind.annotation.XmlAccessOrder; aoqi@0: import javax.xml.bind.annotation.XmlAccessType; aoqi@0: import javax.xml.bind.annotation.XmlAccessorOrder; aoqi@0: import javax.xml.bind.annotation.XmlAccessorType; aoqi@0: import javax.xml.bind.annotation.XmlAnyAttribute; aoqi@0: import javax.xml.bind.annotation.XmlAnyElement; aoqi@0: import javax.xml.bind.annotation.XmlAttachmentRef; aoqi@0: import javax.xml.bind.annotation.XmlAttribute; aoqi@0: import javax.xml.bind.annotation.XmlElement; aoqi@0: import javax.xml.bind.annotation.XmlElementRef; aoqi@0: import javax.xml.bind.annotation.XmlElementRefs; aoqi@0: import javax.xml.bind.annotation.XmlElementWrapper; aoqi@0: import javax.xml.bind.annotation.XmlElements; aoqi@0: import javax.xml.bind.annotation.XmlID; aoqi@0: import javax.xml.bind.annotation.XmlIDREF; aoqi@0: import javax.xml.bind.annotation.XmlInlineBinaryData; aoqi@0: import javax.xml.bind.annotation.XmlList; aoqi@0: import javax.xml.bind.annotation.XmlMimeType; aoqi@0: import javax.xml.bind.annotation.XmlMixed; aoqi@0: import javax.xml.bind.annotation.XmlRootElement; aoqi@0: import javax.xml.bind.annotation.XmlSchemaType; aoqi@0: import javax.xml.bind.annotation.XmlTransient; aoqi@0: import javax.xml.bind.annotation.XmlType; aoqi@0: import javax.xml.bind.annotation.XmlValue; aoqi@0: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; aoqi@0: import javax.xml.namespace.QName; aoqi@0: aoqi@0: import com.sun.istack.internal.FinalArrayList; aoqi@0: import com.sun.xml.internal.bind.annotation.OverrideAnnotationOf; aoqi@0: import com.sun.xml.internal.bind.v2.model.annotation.Locatable; aoqi@0: import com.sun.xml.internal.bind.v2.model.annotation.MethodLocatable; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.ClassInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.Element; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.ID; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.NonElement; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.PropertyInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.PropertyKind; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.ValuePropertyInfo; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.Location; aoqi@0: import com.sun.xml.internal.bind.v2.util.EditDistance; aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * A part of the {@link ClassInfo} that doesn't depend on a particular aoqi@0: * reflection library. aoqi@0: * aoqi@0: * @author Kohsuke Kawaguchi (kk@kohsuke.org) aoqi@0: */ aoqi@0: public class ClassInfoImpl extends TypeInfoImpl aoqi@0: implements ClassInfo, Element { aoqi@0: aoqi@0: protected final C clazz; aoqi@0: aoqi@0: /** aoqi@0: * @see #getElementName() aoqi@0: */ aoqi@0: private final QName elementName; aoqi@0: aoqi@0: /** aoqi@0: * @see #getTypeName() aoqi@0: */ aoqi@0: private final QName typeName; aoqi@0: aoqi@0: /** aoqi@0: * Lazily created. aoqi@0: * aoqi@0: * @see #getProperties() aoqi@0: */ aoqi@0: private FinalArrayList> properties; aoqi@0: aoqi@0: /** aoqi@0: * The property order. aoqi@0: * aoqi@0: * null if unordered. {@link #DEFAULT_ORDER} if ordered but the order is defaulted aoqi@0: * aoqi@0: * @see #isOrdered() aoqi@0: */ aoqi@0: private /*final*/ String[] propOrder; aoqi@0: aoqi@0: /** aoqi@0: * Lazily computed. aoqi@0: * aoqi@0: * To avoid the cyclic references of the form C1 --base--> C2 --property--> C1. aoqi@0: */ aoqi@0: private ClassInfoImpl baseClass; aoqi@0: aoqi@0: private boolean baseClassComputed = false; aoqi@0: aoqi@0: private boolean hasSubClasses = false; aoqi@0: aoqi@0: /** aoqi@0: * If this class has a declared (not inherited) attribute wildcard, keep the reference aoqi@0: * to it. aoqi@0: * aoqi@0: * This parameter is initialized at the construction time and never change. aoqi@0: */ aoqi@0: protected /*final*/ PropertySeed attributeWildcard; aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * @see #getFactoryMethod() aoqi@0: */ aoqi@0: private M factoryMethod = null; aoqi@0: aoqi@0: ClassInfoImpl(ModelBuilder builder, Locatable upstream, C clazz) { aoqi@0: super(builder,upstream); aoqi@0: this.clazz = clazz; aoqi@0: assert clazz!=null; aoqi@0: aoqi@0: // compute the element name aoqi@0: elementName = parseElementName(clazz); aoqi@0: aoqi@0: // compute the type name aoqi@0: XmlType t = reader().getClassAnnotation(XmlType.class,clazz,this); aoqi@0: typeName = parseTypeName(clazz,t); aoqi@0: aoqi@0: if(t!=null) { aoqi@0: String[] propOrder = t.propOrder(); aoqi@0: if(propOrder.length==0) aoqi@0: this.propOrder = null; // unordered aoqi@0: else { aoqi@0: if(propOrder[0].length()==0) aoqi@0: this.propOrder = DEFAULT_ORDER; aoqi@0: else aoqi@0: this.propOrder = propOrder; aoqi@0: } aoqi@0: } else { aoqi@0: propOrder = DEFAULT_ORDER; aoqi@0: } aoqi@0: aoqi@0: // obtain XmlAccessorOrder and set proporder (XmlAccessorOrder can be defined for whole package) aoqi@0: // ( vs ) aoqi@0: XmlAccessorOrder xao = reader().getPackageAnnotation(XmlAccessorOrder.class, clazz, this); aoqi@0: if((xao != null) && (xao.value() == XmlAccessOrder.UNDEFINED)) { aoqi@0: propOrder = null; aoqi@0: } aoqi@0: aoqi@0: // obtain XmlAccessorOrder and set proporder ( vs ) aoqi@0: xao = reader().getClassAnnotation(XmlAccessorOrder.class, clazz, this); aoqi@0: if((xao != null) && (xao.value() == XmlAccessOrder.UNDEFINED)) { aoqi@0: propOrder = null; aoqi@0: } aoqi@0: aoqi@0: if(nav().isInterface(clazz)) { aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.CANT_HANDLE_INTERFACE.format(nav().getClassName(clazz)), this )); aoqi@0: } aoqi@0: aoqi@0: // the class must have the default constructor aoqi@0: if (!hasFactoryConstructor(t)){ aoqi@0: if(!nav().hasDefaultConstructor(clazz)){ aoqi@0: if(nav().isInnerClass(clazz)) { aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.CANT_HANDLE_INNER_CLASS.format(nav().getClassName(clazz)), this )); aoqi@0: } else if (elementName != null) { aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.NO_DEFAULT_CONSTRUCTOR.format(nav().getClassName(clazz)), this )); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public ClassInfoImpl getBaseClass() { aoqi@0: if (!baseClassComputed) { aoqi@0: // compute the base class aoqi@0: C s = nav().getSuperClass(clazz); aoqi@0: if(s==null || s==nav().asDecl(Object.class)) { aoqi@0: baseClass = null; aoqi@0: } else { aoqi@0: NonElement b = builder.getClassInfo(s, true, this); aoqi@0: if(b instanceof ClassInfoImpl) { aoqi@0: baseClass = (ClassInfoImpl) b; aoqi@0: baseClass.hasSubClasses = true; aoqi@0: } else { aoqi@0: baseClass = null; aoqi@0: } aoqi@0: } aoqi@0: baseClassComputed = true; aoqi@0: } aoqi@0: return baseClass; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * {@inheritDoc} aoqi@0: * aoqi@0: * The substitution hierarchy is the same as the inheritance hierarchy. aoqi@0: */ aoqi@0: public final Element getSubstitutionHead() { aoqi@0: ClassInfoImpl c = getBaseClass(); aoqi@0: while(c!=null && !c.isElement()) aoqi@0: c = c.getBaseClass(); aoqi@0: return c; aoqi@0: } aoqi@0: aoqi@0: public final C getClazz() { aoqi@0: return clazz; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * When a bean binds to an element, it's always through {@link XmlRootElement}, aoqi@0: * so this method always return null. aoqi@0: * aoqi@0: * @deprecated aoqi@0: * you shouldn't be invoking this method on {@link ClassInfoImpl}. aoqi@0: */ aoqi@0: public ClassInfoImpl getScope() { aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public final T getType() { aoqi@0: return nav().use(clazz); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A {@link ClassInfo} can be referenced by {@link XmlIDREF} if aoqi@0: * it has an ID property. aoqi@0: */ aoqi@0: public boolean canBeReferencedByIDREF() { aoqi@0: for (PropertyInfo p : getProperties()) { aoqi@0: if(p.id()== ID.ID) aoqi@0: return true; aoqi@0: } aoqi@0: ClassInfoImpl base = getBaseClass(); aoqi@0: if(base!=null) aoqi@0: return base.canBeReferencedByIDREF(); aoqi@0: else aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: public final String getName() { aoqi@0: return nav().getClassName(clazz); aoqi@0: } aoqi@0: aoqi@0: public A readAnnotation(Class a) { aoqi@0: return reader().getClassAnnotation(a,clazz,this); aoqi@0: } aoqi@0: aoqi@0: public Element asElement() { aoqi@0: if(isElement()) aoqi@0: return this; aoqi@0: else aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public List> getProperties() { aoqi@0: if(properties!=null) return properties; aoqi@0: aoqi@0: // check the access type first aoqi@0: XmlAccessType at = getAccessType(); aoqi@0: aoqi@0: properties = new FinalArrayList>(); aoqi@0: aoqi@0: findFieldProperties(clazz,at); aoqi@0: aoqi@0: findGetterSetterProperties(at); aoqi@0: aoqi@0: if(propOrder==DEFAULT_ORDER || propOrder==null) { aoqi@0: XmlAccessOrder ao = getAccessorOrder(); aoqi@0: if(ao==XmlAccessOrder.ALPHABETICAL) aoqi@0: Collections.sort(properties); aoqi@0: } else { aoqi@0: //sort them as specified aoqi@0: PropertySorter sorter = new PropertySorter(); aoqi@0: for (PropertyInfoImpl p : properties) { aoqi@0: sorter.checkedGet(p); // have it check for errors aoqi@0: } aoqi@0: Collections.sort(properties,sorter); aoqi@0: sorter.checkUnusedProperties(); aoqi@0: } aoqi@0: aoqi@0: {// additional error checks aoqi@0: PropertyInfoImpl vp=null; // existing value property aoqi@0: PropertyInfoImpl ep=null; // existing element property aoqi@0: aoqi@0: for (PropertyInfoImpl p : properties) { aoqi@0: switch(p.kind()) { aoqi@0: case ELEMENT: aoqi@0: case REFERENCE: aoqi@0: case MAP: aoqi@0: ep = p; aoqi@0: break; aoqi@0: case VALUE: aoqi@0: if(vp!=null) { aoqi@0: // can't have multiple value properties. aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.MULTIPLE_VALUE_PROPERTY.format(), aoqi@0: vp, p )); aoqi@0: } aoqi@0: if(getBaseClass()!=null) { aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.XMLVALUE_IN_DERIVED_TYPE.format(), p )); aoqi@0: } aoqi@0: vp = p; aoqi@0: break; aoqi@0: case ATTRIBUTE: aoqi@0: break; // noop aoqi@0: default: aoqi@0: assert false; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: if(ep!=null && vp!=null) { aoqi@0: // can't have element and value property at the same time aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.ELEMENT_AND_VALUE_PROPERTY.format(), aoqi@0: vp, ep aoqi@0: )); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return properties; aoqi@0: } aoqi@0: aoqi@0: private void findFieldProperties(C c, XmlAccessType at) { aoqi@0: aoqi@0: // always find properties from the super class first aoqi@0: C sc = nav().getSuperClass(c); aoqi@0: if (shouldRecurseSuperClass(sc)) { aoqi@0: findFieldProperties(sc,at); aoqi@0: } aoqi@0: aoqi@0: for( F f : nav().getDeclaredFields(c) ) { aoqi@0: Annotation[] annotations = reader().getAllFieldAnnotations(f,this); aoqi@0: boolean isDummy = reader().hasFieldAnnotation(OverrideAnnotationOf.class, f); aoqi@0: aoqi@0: if( nav().isTransient(f) ) { aoqi@0: // it's an error for transient field to have any binding annotation aoqi@0: if(hasJAXBAnnotation(annotations)) aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.TRANSIENT_FIELD_NOT_BINDABLE.format(nav().getFieldName(f)), aoqi@0: getSomeJAXBAnnotation(annotations))); aoqi@0: } else aoqi@0: if( nav().isStaticField(f) ) { aoqi@0: // static fields are bound only when there's explicit annotation. aoqi@0: if(hasJAXBAnnotation(annotations)) aoqi@0: addProperty(createFieldSeed(f),annotations, false); aoqi@0: } else { aoqi@0: if(at==XmlAccessType.FIELD aoqi@0: ||(at==XmlAccessType.PUBLIC_MEMBER && nav().isPublicField(f)) aoqi@0: || hasJAXBAnnotation(annotations)) { aoqi@0: if (isDummy) { aoqi@0: ClassInfo top = getBaseClass(); aoqi@0: while ((top != null) && (top.getProperty("content") == null)) { aoqi@0: top = top.getBaseClass(); aoqi@0: } aoqi@0: DummyPropertyInfo prop = (DummyPropertyInfo) top.getProperty("content"); aoqi@0: PropertySeed seed = createFieldSeed(f); aoqi@0: ((DummyPropertyInfo)prop).addType(createReferenceProperty(seed)); aoqi@0: } else { aoqi@0: addProperty(createFieldSeed(f), annotations, false); aoqi@0: } aoqi@0: } aoqi@0: checkFieldXmlLocation(f); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public final boolean hasValueProperty() { aoqi@0: ClassInfoImpl bc = getBaseClass(); aoqi@0: if(bc!=null && bc.hasValueProperty()) aoqi@0: return true; aoqi@0: aoqi@0: for (PropertyInfo p : getProperties()) { aoqi@0: if (p instanceof ValuePropertyInfo) return true; aoqi@0: } aoqi@0: aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: public PropertyInfo getProperty(String name) { aoqi@0: for( PropertyInfo p: getProperties() ) { aoqi@0: if(p.getName().equals(name)) aoqi@0: return p; aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * This hook is used by {@link RuntimeClassInfoImpl} to look for {@link XmlLocation}. aoqi@0: */ aoqi@0: protected void checkFieldXmlLocation(F f) { aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Gets an annotation that are allowed on both class and type. aoqi@0: */ aoqi@0: private T getClassOrPackageAnnotation(Class type) { aoqi@0: T t = reader().getClassAnnotation(type,clazz,this); aoqi@0: if(t!=null) aoqi@0: return t; aoqi@0: // defaults to the package level aoqi@0: return reader().getPackageAnnotation(type,clazz,this); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Computes the {@link XmlAccessType} on this class by looking at {@link XmlAccessorType} aoqi@0: * annotations. aoqi@0: */ aoqi@0: private XmlAccessType getAccessType() { aoqi@0: XmlAccessorType xat = getClassOrPackageAnnotation(XmlAccessorType.class); aoqi@0: if(xat!=null) aoqi@0: return xat.value(); aoqi@0: else aoqi@0: return XmlAccessType.PUBLIC_MEMBER; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Gets the accessor order for this class by consulting {@link XmlAccessorOrder}. aoqi@0: */ aoqi@0: private XmlAccessOrder getAccessorOrder() { aoqi@0: XmlAccessorOrder xao = getClassOrPackageAnnotation(XmlAccessorOrder.class); aoqi@0: if(xao!=null) aoqi@0: return xao.value(); aoqi@0: else aoqi@0: return XmlAccessOrder.UNDEFINED; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Compares orders among {@link PropertyInfoImpl} according to {@link ClassInfoImpl#propOrder}. aoqi@0: * aoqi@0: *

aoqi@0: * extends {@link HashMap} to save memory. aoqi@0: */ aoqi@0: private final class PropertySorter extends HashMap implements Comparator { aoqi@0: /** aoqi@0: * Mark property names that are used, so that we can report unused property names in the propOrder array. aoqi@0: */ aoqi@0: PropertyInfoImpl[] used = new PropertyInfoImpl[propOrder.length]; aoqi@0: aoqi@0: /** aoqi@0: * If any name collides, it will be added to this set. aoqi@0: * This is used to avoid repeating the same error message. aoqi@0: */ aoqi@0: private Set collidedNames; aoqi@0: aoqi@0: PropertySorter() { aoqi@0: super(propOrder.length); aoqi@0: for( String name : propOrder ) aoqi@0: if(put(name,size())!=null) { aoqi@0: // two properties with the same name aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.DUPLICATE_ENTRY_IN_PROP_ORDER.format(name),ClassInfoImpl.this)); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public int compare(PropertyInfoImpl o1, PropertyInfoImpl o2) { aoqi@0: int lhs = checkedGet(o1); aoqi@0: int rhs = checkedGet(o2); aoqi@0: aoqi@0: return lhs-rhs; aoqi@0: } aoqi@0: aoqi@0: private int checkedGet(PropertyInfoImpl p) { aoqi@0: Integer i = get(p.getName()); aoqi@0: if(i==null) { aoqi@0: // missing aoqi@0: if (p.kind().isOrdered) aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.PROPERTY_MISSING_FROM_ORDER.format(p.getName()),p)); aoqi@0: aoqi@0: // give it an order to recover from an error aoqi@0: i = size(); aoqi@0: put(p.getName(),i); aoqi@0: } aoqi@0: aoqi@0: // mark the used field aoqi@0: int ii = i; aoqi@0: if(ii(); aoqi@0: aoqi@0: if(collidedNames.add(p.getName())) aoqi@0: // report the error only on the first time aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.DUPLICATE_PROPERTIES.format(p.getName()),p,used[ii])); aoqi@0: } aoqi@0: used[ii] = p; aoqi@0: } aoqi@0: aoqi@0: return i; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Report errors for unused propOrder entries. aoqi@0: */ aoqi@0: public void checkUnusedProperties() { aoqi@0: for( int i=0; i() { aoqi@0: public String get(int index) { aoqi@0: return properties.get(index).getName(); aoqi@0: } aoqi@0: aoqi@0: public int size() { aoqi@0: return properties.size(); aoqi@0: } aoqi@0: }); aoqi@0: boolean isOverriding = (i > (properties.size()-1)) ? false : properties.get(i).hasAnnotation(OverrideAnnotationOf.class); aoqi@0: if (!isOverriding) { aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.PROPERTY_ORDER_CONTAINS_UNUSED_ENTRY.format(unusedName,nearest),ClassInfoImpl.this)); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public boolean hasProperties() { aoqi@0: return !properties.isEmpty(); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Picks the first non-null argument, or null if all arguments are null. aoqi@0: */ aoqi@0: private static T pickOne( T... args ) { aoqi@0: for( T arg : args ) aoqi@0: if(arg!=null) aoqi@0: return arg; aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: private static List makeSet( T... args ) { aoqi@0: List l = new FinalArrayList(); aoqi@0: for( T arg : args ) aoqi@0: if(arg!=null) l.add(arg); aoqi@0: return l; aoqi@0: } aoqi@0: aoqi@0: private static final class ConflictException extends Exception { aoqi@0: final List annotations; aoqi@0: aoqi@0: public ConflictException(List one) { aoqi@0: this.annotations = one; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private static final class DuplicateException extends Exception { aoqi@0: final Annotation a1,a2; aoqi@0: public DuplicateException(Annotation a1, Annotation a2) { aoqi@0: this.a1 = a1; aoqi@0: this.a2 = a2; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Represents 6 groups of secondary annotations aoqi@0: */ aoqi@0: private static enum SecondaryAnnotation { aoqi@0: JAVA_TYPE (0x01, XmlJavaTypeAdapter.class), aoqi@0: ID_IDREF (0x02, XmlID.class, XmlIDREF.class), aoqi@0: BINARY (0x04, XmlInlineBinaryData.class, XmlMimeType.class, XmlAttachmentRef.class), aoqi@0: ELEMENT_WRAPPER (0x08, XmlElementWrapper.class), aoqi@0: LIST (0x10, XmlList.class), aoqi@0: SCHEMA_TYPE (0x20, XmlSchemaType.class); aoqi@0: aoqi@0: /** aoqi@0: * Each constant gets an unique bit mask so that the presence/absence aoqi@0: * of them can be represented in a single byte. aoqi@0: */ aoqi@0: final int bitMask; aoqi@0: /** aoqi@0: * List of annotations that belong to this member. aoqi@0: */ aoqi@0: final Class[] members; aoqi@0: aoqi@0: SecondaryAnnotation(int bitMask, Class... members) { aoqi@0: this.bitMask = bitMask; aoqi@0: this.members = members; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private static final SecondaryAnnotation[] SECONDARY_ANNOTATIONS = SecondaryAnnotation.values(); aoqi@0: aoqi@0: /** aoqi@0: * Represents 7 groups of properties. aoqi@0: * aoqi@0: * Each instance is also responsible for rejecting annotations aoqi@0: * that are not allowed on that kind. aoqi@0: */ aoqi@0: private static enum PropertyGroup { aoqi@0: TRANSIENT (false,false,false,false,false,false), aoqi@0: ANY_ATTRIBUTE (true, false,false,false,false,false), aoqi@0: ATTRIBUTE (true, true, true, false,true, true ), aoqi@0: VALUE (true, true, true, false,true, true ), aoqi@0: ELEMENT (true, true, true, true, true, true ), aoqi@0: ELEMENT_REF (true, false,false,true, false,false), aoqi@0: MAP (false,false,false,true, false,false); aoqi@0: aoqi@0: /** aoqi@0: * Bit mask that represents secondary annotations that are allowed on this group. aoqi@0: * aoqi@0: * T = not allowed, F = allowed aoqi@0: */ aoqi@0: final int allowedsecondaryAnnotations; aoqi@0: aoqi@0: PropertyGroup(boolean... bits) { aoqi@0: int mask = 0; aoqi@0: assert bits.length==SECONDARY_ANNOTATIONS.length; aoqi@0: for( int i=0; i ANNOTATION_NUMBER_MAP = new HashMap(); aoqi@0: static { aoqi@0: Class[] annotations = { aoqi@0: XmlTransient.class, // 0 aoqi@0: XmlAnyAttribute.class, // 1 aoqi@0: XmlAttribute.class, // 2 aoqi@0: XmlValue.class, // 3 aoqi@0: XmlElement.class, // 4 aoqi@0: XmlElements.class, // 5 aoqi@0: XmlElementRef.class, // 6 aoqi@0: XmlElementRefs.class, // 7 aoqi@0: XmlAnyElement.class, // 8 aoqi@0: XmlMixed.class, // 9 aoqi@0: OverrideAnnotationOf.class,// 10 aoqi@0: }; aoqi@0: aoqi@0: HashMap m = ANNOTATION_NUMBER_MAP; aoqi@0: aoqi@0: // characterizing annotations aoqi@0: for( Class c : annotations ) aoqi@0: m.put(c, m.size() ); aoqi@0: aoqi@0: // secondary annotations aoqi@0: int index = 20; aoqi@0: for( SecondaryAnnotation sa : SECONDARY_ANNOTATIONS ) { aoqi@0: for( Class member : sa.members ) aoqi@0: m.put(member,index); aoqi@0: index++; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private void checkConflict(Annotation a, Annotation b) throws DuplicateException { aoqi@0: assert b!=null; aoqi@0: if(a!=null) aoqi@0: throw new DuplicateException(a,b); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Called only from {@link #getProperties()}. aoqi@0: * aoqi@0: *

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

aoqi@0: * Derived class can override this method to create a sub-class. aoqi@0: */ aoqi@0: protected PropertySeed createFieldSeed(F f) { aoqi@0: return new FieldPropertySeed(this, f); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Creates a new {@link GetterSetterPropertySeed} object. aoqi@0: */ aoqi@0: protected PropertySeed createAccessorSeed(M getter, M setter) { aoqi@0: return new GetterSetterPropertySeed(this, getter,setter); aoqi@0: } aoqi@0: aoqi@0: public final boolean isElement() { aoqi@0: return elementName!=null; aoqi@0: } aoqi@0: aoqi@0: public boolean isAbstract() { aoqi@0: return nav().isAbstract(clazz); aoqi@0: } aoqi@0: aoqi@0: public boolean isOrdered() { aoqi@0: return propOrder!=null; aoqi@0: } aoqi@0: aoqi@0: public final boolean isFinal() { aoqi@0: return nav().isFinal(clazz); aoqi@0: } aoqi@0: aoqi@0: public final boolean hasSubClasses() { aoqi@0: return hasSubClasses; aoqi@0: } aoqi@0: aoqi@0: public final boolean hasAttributeWildcard() { aoqi@0: return declaresAttributeWildcard() || inheritsAttributeWildcard(); aoqi@0: } aoqi@0: aoqi@0: public final boolean inheritsAttributeWildcard() { aoqi@0: return getInheritedAttributeWildcard()!=null; aoqi@0: } aoqi@0: aoqi@0: public final boolean declaresAttributeWildcard() { aoqi@0: return attributeWildcard!=null; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Gets the {@link PropertySeed} object for the inherited attribute wildcard. aoqi@0: */ aoqi@0: private PropertySeed getInheritedAttributeWildcard() { aoqi@0: for( ClassInfoImpl c=getBaseClass(); c!=null; c=c.getBaseClass() ) aoqi@0: if(c.attributeWildcard!=null) aoqi@0: return c.attributeWildcard; aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public final QName getElementName() { aoqi@0: return elementName; aoqi@0: } aoqi@0: aoqi@0: public final QName getTypeName() { aoqi@0: return typeName; aoqi@0: } aoqi@0: aoqi@0: public final boolean isSimpleType() { aoqi@0: List props = getProperties(); aoqi@0: if(props.size()!=1) return false; aoqi@0: return props.get(0).kind()==PropertyKind.VALUE; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Called after all the {@link TypeInfo}s are collected into the {@link #owner}. aoqi@0: */ aoqi@0: @Override aoqi@0: /*package*/ void link() { aoqi@0: getProperties(); // make sure properties!=null aoqi@0: aoqi@0: // property name collision cehck aoqi@0: Map names = new HashMap(); aoqi@0: for( PropertyInfoImpl p : properties ) { aoqi@0: p.link(); aoqi@0: PropertyInfoImpl old = names.put(p.getName(),p); aoqi@0: if(old!=null) { aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.PROPERTY_COLLISION.format(p.getName()), aoqi@0: p, old )); aoqi@0: } aoqi@0: } aoqi@0: super.link(); aoqi@0: } aoqi@0: aoqi@0: public Location getLocation() { aoqi@0: return nav().getClassLocation(clazz); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * XmlType allows specification of factoryClass and aoqi@0: * factoryMethod. There are to be used if no default aoqi@0: * constructor is found. aoqi@0: * aoqi@0: * @return aoqi@0: * true if the factory method was found. False if not. aoqi@0: */ aoqi@0: private boolean hasFactoryConstructor(XmlType t){ aoqi@0: if (t == null) return false; aoqi@0: aoqi@0: String method = t.factoryMethod(); aoqi@0: T fClass = reader().getClassValue(t, "factoryClass"); aoqi@0: if (method.length() > 0){ aoqi@0: if(nav().isSameType(fClass, nav().ref(XmlType.DEFAULT.class))){ aoqi@0: fClass = nav().use(clazz); aoqi@0: } aoqi@0: for(M m: nav().getDeclaredMethods(nav().asDecl(fClass))){ aoqi@0: //- Find the zero-arg public static method with the required return type aoqi@0: if (nav().getMethodName(m).equals(method) && aoqi@0: nav().isSameType(nav().getReturnType(m), nav().use(clazz)) && aoqi@0: nav().getMethodParameters(m).length == 0 && aoqi@0: nav().isStaticMethod(m)){ aoqi@0: factoryMethod = m; aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: if (factoryMethod == null){ aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.NO_FACTORY_METHOD.format(nav().getClassName(nav().asDecl(fClass)), method), this )); aoqi@0: } aoqi@0: } else if(!nav().isSameType(fClass, nav().ref(XmlType.DEFAULT.class))){ aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.FACTORY_CLASS_NEEDS_FACTORY_METHOD.format(nav().getClassName(nav().asDecl(fClass))), this )); aoqi@0: } aoqi@0: return factoryMethod != null; aoqi@0: } aoqi@0: aoqi@0: public Method getFactoryMethod(){ aoqi@0: return (Method) factoryMethod; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String toString() { aoqi@0: return "ClassInfo("+clazz+')'; aoqi@0: } aoqi@0: aoqi@0: private static final String[] DEFAULT_ORDER = new String[0]; aoqi@0: }