0001: /*
0002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package com.sun.xml.internal.bind.v2.model.impl;
0027:
0028: import java.lang.annotation.Annotation;
0029: import java.lang.reflect.Method;
0030: import java.util.Collection;
0031: import java.util.Collections;
0032: import java.util.Comparator;
0033: import java.util.HashMap;
0034: import java.util.HashSet;
0035: import java.util.LinkedHashMap;
0036: import java.util.List;
0037: import java.util.Map;
0038: import java.util.Set;
0039: import java.util.TreeSet;
0040:
0041: import javax.xml.bind.annotation.XmlAccessOrder;
0042: import javax.xml.bind.annotation.XmlAccessType;
0043: import javax.xml.bind.annotation.XmlAccessorOrder;
0044: import javax.xml.bind.annotation.XmlAccessorType;
0045: import javax.xml.bind.annotation.XmlAnyAttribute;
0046: import javax.xml.bind.annotation.XmlAnyElement;
0047: import javax.xml.bind.annotation.XmlAttachmentRef;
0048: import javax.xml.bind.annotation.XmlAttribute;
0049: import javax.xml.bind.annotation.XmlElement;
0050: import javax.xml.bind.annotation.XmlElementRef;
0051: import javax.xml.bind.annotation.XmlElementRefs;
0052: import javax.xml.bind.annotation.XmlElementWrapper;
0053: import javax.xml.bind.annotation.XmlElements;
0054: import javax.xml.bind.annotation.XmlID;
0055: import javax.xml.bind.annotation.XmlIDREF;
0056: import javax.xml.bind.annotation.XmlInlineBinaryData;
0057: import javax.xml.bind.annotation.XmlList;
0058: import javax.xml.bind.annotation.XmlMimeType;
0059: import javax.xml.bind.annotation.XmlMixed;
0060: import javax.xml.bind.annotation.XmlRootElement;
0061: import javax.xml.bind.annotation.XmlSchemaType;
0062: import javax.xml.bind.annotation.XmlTransient;
0063: import javax.xml.bind.annotation.XmlType;
0064: import javax.xml.bind.annotation.XmlValue;
0065: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
0066: import javax.xml.namespace.QName;
0067:
0068: import com.sun.istack.internal.FinalArrayList;
0069: import com.sun.xml.internal.bind.annotation.XmlLocation;
0070: import com.sun.xml.internal.bind.v2.TODO;
0071: import com.sun.xml.internal.bind.v2.model.annotation.Locatable;
0072: import com.sun.xml.internal.bind.v2.model.annotation.MethodLocatable;
0073: import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
0074: import com.sun.xml.internal.bind.v2.model.core.Element;
0075: import com.sun.xml.internal.bind.v2.model.core.ID;
0076: import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
0077: import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
0078: import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
0079: import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
0080: import com.sun.xml.internal.bind.v2.runtime.Location;
0081:
0082: /**
0083: * A part of the {@link ClassInfo} that doesn't depend on a particular
0084: * reflection library.
0085: *
0086: * @author Kohsuke Kawaguchi (kk@kohsuke.org)
0087: */
0088: class ClassInfoImpl<T, C, F, M> extends TypeInfoImpl<T, C, F, M>
0089: implements ClassInfo<T, C>, Element<T, C> {
0090:
0091: protected final C clazz;
0092:
0093: /**
0094: * @see #getElementName()
0095: */
0096: private final QName elementName;
0097:
0098: /**
0099: * @see #getTypeName()
0100: */
0101: private final QName typeName;
0102:
0103: /**
0104: * Lazily created.
0105: *
0106: * @see #getProperties()
0107: */
0108: private FinalArrayList<PropertyInfoImpl<T, C, F, M>> properties;
0109:
0110: /**
0111: * The property order.
0112: *
0113: * null if unordered. {@link #DEFAULT_ORDER} if ordered but the order is defaulted
0114: *
0115: * @see #isOrdered()
0116: */
0117: private final String[] propOrder;
0118:
0119: /**
0120: * Lazily computed.
0121: *
0122: * To avoid the cyclic references of the form C1 --base--> C2 --property--> C1.
0123: */
0124: private ClassInfoImpl<T, C, F, M> baseClass;
0125:
0126: private boolean baseClassComputed = false;
0127:
0128: private boolean hasSubClasses = false;
0129:
0130: /**
0131: * If this class has a declared (not inherited) attribute wildcard, keep the reference
0132: * to it.
0133: *
0134: * This parameter is initialized at the construction time and never change.
0135: */
0136: protected/*final*/PropertySeed<T, C, F, M> attributeWildcard;
0137:
0138: /**
0139: * @see #getFactoryMethod()
0140: */
0141: private M factoryMethod = null;
0142:
0143: ClassInfoImpl(ModelBuilder<T, C, F, M> builder, Locatable upstream,
0144: C clazz) {
0145: super (builder, upstream);
0146: this .clazz = clazz;
0147: assert clazz != null;
0148:
0149: // compute the element name
0150: elementName = parseElementName(clazz);
0151:
0152: // compute the type name
0153: XmlType t = reader().getClassAnnotation(XmlType.class, clazz,
0154: this );
0155: typeName = parseTypeName(clazz, t);
0156:
0157: if (t != null) {
0158: String[] propOrder = t.propOrder();
0159: if (propOrder.length == 0)
0160: this .propOrder = null; // unordered
0161: else {
0162: if (propOrder[0].length() == 0)
0163: this .propOrder = DEFAULT_ORDER;
0164: else
0165: this .propOrder = propOrder;
0166: }
0167: } else {
0168: propOrder = DEFAULT_ORDER;
0169: }
0170:
0171: // the class must have the default constructor
0172: if (!hasFactoryConstructor(t)) {
0173: if (!nav().hasDefaultConstructor(clazz)) {
0174: Messages msg;
0175: if (nav().isInterface(clazz))
0176: msg = Messages.CANT_HANDLE_INTERFACE;
0177: else
0178: msg = Messages.NO_DEFAULT_CONSTRUCTOR;
0179:
0180: builder.reportError(new IllegalAnnotationException(msg
0181: .format(nav().getClassName(clazz)), this ));
0182: }
0183: }
0184: }
0185:
0186: public ClassInfoImpl<T, C, F, M> getBaseClass() {
0187: if (!baseClassComputed) {
0188: baseClassComputed = true;
0189: // compute the base class
0190: C s = nav().getSuperClass(clazz);
0191: if (s == null || s == nav().asDecl(Object.class))
0192: baseClass = null;
0193: else {
0194: baseClass = (ClassInfoImpl<T, C, F, M>) builder
0195: .getClassInfo(s, this );
0196: baseClass.hasSubClasses = true;
0197: }
0198: }
0199: return baseClass;
0200: }
0201:
0202: /**
0203: * {@inheritDoc}
0204: *
0205: * The substitution hierarchy is the same as the inheritance hierarchy.
0206: */
0207: public final Element<T, C> getSubstitutionHead() {
0208: ClassInfoImpl<T, C, F, M> c = getBaseClass();
0209: while (c != null && !c.isElement())
0210: c = c.getBaseClass();
0211: return c;
0212: }
0213:
0214: public final C getClazz() {
0215: return clazz;
0216: }
0217:
0218: /**
0219: * When a bean binds to an element, it's always through {@link XmlRootElement},
0220: * so this method always return null.
0221: *
0222: * @deprecated
0223: * you shouldn't be invoking this method on {@link ClassInfoImpl}.
0224: */
0225: public ClassInfoImpl<T, C, F, M> getScope() {
0226: return null;
0227: }
0228:
0229: public final T getType() {
0230: return nav().use(clazz);
0231: }
0232:
0233: /**
0234: * A {@link ClassInfo} can be referenced by {@link XmlIDREF} if
0235: * it has an ID property.
0236: */
0237: public boolean canBeReferencedByIDREF() {
0238: for (PropertyInfo<T, C> p : getProperties()) {
0239: if (p.id() == ID.ID)
0240: return true;
0241: }
0242: ClassInfoImpl<T, C, F, M> base = getBaseClass();
0243: if (base != null)
0244: return base.canBeReferencedByIDREF();
0245: else
0246: return false;
0247: }
0248:
0249: public final String getName() {
0250: return nav().getClassName(clazz);
0251: }
0252:
0253: public <A extends Annotation> A readAnnotation(Class<A> a) {
0254: return reader().getClassAnnotation(a, clazz, this );
0255: }
0256:
0257: public Element<T, C> asElement() {
0258: if (isElement())
0259: return this ;
0260: else
0261: return null;
0262: }
0263:
0264: public List<? extends PropertyInfo<T, C>> getProperties() {
0265: if (properties != null)
0266: return properties;
0267:
0268: // check the access type first
0269: XmlAccessType at = getAccessType();
0270:
0271: properties = new FinalArrayList<PropertyInfoImpl<T, C, F, M>>();
0272:
0273: // find properties from fields
0274: for (F f : nav().getDeclaredFields(clazz)) {
0275: Annotation[] annotations = reader().getAllFieldAnnotations(
0276: f, this );
0277: if (nav().isTransient(f)) {
0278: // it's an error for transient field to have any binding annotation
0279: if (hasJAXBAnnotation(annotations))
0280: builder.reportError(new IllegalAnnotationException(
0281: Messages.TRANSIENT_FIELD_NOT_BINDABLE
0282: .format(nav().getFieldName(f)),
0283: getSomeJAXBAnnotation(annotations)));
0284: } else if (nav().isStaticField(f)) {
0285: // static fields are bound only when there's explicit annotation.
0286: if (hasJAXBAnnotation(annotations))
0287: addProperty(createFieldSeed(f), annotations);
0288: } else {
0289: if (at == XmlAccessType.FIELD
0290: || (at == XmlAccessType.PUBLIC_MEMBER && nav()
0291: .isPublicField(f))
0292: || hasJAXBAnnotation(annotations))
0293: addProperty(createFieldSeed(f), annotations);
0294: checkFieldXmlLocation(f);
0295: }
0296: }
0297:
0298: findGetterSetterProperties(at);
0299:
0300: if (propOrder == DEFAULT_ORDER || propOrder == null) {
0301: XmlAccessOrder ao = getAccessorOrder();
0302: if (ao == XmlAccessOrder.ALPHABETICAL)
0303: Collections.sort(properties);
0304: } else {
0305: //sort them as specified
0306: PropertySorter sorter = new PropertySorter();
0307: for (PropertyInfoImpl p : properties)
0308: sorter.checkedGet(p); // have it check for errors
0309: Collections.sort(properties, sorter);
0310: sorter.checkUnusedProperties();
0311: }
0312:
0313: {// additional error checks
0314: PropertyInfoImpl vp = null; // existing value property
0315: PropertyInfoImpl ep = null; // existing element property
0316:
0317: for (PropertyInfoImpl p : properties) {
0318: switch (p.kind()) {
0319: case ELEMENT:
0320: case REFERENCE:
0321: case MAP:
0322: ep = p;
0323: break;
0324: case VALUE:
0325: if (vp != null) {
0326: // can't have multiple value properties.
0327: builder
0328: .reportError(new IllegalAnnotationException(
0329: Messages.MULTIPLE_VALUE_PROPERTY
0330: .format(), vp, p));
0331: }
0332: if (getBaseClass() != null) {
0333: builder
0334: .reportError(new IllegalAnnotationException(
0335: Messages.XMLVALUE_IN_DERIVED_TYPE
0336: .format(), p));
0337: }
0338: vp = p;
0339: break;
0340: case ATTRIBUTE:
0341: break; // noop
0342: default:
0343: assert false;
0344: }
0345: }
0346:
0347: if (ep != null && vp != null) {
0348: // can't have element and value property at the same time
0349: builder.reportError(new IllegalAnnotationException(
0350: Messages.ELEMENT_AND_VALUE_PROPERTY.format(),
0351: vp, ep));
0352: }
0353: }
0354:
0355: return properties;
0356: }
0357:
0358: public PropertyInfo<T, C> getProperty(String name) {
0359: for (PropertyInfo<T, C> p : getProperties()) {
0360: if (p.getName().equals(name))
0361: return p;
0362: }
0363: return null;
0364: }
0365:
0366: /**
0367: * This hook is used by {@link RuntimeClassInfoImpl} to look for {@link XmlLocation}.
0368: */
0369: protected void checkFieldXmlLocation(F f) {
0370: }
0371:
0372: /**
0373: * Gets an annotation that are allowed on both class and type.
0374: */
0375: private <T extends Annotation> T getClassOrPackageAnnotation(
0376: Class<T> type) {
0377: T t = reader().getClassAnnotation(type, clazz, this );
0378: if (t != null)
0379: return t;
0380: // defaults to the package level
0381: return reader().getPackageAnnotation(type, clazz, this );
0382: }
0383:
0384: /**
0385: * Computes the {@link XmlAccessType} on this class by looking at {@link XmlAccessorType}
0386: * annotations.
0387: */
0388: private XmlAccessType getAccessType() {
0389: XmlAccessorType xat = getClassOrPackageAnnotation(XmlAccessorType.class);
0390: if (xat != null)
0391: return xat.value();
0392: else
0393: return XmlAccessType.PUBLIC_MEMBER;
0394: }
0395:
0396: /**
0397: * Gets the accessor order for this class by consulting {@link XmlAccessorOrder}.
0398: */
0399: private XmlAccessOrder getAccessorOrder() {
0400: XmlAccessorOrder xao = getClassOrPackageAnnotation(XmlAccessorOrder.class);
0401: if (xao != null)
0402: return xao.value();
0403: else
0404: return XmlAccessOrder.UNDEFINED;
0405: }
0406:
0407: /**
0408: * Compares orders among {@link PropertyInfoImpl} according to {@link ClassInfoImpl#propOrder}.
0409: *
0410: * <p>
0411: * extends {@link HashMap} to save memory.
0412: */
0413: private final class PropertySorter extends HashMap<String, Integer>
0414: implements Comparator<PropertyInfoImpl> {
0415: /**
0416: * Mark property names that are used, so that we can report unused property names in the propOrder array.
0417: */
0418: PropertyInfoImpl[] used = new PropertyInfoImpl[propOrder.length];
0419:
0420: /**
0421: * If any name collides, it will be added to this set.
0422: * This is used to avoid repeating the same error message.
0423: */
0424: private Set<String> collidedNames;
0425:
0426: PropertySorter() {
0427: super (propOrder.length);
0428: for (String name : propOrder)
0429: if (put(name, size()) != null) {
0430: // two properties with the same name
0431: builder.reportError(new IllegalAnnotationException(
0432: Messages.DUPLICATE_ENTRY_IN_PROP_ORDER
0433: .format(name), ClassInfoImpl.this ));
0434: }
0435: }
0436:
0437: public int compare(PropertyInfoImpl o1, PropertyInfoImpl o2) {
0438: int lhs = checkedGet(o1);
0439: int rhs = checkedGet(o2);
0440:
0441: return lhs - rhs;
0442: }
0443:
0444: private int checkedGet(PropertyInfoImpl p) {
0445: Integer i = get(p.getName());
0446: if (i == null) {
0447: // missing
0448: if ((p.kind().isOrdered))
0449: builder.reportError(new IllegalAnnotationException(
0450: Messages.PROPERTY_MISSING_FROM_ORDER
0451: .format(p.getName()), p));
0452:
0453: // give it an order to recover from an error
0454: i = size();
0455: put(p.getName(), i);
0456: }
0457:
0458: // mark the used field
0459: int ii = i;
0460: if (ii < used.length) {
0461: if (used[ii] != null && used[ii] != p) {
0462: if (collidedNames == null)
0463: collidedNames = new HashSet<String>();
0464:
0465: if (collidedNames.add(p.getName()))
0466: // report the error only on the first time
0467: builder
0468: .reportError(new IllegalAnnotationException(
0469: Messages.DUPLICATE_PROPERTIES
0470: .format(p.getName()),
0471: p, used[ii]));
0472: }
0473: used[ii] = p;
0474: }
0475:
0476: return i;
0477: }
0478:
0479: /**
0480: * Report errors for unused propOrder entries.
0481: */
0482: public void checkUnusedProperties() {
0483: for (int i = 0; i < used.length; i++)
0484: if (used[i] == null) {
0485: String unusedName = propOrder[i];
0486: builder
0487: .reportError(new IllegalAnnotationException(
0488: Messages.PROPERTY_ORDER_CONTAINS_UNUSED_ENTRY
0489: .format(unusedName),
0490: ClassInfoImpl.this ));
0491: }
0492: }
0493: }
0494:
0495: public boolean hasProperties() {
0496: return !properties.isEmpty();
0497: }
0498:
0499: /**
0500: * Picks the first non-null argument, or null if all arguments are null.
0501: */
0502: private static <T> T pickOne(T... args) {
0503: for (T arg : args)
0504: if (arg != null)
0505: return arg;
0506: return null;
0507: }
0508:
0509: private static <T> List<T> makeSet(T... args) {
0510: List<T> l = new FinalArrayList<T>();
0511: for (T arg : args)
0512: if (arg != null)
0513: l.add(arg);
0514: return l;
0515: }
0516:
0517: private static final class ConflictException extends Exception {
0518: final List<Annotation> annotations;
0519:
0520: public ConflictException(List<Annotation> one) {
0521: this .annotations = one;
0522: }
0523: }
0524:
0525: private static final class DupliateException extends Exception {
0526: final Annotation a1, a2;
0527:
0528: public DupliateException(Annotation a1, Annotation a2) {
0529: this .a1 = a1;
0530: this .a2 = a2;
0531: }
0532: }
0533:
0534: /**
0535: * Represents 6 groups of secondary annotations
0536: */
0537: private static enum SecondaryAnnotation {
0538: JAVA_TYPE(0x01, XmlJavaTypeAdapter.class), ID_IDREF(0x02,
0539: XmlID.class, XmlIDREF.class), BINARY(0x04,
0540: XmlInlineBinaryData.class, XmlMimeType.class,
0541: XmlAttachmentRef.class), ELEMENT_WRAPPER(0x08,
0542: XmlElementWrapper.class), LIST(0x10, XmlList.class), SCHEMA_TYPE(
0543: 0x20, XmlSchemaType.class);
0544:
0545: /**
0546: * Each constant gets an unique bit mask so that the presence/absence
0547: * of them can be represented in a single byte.
0548: */
0549: final int bitMask;
0550: /**
0551: * List of annotations that belong to this member.
0552: */
0553: final Class<? extends Annotation>[] members;
0554:
0555: SecondaryAnnotation(int bitMask,
0556: Class<? extends Annotation>... members) {
0557: this .bitMask = bitMask;
0558: this .members = members;
0559: }
0560: }
0561:
0562: private static final SecondaryAnnotation[] SECONDARY_ANNOTATIONS = SecondaryAnnotation
0563: .values();
0564:
0565: /**
0566: * Represents 7 groups of properties.
0567: *
0568: * Each instance is also responsible for rejecting annotations
0569: * that are not allowed on that kind.
0570: */
0571: private static enum PropertyGroup {
0572: TRANSIENT(false, false, false, false, false, false), ANY_ATTRIBUTE(
0573: true, false, false, false, false, false), ATTRIBUTE(
0574: true, true, true, false, true, true), VALUE(true, true,
0575: true, false, true, true), ELEMENT(true, true, true,
0576: true, true, true), ELEMENT_REF(true, false, false,
0577: true, false, false), MAP(false, false, false, true,
0578: false, false);
0579:
0580: /**
0581: * Bit mask that represents secondary annotations that are allowed on this group.
0582: *
0583: * T = not allowed, F = allowed
0584: */
0585: final int allowedsecondaryAnnotations;
0586:
0587: PropertyGroup(boolean... bits) {
0588: int mask = 0;
0589: assert bits.length == SECONDARY_ANNOTATIONS.length;
0590: for (int i = 0; i < bits.length; i++) {
0591: if (bits[i])
0592: mask |= SECONDARY_ANNOTATIONS[i].bitMask;
0593: }
0594: allowedsecondaryAnnotations = ~mask;
0595: }
0596:
0597: boolean allows(SecondaryAnnotation a) {
0598: return (allowedsecondaryAnnotations & a.bitMask) == 0;
0599: }
0600: }
0601:
0602: private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
0603:
0604: /**
0605: * All the annotations in JAXB to their internal index.
0606: */
0607: private static final HashMap<Class, Integer> ANNOTATION_NUMBER_MAP = new HashMap<Class, Integer>();
0608: static {
0609: Class[] annotations = { XmlTransient.class, // 0
0610: XmlAnyAttribute.class, // 1
0611: XmlAttribute.class, // 2
0612: XmlValue.class, // 3
0613: XmlElement.class, // 4
0614: XmlElements.class, // 5
0615: XmlElementRef.class, // 6
0616: XmlElementRefs.class, // 7
0617: XmlAnyElement.class, // 8
0618: XmlMixed.class, // 9
0619: };
0620:
0621: HashMap<Class, Integer> m = ANNOTATION_NUMBER_MAP;
0622:
0623: // characterizing annotations
0624: for (Class c : annotations)
0625: m.put(c, m.size());
0626:
0627: // secondary annotations
0628: int index = 20;
0629: for (SecondaryAnnotation sa : SECONDARY_ANNOTATIONS) {
0630: for (Class member : sa.members)
0631: m.put(member, index);
0632: index++;
0633: }
0634: }
0635:
0636: private void checkConflict(Annotation a, Annotation b)
0637: throws DupliateException {
0638: assert b != null;
0639: if (a != null)
0640: throw new DupliateException(a, b);
0641: }
0642:
0643: /**
0644: * Called only from {@link #getProperties()}.
0645: *
0646: * <p>
0647: * This is where we decide the type of the property and checks for annotations
0648: * that are not allowed.
0649: *
0650: * @param annotations
0651: * all annotations on this property. It's the same as
0652: * {@code seed.readAllAnnotation()}, but taken as a parameter
0653: * because the caller should know it already.
0654: */
0655: private void addProperty(PropertySeed<T, C, F, M> seed,
0656: Annotation[] annotations) {
0657: // since typically there's a very few annotations on a method,
0658: // this runs faster than checking for each annotation via readAnnotation(A)
0659:
0660: // characterizing annotations. these annotations (or lack thereof) decides
0661: // the kind of the property it goes to.
0662: // I wish I could use an array...
0663: XmlTransient t = null;
0664: XmlAnyAttribute aa = null;
0665: XmlAttribute a = null;
0666: XmlValue v = null;
0667: XmlElement e1 = null;
0668: XmlElements e2 = null;
0669: XmlElementRef r1 = null;
0670: XmlElementRefs r2 = null;
0671: XmlAnyElement xae = null;
0672: XmlMixed mx = null;
0673:
0674: // encountered secondary annotations are accumulated into a bit mask
0675: int secondaryAnnotations = 0;
0676:
0677: try {
0678: for (Annotation ann : annotations) {
0679: Integer index = ANNOTATION_NUMBER_MAP.get(ann
0680: .annotationType());
0681: if (index == null)
0682: continue;
0683: switch (index) {
0684: case 0:
0685: checkConflict(t, ann);
0686: t = (XmlTransient) ann;
0687: break;
0688: case 1:
0689: checkConflict(aa, ann);
0690: aa = (XmlAnyAttribute) ann;
0691: break;
0692: case 2:
0693: checkConflict(a, ann);
0694: a = (XmlAttribute) ann;
0695: break;
0696: case 3:
0697: checkConflict(v, ann);
0698: v = (XmlValue) ann;
0699: break;
0700: case 4:
0701: checkConflict(e1, ann);
0702: e1 = (XmlElement) ann;
0703: break;
0704: case 5:
0705: checkConflict(e2, ann);
0706: e2 = (XmlElements) ann;
0707: break;
0708: case 6:
0709: checkConflict(r1, ann);
0710: r1 = (XmlElementRef) ann;
0711: break;
0712: case 7:
0713: checkConflict(r2, ann);
0714: r2 = (XmlElementRefs) ann;
0715: break;
0716: case 8:
0717: checkConflict(xae, ann);
0718: xae = (XmlAnyElement) ann;
0719: break;
0720: case 9:
0721: checkConflict(mx, ann);
0722: mx = (XmlMixed) ann;
0723: break;
0724: default:
0725: // secondary annotations
0726: secondaryAnnotations |= (1 << (index - 20));
0727: break;
0728: }
0729: }
0730:
0731: // determine the group kind, and also count the numbers, since
0732: // characterizing annotations are mutually exclusive.
0733: PropertyGroup group = null;
0734: int groupCount = 0;
0735:
0736: if (t != null) {
0737: group = PropertyGroup.TRANSIENT;
0738: groupCount++;
0739: }
0740: if (aa != null) {
0741: group = PropertyGroup.ANY_ATTRIBUTE;
0742: groupCount++;
0743: }
0744: if (a != null) {
0745: group = PropertyGroup.ATTRIBUTE;
0746: groupCount++;
0747: }
0748: if (v != null) {
0749: group = PropertyGroup.VALUE;
0750: groupCount++;
0751: }
0752: if (e1 != null || e2 != null) {
0753: group = PropertyGroup.ELEMENT;
0754: groupCount++;
0755: }
0756: if (r1 != null || r2 != null || xae != null || mx != null) {
0757: group = PropertyGroup.ELEMENT_REF;
0758: groupCount++;
0759: }
0760:
0761: if (groupCount > 1) {
0762: // collision between groups
0763: List<Annotation> err = makeSet(t, aa, a, v, pickOne(e1,
0764: e2), pickOne(r1, r2, xae));
0765: throw new ConflictException(err);
0766: }
0767:
0768: if (group == null) {
0769: // if no characterizing annotation was found, it's either element or map
0770: // sniff the signature and then decide.
0771: assert groupCount == 0;
0772:
0773: // UGLY: the presence of XmlJavaTypeAdapter makes it an element property. ARGH.
0774: if (nav().isSubClassOf(seed.getRawType(),
0775: nav().ref(Map.class))
0776: && !seed
0777: .hasAnnotation(XmlJavaTypeAdapter.class))
0778: group = PropertyGroup.MAP;
0779: else
0780: group = PropertyGroup.ELEMENT;
0781: }
0782:
0783: // group determined by now
0784: // make sure that there are no prohibited secondary annotations
0785: if ((secondaryAnnotations & group.allowedsecondaryAnnotations) != 0) {
0786: // uh oh. find the offending annotation
0787: for (SecondaryAnnotation sa : SECONDARY_ANNOTATIONS) {
0788: if (group.allows(sa))
0789: continue;
0790: for (Class<? extends Annotation> m : sa.members) {
0791: Annotation offender = seed.readAnnotation(m);
0792: if (offender != null) {
0793: // found it
0794: builder
0795: .reportError(new IllegalAnnotationException(
0796: Messages.ANNOTATION_NOT_ALLOWED
0797: .format(m
0798: .getSimpleName()),
0799: offender));
0800: return;
0801: }
0802: }
0803: }
0804: // there must have been an offender
0805: assert false;
0806: }
0807:
0808: // actually create annotations
0809: switch (group) {
0810: case TRANSIENT:
0811: return;
0812: case ANY_ATTRIBUTE:
0813: // an attribute wildcard property
0814: if (attributeWildcard != null) {
0815: builder.reportError(new IllegalAnnotationException(
0816: Messages.TWO_ATTRIBUTE_WILDCARDS
0817: .format(nav().getClassName(
0818: getClazz())), aa,
0819: attributeWildcard));
0820: return; // recover by ignore
0821: }
0822: attributeWildcard = seed;
0823:
0824: if (inheritsAttributeWildcard()) {
0825: builder.reportError(new IllegalAnnotationException(
0826: Messages.SUPER_CLASS_HAS_WILDCARD.format(),
0827: aa, getInheritedAttributeWildcard()));
0828: return;
0829: }
0830:
0831: // check the signature and make sure it's assignable to Map
0832: if (!nav().isSubClassOf(seed.getRawType(),
0833: nav().ref(Map.class))) {
0834: builder.reportError(new IllegalAnnotationException(
0835: Messages.INVALID_ATTRIBUTE_WILDCARD_TYPE
0836: .format(nav().getTypeName(
0837: seed.getRawType())), aa,
0838: getInheritedAttributeWildcard()));
0839: return;
0840: }
0841:
0842: return;
0843: case ATTRIBUTE:
0844: properties.add(createAttributeProperty(seed));
0845: return;
0846: case VALUE:
0847: properties.add(createValueProperty(seed));
0848: return;
0849: case ELEMENT:
0850: properties.add(createElementProperty(seed));
0851: return;
0852: case ELEMENT_REF:
0853: properties.add(createReferenceProperty(seed));
0854: return;
0855: case MAP:
0856: properties.add(createMapProperty(seed));
0857: return;
0858: default:
0859: assert false;
0860: }
0861: } catch (ConflictException x) {
0862: // report a conflicting annotation
0863: List<Annotation> err = x.annotations;
0864:
0865: builder
0866: .reportError(new IllegalAnnotationException(
0867: Messages.MUTUALLY_EXCLUSIVE_ANNOTATIONS
0868: .format(nav().getClassName(
0869: getClazz())
0870: + '#' + seed.getName(), err
0871: .get(0).annotationType()
0872: .getName(), err.get(1)
0873: .annotationType().getName()),
0874: err.get(0), err.get(1)));
0875:
0876: // recover by ignoring this property
0877: } catch (DupliateException e) {
0878: // both are present
0879: builder.reportError(new IllegalAnnotationException(
0880: Messages.DUPLICATE_ANNOTATIONS.format(e.a1
0881: .annotationType().getName()), e.a1, e.a2));
0882: // recover by ignoring this property
0883:
0884: }
0885: }
0886:
0887: protected ReferencePropertyInfoImpl<T, C, F, M> createReferenceProperty(
0888: PropertySeed<T, C, F, M> seed) {
0889: return new ReferencePropertyInfoImpl<T, C, F, M>(this , seed);
0890: }
0891:
0892: protected AttributePropertyInfoImpl<T, C, F, M> createAttributeProperty(
0893: PropertySeed<T, C, F, M> seed) {
0894: return new AttributePropertyInfoImpl<T, C, F, M>(this , seed);
0895: }
0896:
0897: protected ValuePropertyInfoImpl<T, C, F, M> createValueProperty(
0898: PropertySeed<T, C, F, M> seed) {
0899: return new ValuePropertyInfoImpl<T, C, F, M>(this , seed);
0900: }
0901:
0902: protected ElementPropertyInfoImpl<T, C, F, M> createElementProperty(
0903: PropertySeed<T, C, F, M> seed) {
0904: return new ElementPropertyInfoImpl<T, C, F, M>(this , seed);
0905: }
0906:
0907: protected MapPropertyInfoImpl<T, C, F, M> createMapProperty(
0908: PropertySeed<T, C, F, M> seed) {
0909: return new MapPropertyInfoImpl<T, C, F, M>(this , seed);
0910: }
0911:
0912: /**
0913: * Adds properties that consists of accessors.
0914: */
0915: private void findGetterSetterProperties(XmlAccessType at) {
0916: TODO.checkSpec(); // TODO: I don't think the spec describes how properties are found
0917:
0918: // in the first step we accumulate getters and setters
0919: // into this map keyed by the property name.
0920: Map<String, M> getters = new LinkedHashMap<String, M>();
0921: Map<String, M> setters = new LinkedHashMap<String, M>();
0922:
0923: Collection<? extends M> methods = nav().getDeclaredMethods(
0924: clazz);
0925: for (M method : methods) {
0926: boolean used = false; // if this method is added to getters or setters
0927:
0928: if (nav().isBridgeMethod(method))
0929: continue; // ignore
0930:
0931: String name = nav().getMethodName(method);
0932: int arity = nav().getMethodParameters(method).length;
0933:
0934: if (nav().isStaticMethod(method)) {
0935: ensureNoAnnotation(method);
0936: continue;
0937: }
0938:
0939: // don't look at XmlTransient. We'll deal with that later.
0940:
0941: // is this a get method?
0942: String propName = getPropertyNameFromGetMethod(name);
0943: if (propName != null) {
0944: if (arity == 0) {
0945: getters.put(propName, method);
0946: used = true;
0947: }
0948: // TODO: do we support indexed property?
0949: }
0950:
0951: // is this a set method?
0952: propName = getPropertyNameFromSetMethod(name);
0953: if (propName != null) {
0954: if (arity == 1) {
0955: // TODO: we should check collisions like setFoo(int) and setFoo(String)
0956: setters.put(propName, method);
0957: used = true;
0958: }
0959: // TODO: do we support indexed property?
0960: }
0961:
0962: if (!used)
0963: ensureNoAnnotation(method);
0964: }
0965:
0966: // compute the intersection
0967: Set<String> complete = new TreeSet<String>(getters.keySet());
0968: complete.retainAll(setters.keySet());
0969:
0970: resurrect(getters, complete);
0971: resurrect(setters, complete);
0972:
0973: // then look for read/write properties.
0974: for (String name : complete) {
0975: M getter = getters.get(name);
0976: M setter = setters.get(name);
0977:
0978: Annotation[] ga = getter != null ? reader()
0979: .getAllMethodAnnotations(getter,
0980: new MethodLocatable<M>(this , getter, nav()))
0981: : EMPTY_ANNOTATIONS;
0982: Annotation[] sa = setter != null ? reader()
0983: .getAllMethodAnnotations(setter,
0984: new MethodLocatable<M>(this , setter, nav()))
0985: : EMPTY_ANNOTATIONS;
0986:
0987: boolean hasAnnotation = hasJAXBAnnotation(ga)
0988: || hasJAXBAnnotation(sa);
0989: boolean isOverriding = false;
0990: if (!hasAnnotation) {
0991: // checking if the method is overriding others isn't free,
0992: // so we don't compute it if it's not necessary.
0993: isOverriding = (getter != null && nav().isOverriding(
0994: getter))
0995: || (setter != null && nav()
0996: .isOverriding(setter));
0997: }
0998:
0999: if ((at == XmlAccessType.PROPERTY && !isOverriding)
1000: || (at == XmlAccessType.PUBLIC_MEMBER
1001: && isConsideredPublic(getter)
1002: && isConsideredPublic(setter) && !isOverriding)
1003: || hasAnnotation) {
1004: // make sure that the type is consistent
1005: if (getter != null
1006: && setter != null
1007: && !nav().getReturnType(getter).equals(
1008: nav().getMethodParameters(setter)[0])) {
1009: // inconsistent
1010: builder
1011: .reportError(new IllegalAnnotationException(
1012: Messages.GETTER_SETTER_INCOMPATIBLE_TYPE
1013: .format(
1014: nav()
1015: .getTypeName(
1016: nav()
1017: .getReturnType(
1018: getter)),
1019: nav()
1020: .getTypeName(
1021: nav()
1022: .getMethodParameters(
1023: setter)[0])),
1024: new MethodLocatable<M>(this ,
1025: getter, nav()),
1026: new MethodLocatable<M>(this ,
1027: setter, nav())));
1028: continue;
1029: }
1030:
1031: // merge annotations from two list
1032: Annotation[] r;
1033: if (ga.length == 0) {
1034: r = sa;
1035: } else if (sa.length == 0) {
1036: r = ga;
1037: } else {
1038: r = new Annotation[ga.length + sa.length];
1039: System.arraycopy(ga, 0, r, 0, ga.length);
1040: System.arraycopy(sa, 0, r, ga.length, sa.length);
1041: }
1042:
1043: addProperty(createAccessorSeed(getter, setter), r);
1044: }
1045: }
1046: // done with complete pairs
1047: getters.keySet().removeAll(complete);
1048: setters.keySet().removeAll(complete);
1049:
1050: // TODO: think about
1051: // class Foo {
1052: // int getFoo();
1053: // }
1054: // class Bar extends Foo {
1055: // void setFoo(int x);
1056: // }
1057: // and how it will be XML-ized.
1058: }
1059:
1060: /**
1061: * Returns true if the method is considered 'public'.
1062: */
1063: private boolean isConsideredPublic(M m) {
1064: return m == null || nav().isPublicMethod(m);
1065: }
1066:
1067: /**
1068: * If the method has an explicit annotation, allow it to participate
1069: * to the processing even if it lacks the setter or the getter.
1070: */
1071: private void resurrect(Map<String, M> methods, Set<String> complete) {
1072: for (Map.Entry<String, M> e : methods.entrySet()) {
1073: if (complete.contains(e.getKey()))
1074: continue;
1075: if (hasJAXBAnnotation(reader().getAllMethodAnnotations(
1076: e.getValue(), this )))
1077: complete.add(e.getKey());
1078: }
1079: }
1080:
1081: /**
1082: * Makes sure that the method doesn't have any annotation, if it does,
1083: * report it as an error
1084: */
1085: private void ensureNoAnnotation(M method) {
1086: Annotation[] annotations = reader().getAllMethodAnnotations(
1087: method, this );
1088: for (Annotation a : annotations) {
1089: if (isJAXBAnnotation(a)) {
1090: builder
1091: .reportError(new IllegalAnnotationException(
1092: Messages.ANNOTATION_ON_WRONG_METHOD
1093: .format(), a));
1094: return;
1095: }
1096: }
1097: }
1098:
1099: /**
1100: * Returns true if a given annotation is a JAXB annotation.
1101: */
1102: private static boolean isJAXBAnnotation(Annotation a) {
1103: return ANNOTATION_NUMBER_MAP.containsKey(a.annotationType());
1104: }
1105:
1106: /**
1107: * Returns true if the array contains a JAXB annotation.
1108: */
1109: private static boolean hasJAXBAnnotation(Annotation[] annotations) {
1110: return getSomeJAXBAnnotation(annotations) != null;
1111: }
1112:
1113: private static Annotation getSomeJAXBAnnotation(
1114: Annotation[] annotations) {
1115: for (Annotation a : annotations)
1116: if (isJAXBAnnotation(a))
1117: return a;
1118: return null;
1119: }
1120:
1121: /**
1122: * Returns "Foo" from "getFoo" or "isFoo".
1123: *
1124: * @return null
1125: * if the method name doesn't look like a getter.
1126: */
1127: private static String getPropertyNameFromGetMethod(String name) {
1128: if (name.startsWith("get") && name.length() > 3)
1129: return name.substring(3);
1130: if (name.startsWith("is") && name.length() > 2)
1131: return name.substring(2);
1132: return null;
1133: }
1134:
1135: /**
1136: * Returns "Foo" from "setFoo".
1137: *
1138: * @return null
1139: * if the method name doesn't look like a setter.
1140: */
1141: private static String getPropertyNameFromSetMethod(String name) {
1142: if (name.startsWith("set") && name.length() > 3)
1143: return name.substring(3);
1144: return null;
1145: }
1146:
1147: /**
1148: * Creates a new {@link FieldPropertySeed} object.
1149: *
1150: * <p>
1151: * Derived class can override this method to create a sub-class.
1152: */
1153: protected PropertySeed<T, C, F, M> createFieldSeed(F f) {
1154: return new FieldPropertySeed<T, C, F, M>(this , f);
1155: }
1156:
1157: /**
1158: * Creates a new {@link GetterSetterPropertySeed} object.
1159: */
1160: protected PropertySeed<T, C, F, M> createAccessorSeed(M getter,
1161: M setter) {
1162: return new GetterSetterPropertySeed<T, C, F, M>(this , getter,
1163: setter);
1164: }
1165:
1166: public final boolean isElement() {
1167: return elementName != null;
1168: }
1169:
1170: public boolean isAbstract() {
1171: return nav().isAbstract(clazz);
1172: }
1173:
1174: public boolean isOrdered() {
1175: return propOrder != null;
1176: }
1177:
1178: public final boolean isFinal() {
1179: return nav().isFinal(clazz);
1180: }
1181:
1182: public final boolean hasSubClasses() {
1183: return hasSubClasses;
1184: }
1185:
1186: public final boolean hasAttributeWildcard() {
1187: return declaresAttributeWildcard()
1188: || inheritsAttributeWildcard();
1189: }
1190:
1191: public final boolean inheritsAttributeWildcard() {
1192: return getInheritedAttributeWildcard() != null;
1193: }
1194:
1195: public final boolean declaresAttributeWildcard() {
1196: return attributeWildcard != null;
1197: }
1198:
1199: /**
1200: * Gets the {@link PropertySeed} object for the inherited attribute wildcard.
1201: */
1202: private PropertySeed<T, C, F, M> getInheritedAttributeWildcard() {
1203: for (ClassInfoImpl<T, C, F, M> c = getBaseClass(); c != null; c = c
1204: .getBaseClass())
1205: if (c.attributeWildcard != null)
1206: return c.attributeWildcard;
1207: return null;
1208: }
1209:
1210: public final QName getElementName() {
1211: return elementName;
1212: }
1213:
1214: public final QName getTypeName() {
1215: return typeName;
1216: }
1217:
1218: public final boolean isSimpleType() {
1219: List<? extends PropertyInfo> props = getProperties();
1220: if (props.size() != 1)
1221: return false;
1222: return props.get(0).kind() == PropertyKind.VALUE;
1223: }
1224:
1225: /**
1226: * Called after all the {@link TypeInfo}s are collected into the {@link #owner}.
1227: */
1228: @Override
1229: /*package*/void link() {
1230: getProperties(); // make sure properties!=null
1231:
1232: // property name collision cehck
1233: Map<String, PropertyInfoImpl> names = new HashMap<String, PropertyInfoImpl>();
1234: for (PropertyInfoImpl<T, C, F, M> p : properties) {
1235: p.link();
1236: PropertyInfoImpl old = names.put(p.getName(), p);
1237: if (old != null) {
1238: builder
1239: .reportError(new IllegalAnnotationException(
1240: Messages.PROPERTY_COLLISION.format(p
1241: .getName()), p, old));
1242: }
1243: }
1244: super .link();
1245: }
1246:
1247: public Location getLocation() {
1248: return nav().getClassLocation(clazz);
1249: }
1250:
1251: /**
1252: * XmlType allows specification of factoryClass and
1253: * factoryMethod. There are to be used if no default
1254: * constructor is found.
1255: *
1256: * @return
1257: * true if the factory method was found. False if not.
1258: */
1259: private boolean hasFactoryConstructor(XmlType t) {
1260: if (t == null)
1261: return false;
1262:
1263: String method = t.factoryMethod();
1264: T fClass = reader().getClassValue(t, "factoryClass");
1265: if (method.length() > 0) {
1266: if (fClass.equals(nav().ref(XmlType.DEFAULT.class))) {
1267: fClass = nav().use(clazz);
1268: }
1269: for (M m : nav().getDeclaredMethods(nav().asDecl(fClass))) {
1270: //- Find the zero-arg public static method with the required return type
1271: if (nav().getMethodName(m).equals(method)
1272: && nav().getReturnType(m).equals(
1273: nav().use(clazz))
1274: && nav().getMethodParameters(m).length == 0
1275: && nav().isStaticMethod(m)) {
1276: factoryMethod = m;
1277: break;
1278: }
1279: }
1280: if (factoryMethod == null) {
1281: builder.reportError(new IllegalAnnotationException(
1282: Messages.NO_FACTORY_METHOD.format(nav()
1283: .getClassName(nav().asDecl(fClass)),
1284: method), this ));
1285: }
1286: } else if (!fClass.equals(nav().ref(XmlType.DEFAULT.class))) {
1287: builder.reportError(new IllegalAnnotationException(
1288: Messages.FACTORY_CLASS_NEEDS_FACTORY_METHOD
1289: .format(nav().getClassName(
1290: nav().asDecl(fClass))), this ));
1291: }
1292: return factoryMethod != null;
1293: }
1294:
1295: public Method getFactoryMethod() {
1296: return (Method) factoryMethod;
1297: }
1298:
1299: private static final String[] DEFAULT_ORDER = new String[0];
1300: }
|