src/share/jaxws_classes/com/sun/xml/internal/bind/v2/model/impl/ClassInfoImpl.java

Wed, 27 Apr 2016 01:27:09 +0800

author
aoqi
date
Wed, 27 Apr 2016 01:27:09 +0800
changeset 0
373ffda63c9a
permissions
-rw-r--r--

Initial load
http://hg.openjdk.java.net/jdk8u/jdk8u/jaxws/
changeset: 657:d47a47f961ee
tag: jdk8u25-b17

     1 /*
     2  * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.xml.internal.bind.v2.model.impl;
    28 import java.lang.annotation.Annotation;
    29 import java.lang.reflect.Method;
    30 import java.util.ArrayList;
    31 import java.util.Collection;
    32 import java.util.Collections;
    33 import java.util.Comparator;
    34 import java.util.HashMap;
    35 import java.util.HashSet;
    36 import java.util.LinkedHashMap;
    37 import java.util.List;
    38 import java.util.Map;
    39 import java.util.Set;
    40 import java.util.TreeSet;
    41 import java.util.AbstractList;
    43 import javax.xml.bind.annotation.XmlAccessOrder;
    44 import javax.xml.bind.annotation.XmlAccessType;
    45 import javax.xml.bind.annotation.XmlAccessorOrder;
    46 import javax.xml.bind.annotation.XmlAccessorType;
    47 import javax.xml.bind.annotation.XmlAnyAttribute;
    48 import javax.xml.bind.annotation.XmlAnyElement;
    49 import javax.xml.bind.annotation.XmlAttachmentRef;
    50 import javax.xml.bind.annotation.XmlAttribute;
    51 import javax.xml.bind.annotation.XmlElement;
    52 import javax.xml.bind.annotation.XmlElementRef;
    53 import javax.xml.bind.annotation.XmlElementRefs;
    54 import javax.xml.bind.annotation.XmlElementWrapper;
    55 import javax.xml.bind.annotation.XmlElements;
    56 import javax.xml.bind.annotation.XmlID;
    57 import javax.xml.bind.annotation.XmlIDREF;
    58 import javax.xml.bind.annotation.XmlInlineBinaryData;
    59 import javax.xml.bind.annotation.XmlList;
    60 import javax.xml.bind.annotation.XmlMimeType;
    61 import javax.xml.bind.annotation.XmlMixed;
    62 import javax.xml.bind.annotation.XmlRootElement;
    63 import javax.xml.bind.annotation.XmlSchemaType;
    64 import javax.xml.bind.annotation.XmlTransient;
    65 import javax.xml.bind.annotation.XmlType;
    66 import javax.xml.bind.annotation.XmlValue;
    67 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
    68 import javax.xml.namespace.QName;
    70 import com.sun.istack.internal.FinalArrayList;
    71 import com.sun.xml.internal.bind.annotation.OverrideAnnotationOf;
    72 import com.sun.xml.internal.bind.v2.model.annotation.Locatable;
    73 import com.sun.xml.internal.bind.v2.model.annotation.MethodLocatable;
    74 import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
    75 import com.sun.xml.internal.bind.v2.model.core.Element;
    76 import com.sun.xml.internal.bind.v2.model.core.ID;
    77 import com.sun.xml.internal.bind.v2.model.core.NonElement;
    78 import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
    79 import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
    80 import com.sun.xml.internal.bind.v2.model.core.ValuePropertyInfo;
    81 import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
    82 import com.sun.xml.internal.bind.v2.runtime.Location;
    83 import com.sun.xml.internal.bind.v2.util.EditDistance;
    86 /**
    87  * A part of the {@link ClassInfo} that doesn't depend on a particular
    88  * reflection library.
    89  *
    90  * @author Kohsuke Kawaguchi (kk@kohsuke.org)
    91  */
    92 public class ClassInfoImpl<T,C,F,M> extends TypeInfoImpl<T,C,F,M>
    93     implements ClassInfo<T,C>, Element<T,C> {
    95     protected final C clazz;
    97     /**
    98      * @see #getElementName()
    99      */
   100     private final QName elementName;
   102     /**
   103      * @see #getTypeName()
   104      */
   105     private final QName typeName;
   107     /**
   108      * Lazily created.
   109      *
   110      * @see #getProperties()
   111      */
   112     private FinalArrayList<PropertyInfoImpl<T,C,F,M>> properties;
   114     /**
   115      * The property order.
   116      *
   117      * null if unordered. {@link #DEFAULT_ORDER} if ordered but the order is defaulted
   118      *
   119      * @see #isOrdered()
   120      */
   121     private /*final*/ String[] propOrder;
   123     /**
   124      * Lazily computed.
   125      *
   126      * To avoid the cyclic references of the form C1 --base--> C2 --property--> C1.
   127      */
   128     private ClassInfoImpl<T,C,F,M> baseClass;
   130     private boolean baseClassComputed = false;
   132     private boolean hasSubClasses = false;
   134     /**
   135      * If this class has a declared (not inherited) attribute wildcard,  keep the reference
   136      * to it.
   137      *
   138      * This parameter is initialized at the construction time and never change.
   139      */
   140     protected /*final*/ PropertySeed<T,C,F,M> attributeWildcard;
   143     /**
   144      * @see #getFactoryMethod()
   145      */
   146     private M factoryMethod = null;
   148     ClassInfoImpl(ModelBuilder<T,C,F,M> builder, Locatable upstream, C clazz) {
   149         super(builder,upstream);
   150         this.clazz = clazz;
   151         assert clazz!=null;
   153         // compute the element name
   154         elementName = parseElementName(clazz);
   156         // compute the type name
   157         XmlType t = reader().getClassAnnotation(XmlType.class,clazz,this);
   158         typeName = parseTypeName(clazz,t);
   160         if(t!=null) {
   161             String[] propOrder = t.propOrder();
   162             if(propOrder.length==0)
   163                 this.propOrder = null;   // unordered
   164             else {
   165                 if(propOrder[0].length()==0)
   166                     this.propOrder = DEFAULT_ORDER;
   167                 else
   168                     this.propOrder = propOrder;
   169             }
   170         } else {
   171             propOrder = DEFAULT_ORDER;
   172         }
   174         // obtain XmlAccessorOrder and  set proporder (XmlAccessorOrder can be defined for whole package)
   175         // (<xs:all> vs <xs:sequence>)
   176         XmlAccessorOrder xao = reader().getPackageAnnotation(XmlAccessorOrder.class, clazz, this);
   177         if((xao != null) && (xao.value() == XmlAccessOrder.UNDEFINED)) {
   178             propOrder = null;
   179         }
   181         // obtain XmlAccessorOrder and  set proporder (<xs:all> vs <xs:sequence>)
   182         xao = reader().getClassAnnotation(XmlAccessorOrder.class, clazz, this);
   183         if((xao != null) && (xao.value() == XmlAccessOrder.UNDEFINED)) {
   184             propOrder = null;
   185         }
   187         if(nav().isInterface(clazz)) {
   188             builder.reportError(new IllegalAnnotationException(
   189                 Messages.CANT_HANDLE_INTERFACE.format(nav().getClassName(clazz)), this ));
   190         }
   192         // the class must have the default constructor
   193         if (!hasFactoryConstructor(t)){
   194             if(!nav().hasDefaultConstructor(clazz)){
   195                 if(nav().isInnerClass(clazz)) {
   196                     builder.reportError(new IllegalAnnotationException(
   197                         Messages.CANT_HANDLE_INNER_CLASS.format(nav().getClassName(clazz)), this ));
   198                 } else if (elementName != null) {
   199                     builder.reportError(new IllegalAnnotationException(
   200                         Messages.NO_DEFAULT_CONSTRUCTOR.format(nav().getClassName(clazz)), this ));
   201                 }
   202             }
   203         }
   204     }
   206     public ClassInfoImpl<T,C,F,M> getBaseClass() {
   207         if (!baseClassComputed) {
   208             // compute the base class
   209             C s = nav().getSuperClass(clazz);
   210             if(s==null || s==nav().asDecl(Object.class)) {
   211                 baseClass = null;
   212             } else {
   213                 NonElement<T,C> b = builder.getClassInfo(s, true, this);
   214                 if(b instanceof ClassInfoImpl) {
   215                     baseClass = (ClassInfoImpl<T,C,F,M>) b;
   216                     baseClass.hasSubClasses = true;
   217                 } else {
   218                     baseClass = null;
   219                 }
   220             }
   221             baseClassComputed = true;
   222         }
   223         return baseClass;
   224     }
   226     /**
   227      * {@inheritDoc}
   228      *
   229      * The substitution hierarchy is the same as the inheritance hierarchy.
   230      */
   231     public final Element<T,C> getSubstitutionHead() {
   232         ClassInfoImpl<T,C,F,M> c = getBaseClass();
   233         while(c!=null && !c.isElement())
   234             c = c.getBaseClass();
   235         return c;
   236     }
   238     public final C getClazz() {
   239         return clazz;
   240     }
   242     /**
   243      * When a bean binds to an element, it's always through {@link XmlRootElement},
   244      * so this method always return null.
   245      *
   246      * @deprecated
   247      *      you shouldn't be invoking this method on {@link ClassInfoImpl}.
   248      */
   249     public ClassInfoImpl<T,C,F,M> getScope() {
   250         return null;
   251     }
   253     public final T getType() {
   254         return nav().use(clazz);
   255     }
   257     /**
   258      * A {@link ClassInfo} can be referenced by {@link XmlIDREF} if
   259      * it has an ID property.
   260      */
   261     public boolean canBeReferencedByIDREF() {
   262         for (PropertyInfo<T,C> p : getProperties()) {
   263             if(p.id()== ID.ID)
   264                 return true;
   265         }
   266         ClassInfoImpl<T,C,F,M> base = getBaseClass();
   267         if(base!=null)
   268             return base.canBeReferencedByIDREF();
   269         else
   270             return false;
   271     }
   273     public final String getName() {
   274         return nav().getClassName(clazz);
   275     }
   277     public <A extends Annotation> A readAnnotation(Class<A> a) {
   278         return reader().getClassAnnotation(a,clazz,this);
   279     }
   281     public Element<T,C> asElement() {
   282         if(isElement())
   283             return this;
   284         else
   285             return null;
   286     }
   288     public List<? extends PropertyInfo<T,C>> getProperties() {
   289         if(properties!=null)    return properties;
   291         // check the access type first
   292         XmlAccessType at = getAccessType();
   294         properties = new FinalArrayList<PropertyInfoImpl<T,C,F,M>>();
   296         findFieldProperties(clazz,at);
   298         findGetterSetterProperties(at);
   300         if(propOrder==DEFAULT_ORDER || propOrder==null) {
   301             XmlAccessOrder ao = getAccessorOrder();
   302             if(ao==XmlAccessOrder.ALPHABETICAL)
   303                 Collections.sort(properties);
   304         } else {
   305             //sort them as specified
   306             PropertySorter sorter = new PropertySorter();
   307             for (PropertyInfoImpl p : properties) {
   308                 sorter.checkedGet(p);   // have it check for errors
   309             }
   310             Collections.sort(properties,sorter);
   311             sorter.checkUnusedProperties();
   312         }
   314         {// additional error checks
   315             PropertyInfoImpl vp=null; // existing value property
   316             PropertyInfoImpl ep=null; // existing element property
   318             for (PropertyInfoImpl p : properties) {
   319                 switch(p.kind()) {
   320                 case ELEMENT:
   321                 case REFERENCE:
   322                 case MAP:
   323                     ep = p;
   324                     break;
   325                 case VALUE:
   326                     if(vp!=null) {
   327                         // can't have multiple value properties.
   328                         builder.reportError(new IllegalAnnotationException(
   329                             Messages.MULTIPLE_VALUE_PROPERTY.format(),
   330                             vp, p ));
   331                     }
   332                     if(getBaseClass()!=null) {
   333                         builder.reportError(new IllegalAnnotationException(
   334                             Messages.XMLVALUE_IN_DERIVED_TYPE.format(), p ));
   335                     }
   336                     vp = p;
   337                     break;
   338                 case ATTRIBUTE:
   339                     break;  // noop
   340                 default:
   341                     assert false;
   342                 }
   343             }
   345             if(ep!=null && vp!=null) {
   346                 // can't have element and value property at the same time
   347                 builder.reportError(new IllegalAnnotationException(
   348                     Messages.ELEMENT_AND_VALUE_PROPERTY.format(),
   349                     vp, ep
   350                 ));
   351             }
   352         }
   354         return properties;
   355     }
   357     private void findFieldProperties(C c, XmlAccessType at) {
   359         // always find properties from the super class first
   360         C sc = nav().getSuperClass(c);
   361         if (shouldRecurseSuperClass(sc)) {
   362             findFieldProperties(sc,at);
   363         }
   365         for( F f : nav().getDeclaredFields(c) ) {
   366             Annotation[] annotations = reader().getAllFieldAnnotations(f,this);
   367             boolean isDummy = reader().hasFieldAnnotation(OverrideAnnotationOf.class, f);
   369             if( nav().isTransient(f) ) {
   370                 // it's an error for transient field to have any binding annotation
   371                 if(hasJAXBAnnotation(annotations))
   372                     builder.reportError(new IllegalAnnotationException(
   373                         Messages.TRANSIENT_FIELD_NOT_BINDABLE.format(nav().getFieldName(f)),
   374                             getSomeJAXBAnnotation(annotations)));
   375             } else
   376             if( nav().isStaticField(f) ) {
   377                 // static fields are bound only when there's explicit annotation.
   378                 if(hasJAXBAnnotation(annotations))
   379                     addProperty(createFieldSeed(f),annotations, false);
   380             } else {
   381                 if(at==XmlAccessType.FIELD
   382                 ||(at==XmlAccessType.PUBLIC_MEMBER && nav().isPublicField(f))
   383                 || hasJAXBAnnotation(annotations)) {
   384                     if (isDummy) {
   385                         ClassInfo<T, C> top = getBaseClass();
   386                         while ((top != null) && (top.getProperty("content") == null)) {
   387                             top = top.getBaseClass();
   388                         }
   389                         DummyPropertyInfo prop = (DummyPropertyInfo) top.getProperty("content");
   390                         PropertySeed seed = createFieldSeed(f);
   391                         ((DummyPropertyInfo)prop).addType(createReferenceProperty(seed));
   392                     } else {
   393                         addProperty(createFieldSeed(f), annotations, false);
   394                     }
   395                 }
   396                 checkFieldXmlLocation(f);
   397             }
   398         }
   399     }
   401     public final boolean hasValueProperty() {
   402         ClassInfoImpl<T, C, F, M> bc = getBaseClass();
   403         if(bc!=null && bc.hasValueProperty())
   404             return true;
   406         for (PropertyInfo p : getProperties()) {
   407             if (p instanceof ValuePropertyInfo) return true;
   408         }
   410         return false;
   411         }
   413     public PropertyInfo<T,C> getProperty(String name) {
   414         for( PropertyInfo<T,C> p: getProperties() ) {
   415             if(p.getName().equals(name))
   416                 return p;
   417         }
   418         return null;
   419     }
   421     /**
   422      * This hook is used by {@link RuntimeClassInfoImpl} to look for {@link XmlLocation}.
   423      */
   424     protected void checkFieldXmlLocation(F f) {
   425     }
   427     /**
   428      * Gets an annotation that are allowed on both class and type.
   429      */
   430     private <T extends Annotation> T getClassOrPackageAnnotation(Class<T> type) {
   431         T t = reader().getClassAnnotation(type,clazz,this);
   432         if(t!=null)
   433             return t;
   434         // defaults to the package level
   435         return reader().getPackageAnnotation(type,clazz,this);
   436     }
   438     /**
   439      * Computes the {@link XmlAccessType} on this class by looking at {@link XmlAccessorType}
   440      * annotations.
   441      */
   442     private XmlAccessType getAccessType() {
   443         XmlAccessorType xat = getClassOrPackageAnnotation(XmlAccessorType.class);
   444         if(xat!=null)
   445             return xat.value();
   446         else
   447             return XmlAccessType.PUBLIC_MEMBER;
   448     }
   450     /**
   451      * Gets the accessor order for this class by consulting {@link XmlAccessorOrder}.
   452      */
   453     private XmlAccessOrder getAccessorOrder() {
   454         XmlAccessorOrder xao = getClassOrPackageAnnotation(XmlAccessorOrder.class);
   455         if(xao!=null)
   456             return xao.value();
   457         else
   458             return XmlAccessOrder.UNDEFINED;
   459     }
   461     /**
   462      * Compares orders among {@link PropertyInfoImpl} according to {@link ClassInfoImpl#propOrder}.
   463      *
   464      * <p>
   465      * extends {@link HashMap} to save memory.
   466      */
   467     private final class PropertySorter extends HashMap<String,Integer> implements Comparator<PropertyInfoImpl> {
   468         /**
   469          * Mark property names that are used, so that we can report unused property names in the propOrder array.
   470          */
   471         PropertyInfoImpl[] used = new PropertyInfoImpl[propOrder.length];
   473         /**
   474          * If any name collides, it will be added to this set.
   475          * This is used to avoid repeating the same error message.
   476          */
   477         private Set<String> collidedNames;
   479         PropertySorter() {
   480             super(propOrder.length);
   481             for( String name : propOrder )
   482                 if(put(name,size())!=null) {
   483                     // two properties with the same name
   484                     builder.reportError(new IllegalAnnotationException(
   485                         Messages.DUPLICATE_ENTRY_IN_PROP_ORDER.format(name),ClassInfoImpl.this));
   486                 }
   487         }
   489         public int compare(PropertyInfoImpl o1, PropertyInfoImpl o2) {
   490             int lhs = checkedGet(o1);
   491             int rhs = checkedGet(o2);
   493             return lhs-rhs;
   494         }
   496         private int checkedGet(PropertyInfoImpl p) {
   497             Integer i = get(p.getName());
   498             if(i==null) {
   499                 // missing
   500                 if (p.kind().isOrdered)
   501                     builder.reportError(new IllegalAnnotationException(
   502                         Messages.PROPERTY_MISSING_FROM_ORDER.format(p.getName()),p));
   504                 // give it an order to recover from an error
   505                 i = size();
   506                 put(p.getName(),i);
   507             }
   509             // mark the used field
   510             int ii = i;
   511             if(ii<used.length) {
   512                 if(used[ii]!=null && used[ii]!=p) {
   513                     if(collidedNames==null) collidedNames = new HashSet<String>();
   515                     if(collidedNames.add(p.getName()))
   516                         // report the error only on the first time
   517                         builder.reportError(new IllegalAnnotationException(
   518                             Messages.DUPLICATE_PROPERTIES.format(p.getName()),p,used[ii]));
   519                 }
   520                 used[ii] = p;
   521             }
   523             return i;
   524         }
   526         /**
   527          * Report errors for unused propOrder entries.
   528          */
   529         public void checkUnusedProperties() {
   530             for( int i=0; i<used.length; i++ )
   531                 if(used[i]==null) {
   532                     String unusedName = propOrder[i];
   533                     String nearest = EditDistance.findNearest(unusedName, new AbstractList<String>() {
   534                         public String get(int index) {
   535                             return properties.get(index).getName();
   536                         }
   538                         public int size() {
   539                             return properties.size();
   540                         }
   541                     });
   542                     boolean isOverriding = (i > (properties.size()-1)) ? false : properties.get(i).hasAnnotation(OverrideAnnotationOf.class);
   543                     if (!isOverriding) {
   544                         builder.reportError(new IllegalAnnotationException(
   545                         Messages.PROPERTY_ORDER_CONTAINS_UNUSED_ENTRY.format(unusedName,nearest),ClassInfoImpl.this));
   546                     }
   547                 }
   548         }
   549     }
   551     public boolean hasProperties() {
   552         return !properties.isEmpty();
   553     }
   556     /**
   557      * Picks the first non-null argument, or null if all arguments are null.
   558      */
   559     private static <T> T pickOne( T... args ) {
   560         for( T arg : args )
   561             if(arg!=null)
   562                 return arg;
   563         return null;
   564     }
   566     private static <T> List<T> makeSet( T... args ) {
   567         List<T> l = new FinalArrayList<T>();
   568         for( T arg : args )
   569             if(arg!=null)   l.add(arg);
   570         return l;
   571     }
   573     private static final class ConflictException extends Exception {
   574         final List<Annotation> annotations;
   576         public ConflictException(List<Annotation> one) {
   577             this.annotations = one;
   578         }
   579     }
   581     private static final class DuplicateException extends Exception {
   582         final Annotation a1,a2;
   583         public DuplicateException(Annotation a1, Annotation a2) {
   584             this.a1 = a1;
   585             this.a2 = a2;
   586         }
   587     }
   589     /**
   590      * Represents 6 groups of secondary annotations
   591      */
   592     private static enum SecondaryAnnotation {
   593         JAVA_TYPE       (0x01, XmlJavaTypeAdapter.class),
   594         ID_IDREF        (0x02, XmlID.class, XmlIDREF.class),
   595         BINARY          (0x04, XmlInlineBinaryData.class, XmlMimeType.class, XmlAttachmentRef.class),
   596         ELEMENT_WRAPPER (0x08, XmlElementWrapper.class),
   597         LIST            (0x10, XmlList.class),
   598         SCHEMA_TYPE     (0x20, XmlSchemaType.class);
   600         /**
   601          * Each constant gets an unique bit mask so that the presence/absence
   602          * of them can be represented in a single byte.
   603          */
   604         final int bitMask;
   605         /**
   606          * List of annotations that belong to this member.
   607          */
   608         final Class<? extends Annotation>[] members;
   610         SecondaryAnnotation(int bitMask, Class<? extends Annotation>... members) {
   611             this.bitMask = bitMask;
   612             this.members = members;
   613         }
   614     }
   616     private static final SecondaryAnnotation[] SECONDARY_ANNOTATIONS = SecondaryAnnotation.values();
   618     /**
   619      * Represents 7 groups of properties.
   620      *
   621      * Each instance is also responsible for rejecting annotations
   622      * that are not allowed on that kind.
   623      */
   624     private static enum PropertyGroup {
   625         TRANSIENT       (false,false,false,false,false,false),
   626         ANY_ATTRIBUTE   (true, false,false,false,false,false),
   627         ATTRIBUTE       (true, true, true, false,true, true ),
   628         VALUE           (true, true, true, false,true, true ),
   629         ELEMENT         (true, true, true, true, true, true ),
   630         ELEMENT_REF     (true, false,false,true, false,false),
   631         MAP             (false,false,false,true, false,false);
   633         /**
   634          * Bit mask that represents secondary annotations that are allowed on this group.
   635          *
   636          * T = not allowed, F = allowed
   637          */
   638         final int allowedsecondaryAnnotations;
   640         PropertyGroup(boolean... bits) {
   641             int mask = 0;
   642             assert bits.length==SECONDARY_ANNOTATIONS.length;
   643             for( int i=0; i<bits.length; i++ ) {
   644                 if(bits[i])
   645                     mask |= SECONDARY_ANNOTATIONS[i].bitMask;
   646             }
   647             allowedsecondaryAnnotations = ~mask;
   648         }
   650         boolean allows(SecondaryAnnotation a) {
   651             return (allowedsecondaryAnnotations&a.bitMask)==0;
   652         }
   653     }
   655     private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
   657     /**
   658      * All the annotations in JAXB to their internal index.
   659      */
   660     private static final HashMap<Class,Integer> ANNOTATION_NUMBER_MAP = new HashMap<Class,Integer>();
   661     static {
   662         Class[] annotations = {
   663             XmlTransient.class,     // 0
   664             XmlAnyAttribute.class,  // 1
   665             XmlAttribute.class,     // 2
   666             XmlValue.class,         // 3
   667             XmlElement.class,       // 4
   668             XmlElements.class,      // 5
   669             XmlElementRef.class,    // 6
   670             XmlElementRefs.class,   // 7
   671             XmlAnyElement.class,    // 8
   672             XmlMixed.class,         // 9
   673             OverrideAnnotationOf.class,// 10
   674         };
   676         HashMap<Class,Integer> m = ANNOTATION_NUMBER_MAP;
   678         // characterizing annotations
   679         for( Class c : annotations )
   680             m.put(c, m.size() );
   682         // secondary annotations
   683         int index = 20;
   684         for( SecondaryAnnotation sa : SECONDARY_ANNOTATIONS ) {
   685             for( Class member : sa.members )
   686                 m.put(member,index);
   687             index++;
   688         }
   689     }
   691     private void checkConflict(Annotation a, Annotation b) throws DuplicateException {
   692         assert b!=null;
   693         if(a!=null)
   694             throw new DuplicateException(a,b);
   695     }
   697     /**
   698      * Called only from {@link #getProperties()}.
   699      *
   700      * <p>
   701      * This is where we decide the type of the property and checks for annotations
   702      * that are not allowed.
   703      *
   704      * @param annotations
   705      *      all annotations on this property. It's the same as
   706      *      {@code seed.readAllAnnotation()}, but taken as a parameter
   707      *      because the caller should know it already.
   708      */
   709     private void addProperty( PropertySeed<T,C,F,M> seed, Annotation[] annotations, boolean dummy ) {
   710         // since typically there's a very few annotations on a method,
   711         // this runs faster than checking for each annotation via readAnnotation(A)
   714         // characterizing annotations. these annotations (or lack thereof) decides
   715         // the kind of the property it goes to.
   716         // I wish I could use an array...
   717         XmlTransient t = null;
   718         XmlAnyAttribute aa = null;
   719         XmlAttribute a = null;
   720         XmlValue v = null;
   721         XmlElement e1 = null;
   722         XmlElements e2 = null;
   723         XmlElementRef r1 = null;
   724         XmlElementRefs r2 = null;
   725         XmlAnyElement xae = null;
   726         XmlMixed mx = null;
   727         OverrideAnnotationOf ov = null;
   729         // encountered secondary annotations are accumulated into a bit mask
   730         int secondaryAnnotations = 0;
   732         try {
   733             for( Annotation ann : annotations ) {
   734                 Integer index = ANNOTATION_NUMBER_MAP.get(ann.annotationType());
   735                 if(index==null) continue;
   736                 switch(index) {
   737                 case 0:     checkConflict(t  ,ann); t   = (XmlTransient) ann; break;
   738                 case 1:     checkConflict(aa ,ann); aa  = (XmlAnyAttribute) ann; break;
   739                 case 2:     checkConflict(a  ,ann); a   = (XmlAttribute) ann; break;
   740                 case 3:     checkConflict(v  ,ann); v   = (XmlValue) ann; break;
   741                 case 4:     checkConflict(e1 ,ann); e1  = (XmlElement) ann; break;
   742                 case 5:     checkConflict(e2 ,ann); e2  = (XmlElements) ann; break;
   743                 case 6:     checkConflict(r1 ,ann); r1  = (XmlElementRef) ann; break;
   744                 case 7:     checkConflict(r2 ,ann); r2  = (XmlElementRefs) ann; break;
   745                 case 8:     checkConflict(xae,ann); xae = (XmlAnyElement) ann; break;
   746                 case 9:     checkConflict(mx, ann); mx  = (XmlMixed) ann; break;
   747                 case 10:    checkConflict(ov, ann); ov  = (OverrideAnnotationOf) ann; break;
   748                 default:
   749                     // secondary annotations
   750                     secondaryAnnotations |= (1<<(index-20));
   751                     break;
   752                 }
   753             }
   755             // determine the group kind, and also count the numbers, since
   756             // characterizing annotations are mutually exclusive.
   757             PropertyGroup group = null;
   758             int groupCount = 0;
   760             if(t!=null) {
   761                 group = PropertyGroup.TRANSIENT;
   762                 groupCount++;
   763             }
   764             if(aa!=null) {
   765                 group = PropertyGroup.ANY_ATTRIBUTE;
   766                 groupCount++;
   767             }
   768             if(a!=null) {
   769                 group = PropertyGroup.ATTRIBUTE;
   770                 groupCount++;
   771             }
   772             if(v!=null) {
   773                 group = PropertyGroup.VALUE;
   774                 groupCount++;
   775             }
   776             if(e1!=null || e2!=null) {
   777                 group = PropertyGroup.ELEMENT;
   778                 groupCount++;
   779             }
   780             if(r1!=null || r2!=null || xae!=null || mx!=null || ov != null) {
   781                 group = PropertyGroup.ELEMENT_REF;
   782                 groupCount++;
   783             }
   785             if(groupCount>1) {
   786                 // collision between groups
   787                 List<Annotation> err = makeSet(t,aa,a,v,pickOne(e1,e2),pickOne(r1,r2,xae));
   788                 throw new ConflictException(err);
   789             }
   791             if(group==null) {
   792                 // if no characterizing annotation was found, it's either element or map
   793                 // sniff the signature and then decide.
   794                 assert groupCount==0;
   796                 // UGLY: the presence of XmlJavaTypeAdapter makes it an element property. ARGH.
   797                 if(nav().isSubClassOf( seed.getRawType(), nav().ref(Map.class) )
   798                 && !seed.hasAnnotation(XmlJavaTypeAdapter.class))
   799                     group = PropertyGroup.MAP;
   800                 else
   801                     group = PropertyGroup.ELEMENT;
   802             } else if (group.equals(PropertyGroup.ELEMENT)) { // see issue 791 - make sure @XmlElement annotated map property is mapped to map
   803                 if (nav().isSubClassOf( seed.getRawType(), nav().ref(Map.class)) && !seed.hasAnnotation(XmlJavaTypeAdapter.class)) {
   804                     group = PropertyGroup.MAP;
   805                 }
   806             }
   808             // group determined by now
   809             // make sure that there are no prohibited secondary annotations
   810             if( (secondaryAnnotations&group.allowedsecondaryAnnotations)!=0 ) {
   811                 // uh oh. find the offending annotation
   812                 for( SecondaryAnnotation sa : SECONDARY_ANNOTATIONS ) {
   813                     if(group.allows(sa))
   814                         continue;
   815                     for( Class<? extends Annotation> m : sa.members ) {
   816                         Annotation offender = seed.readAnnotation(m);
   817                         if(offender!=null) {
   818                             // found it
   819                             builder.reportError(new IllegalAnnotationException(
   820                                 Messages.ANNOTATION_NOT_ALLOWED.format(m.getSimpleName()),offender));
   821                             return;
   822                         }
   823                     }
   824                 }
   825                 // there must have been an offender
   826                 assert false;
   827             }
   829             // actually create annotations
   830             switch(group) {
   831             case TRANSIENT:
   832                 return;
   833             case ANY_ATTRIBUTE:
   834                 // an attribute wildcard property
   835                 if(attributeWildcard!=null) {
   836                     builder.reportError(new IllegalAnnotationException(
   837                         Messages.TWO_ATTRIBUTE_WILDCARDS.format(
   838                             nav().getClassName(getClazz())),aa,attributeWildcard));
   839                     return; // recover by ignore
   840                 }
   841                 attributeWildcard = seed;
   843                 if(inheritsAttributeWildcard()) {
   844                     builder.reportError(new IllegalAnnotationException(
   845                         Messages.SUPER_CLASS_HAS_WILDCARD.format(),
   846                             aa,getInheritedAttributeWildcard()));
   847                     return;
   848                 }
   850                 // check the signature and make sure it's assignable to Map
   851                 if(!nav().isSubClassOf(seed.getRawType(),nav().ref(Map.class))) {
   852                     builder.reportError(new IllegalAnnotationException(
   853                         Messages.INVALID_ATTRIBUTE_WILDCARD_TYPE.format(nav().getTypeName(seed.getRawType())),
   854                             aa,getInheritedAttributeWildcard()));
   855                     return;
   856                 }
   859                 return;
   860             case ATTRIBUTE:
   861                 properties.add(createAttributeProperty(seed));
   862                 return;
   863             case VALUE:
   864                 properties.add(createValueProperty(seed));
   865                 return;
   866             case ELEMENT:
   867                 properties.add(createElementProperty(seed));
   868                 return;
   869             case ELEMENT_REF:
   870                 properties.add(createReferenceProperty(seed));
   871                 return;
   872             case MAP:
   873                 properties.add(createMapProperty(seed));
   874                 return;
   875             default:
   876                 assert false;
   877             }
   878         } catch( ConflictException x ) {
   879             // report a conflicting annotation
   880             List<Annotation> err = x.annotations;
   882             builder.reportError(new IllegalAnnotationException(
   883                 Messages.MUTUALLY_EXCLUSIVE_ANNOTATIONS.format(
   884                     nav().getClassName(getClazz())+'#'+seed.getName(),
   885                     err.get(0).annotationType().getName(), err.get(1).annotationType().getName()),
   886                     err.get(0), err.get(1) ));
   888             // recover by ignoring this property
   889         } catch( DuplicateException e ) {
   890             // both are present
   891             builder.reportError(new IllegalAnnotationException(
   892                 Messages.DUPLICATE_ANNOTATIONS.format(e.a1.annotationType().getName()),
   893                 e.a1, e.a2 ));
   894             // recover by ignoring this property
   896         }
   897     }
   899     protected ReferencePropertyInfoImpl<T,C,F,M> createReferenceProperty(PropertySeed<T,C,F,M> seed) {
   900         return new ReferencePropertyInfoImpl<T,C,F,M>(this,seed);
   901     }
   903     protected AttributePropertyInfoImpl<T,C,F,M> createAttributeProperty(PropertySeed<T,C,F,M> seed) {
   904         return new AttributePropertyInfoImpl<T,C,F,M>(this,seed);
   905     }
   907     protected ValuePropertyInfoImpl<T,C,F,M> createValueProperty(PropertySeed<T,C,F,M> seed) {
   908         return new ValuePropertyInfoImpl<T,C,F,M>(this,seed);
   909     }
   911     protected ElementPropertyInfoImpl<T,C,F,M> createElementProperty(PropertySeed<T,C,F,M> seed) {
   912         return new ElementPropertyInfoImpl<T,C,F,M>(this,seed);
   913     }
   915     protected MapPropertyInfoImpl<T,C,F,M> createMapProperty(PropertySeed<T,C,F,M> seed) {
   916         return new MapPropertyInfoImpl<T,C,F,M>(this,seed);
   917     }
   920     /**
   921      * Adds properties that consists of accessors.
   922      */
   923     private void findGetterSetterProperties(XmlAccessType at) {
   924         // in the first step we accumulate getters and setters
   925         // into this map keyed by the property name.
   926         Map<String,M> getters = new LinkedHashMap<String,M>();
   927         Map<String,M> setters = new LinkedHashMap<String,M>();
   929         C c = clazz;
   930         do {
   931             collectGetterSetters(clazz, getters, setters);
   933             // take super classes into account if they have @XmlTransient
   934             c = nav().getSuperClass(c);
   935         } while(shouldRecurseSuperClass(c));
   938         // compute the intersection
   939         Set<String> complete = new TreeSet<String>(getters.keySet());
   940         complete.retainAll(setters.keySet());
   942         resurrect(getters, complete);
   943         resurrect(setters, complete);
   945         // then look for read/write properties.
   946         for (String name : complete) {
   947             M getter = getters.get(name);
   948             M setter = setters.get(name);
   950             Annotation[] ga = getter!=null ? reader().getAllMethodAnnotations(getter,new MethodLocatable<M>(this,getter,nav())) : EMPTY_ANNOTATIONS;
   951             Annotation[] sa = setter!=null ? reader().getAllMethodAnnotations(setter,new MethodLocatable<M>(this,setter,nav())) : EMPTY_ANNOTATIONS;
   953             boolean hasAnnotation = hasJAXBAnnotation(ga) || hasJAXBAnnotation(sa);
   954             boolean isOverriding = false;
   955             if(!hasAnnotation) {
   956                 // checking if the method is overriding others isn't free,
   957                 // so we don't compute it if it's not necessary.
   958                 isOverriding = (getter!=null && nav().isOverriding(getter,c))
   959                             && (setter!=null && nav().isOverriding(setter,c));
   960             }
   962             if((at==XmlAccessType.PROPERTY && !isOverriding)
   963                 || (at==XmlAccessType.PUBLIC_MEMBER && isConsideredPublic(getter) && isConsideredPublic(setter) && !isOverriding)
   964             || hasAnnotation) {
   965                 // make sure that the type is consistent
   966                 if(getter!=null && setter!=null
   967                 && !nav().isSameType(nav().getReturnType(getter), nav().getMethodParameters(setter)[0])) {
   968                     // inconsistent
   969                     builder.reportError(new IllegalAnnotationException(
   970                         Messages.GETTER_SETTER_INCOMPATIBLE_TYPE.format(
   971                             nav().getTypeName(nav().getReturnType(getter)),
   972                             nav().getTypeName(nav().getMethodParameters(setter)[0])
   973                         ),
   974                         new MethodLocatable<M>( this, getter, nav()),
   975                         new MethodLocatable<M>( this, setter, nav())));
   976                     continue;
   977                 }
   979                 // merge annotations from two list
   980                 Annotation[] r;
   981                 if(ga.length==0) {
   982                     r = sa;
   983                 } else
   984                 if(sa.length==0) {
   985                     r = ga;
   986                 } else {
   987                     r = new Annotation[ga.length+sa.length];
   988                     System.arraycopy(ga,0,r,0,ga.length);
   989                     System.arraycopy(sa,0,r,ga.length,sa.length);
   990                 }
   992                 addProperty(createAccessorSeed(getter, setter), r, false);
   993             }
   994         }
   995         // done with complete pairs
   996         getters.keySet().removeAll(complete);
   997         setters.keySet().removeAll(complete);
   999         // TODO: think about
  1000         // class Foo {
  1001         //   int getFoo();
  1002         // }
  1003         // class Bar extends Foo {
  1004         //   void setFoo(int x);
  1005         // }
  1006         // and how it will be XML-ized.
  1009     private void collectGetterSetters(C c, Map<String,M> getters, Map<String,M> setters) {
  1010         // take super classes into account if they have @XmlTransient.
  1011         // always visit them first so that
  1012         //   1) order is right
  1013         //   2) overriden properties are handled accordingly
  1014         C sc = nav().getSuperClass(c);
  1015         if(shouldRecurseSuperClass(sc))
  1016             collectGetterSetters(sc,getters,setters);
  1018         Collection<? extends M> methods = nav().getDeclaredMethods(c);
  1019         Map<String,List<M>> allSetters = new LinkedHashMap<String,List<M>>();
  1020         for( M method : methods ) {
  1021             boolean used = false;   // if this method is added to getters or setters
  1023             if(nav().isBridgeMethod(method))
  1024                 continue;   // ignore
  1026             String name = nav().getMethodName(method);
  1027             int arity = nav().getMethodParameters(method).length;
  1029             if(nav().isStaticMethod(method)) {
  1030                 ensureNoAnnotation(method);
  1031                 continue;
  1034             // is this a get method?
  1035             String propName = getPropertyNameFromGetMethod(name);
  1036             if(propName!=null && arity==0) {
  1037                     getters.put(propName,method);
  1038                 used = true;
  1041             // is this a set method?
  1042             propName = getPropertyNameFromSetMethod(name);
  1043             if(propName!=null && arity==1) {
  1044                     List<M> propSetters = allSetters.get(propName);
  1045                     if(null == propSetters){
  1046                         propSetters = new ArrayList<M>();
  1047                         allSetters.put(propName, propSetters);
  1049                     propSetters.add(method);
  1050                 used = true; // used check performed later
  1053             if(!used)
  1054                 ensureNoAnnotation(method);
  1057         // Match getter with setters by comparing getter return type to setter param
  1058         for (Map.Entry<String,M> entry : getters.entrySet()) {
  1059             String propName = entry.getKey();
  1060             M getter = entry.getValue();
  1061             List<M> propSetters = allSetters.remove(propName);
  1062             if (null == propSetters) {
  1063                 //no matching setter
  1064                 continue;
  1066             T getterType = nav().getReturnType(getter);
  1067             for (M setter : propSetters) {
  1068                 T setterType = nav().getMethodParameters(setter)[0];
  1069                 if (nav().isSameType(setterType, getterType)) {
  1070                     setters.put(propName, setter);
  1071                     break;
  1076         // also allow set-only properties
  1077         for (Map.Entry<String,List<M>> e : allSetters.entrySet()) {
  1078             setters.put(e.getKey(),e.getValue().get(0));
  1082     /**
  1083      * Checks if the properties in this given super class should be aggregated into this class.
  1084      */
  1085     private boolean shouldRecurseSuperClass(C sc) {
  1086         return sc!=null
  1087             && (builder.isReplaced(sc) || reader().hasClassAnnotation(sc, XmlTransient.class));
  1090     /**
  1091      * Returns true if the method is considered 'public'.
  1092      */
  1093     private boolean isConsideredPublic(M m) {
  1094         return m ==null || nav().isPublicMethod(m);
  1097     /**
  1098      * If the method has an explicit annotation, allow it to participate
  1099      * to the processing even if it lacks the setter or the getter.
  1100      */
  1101     private void resurrect(Map<String, M> methods, Set<String> complete) {
  1102         for (Map.Entry<String, M> e : methods.entrySet()) {
  1103             if(complete.contains(e.getKey()))
  1104                 continue;
  1105             if(hasJAXBAnnotation(reader().getAllMethodAnnotations(e.getValue(),this)))
  1106                 complete.add(e.getKey());
  1110     /**
  1111      * Makes sure that the method doesn't have any annotation, if it does,
  1112      * report it as an error
  1113      */
  1114     private void ensureNoAnnotation(M method) {
  1115         Annotation[] annotations = reader().getAllMethodAnnotations(method,this);
  1116         for( Annotation a : annotations ) {
  1117             if(isJAXBAnnotation(a)) {
  1118                 builder.reportError(new IllegalAnnotationException(
  1119                     Messages.ANNOTATION_ON_WRONG_METHOD.format(),
  1120                     a));
  1121                 return;
  1126     /**
  1127      * Returns true if a given annotation is a JAXB annotation.
  1128      */
  1129     private static boolean isJAXBAnnotation(Annotation a) {
  1130         return ANNOTATION_NUMBER_MAP.containsKey(a.annotationType());
  1133     /**
  1134      * Returns true if the array contains a JAXB annotation.
  1135      */
  1136     private static boolean hasJAXBAnnotation(Annotation[] annotations) {
  1137         return getSomeJAXBAnnotation(annotations)!=null;
  1140     private static Annotation getSomeJAXBAnnotation(Annotation[] annotations) {
  1141         for( Annotation a : annotations )
  1142             if(isJAXBAnnotation(a))
  1143                 return a;
  1144         return null;
  1148     /**
  1149      * Returns "Foo" from "getFoo" or "isFoo".
  1151      * @return null
  1152      *      if the method name doesn't look like a getter.
  1153      */
  1154     private static String getPropertyNameFromGetMethod(String name) {
  1155         if(name.startsWith("get") && name.length()>3)
  1156             return name.substring(3);
  1157         if(name.startsWith("is") && name.length()>2)
  1158             return name.substring(2);
  1159         return null;
  1162     /**
  1163      * Returns "Foo" from "setFoo".
  1165      * @return null
  1166      *      if the method name doesn't look like a setter.
  1167      */
  1168     private static String getPropertyNameFromSetMethod(String name) {
  1169         if(name.startsWith("set") && name.length()>3)
  1170             return name.substring(3);
  1171         return null;
  1174     /**
  1175      * Creates a new {@link FieldPropertySeed} object.
  1177      * <p>
  1178      * Derived class can override this method to create a sub-class.
  1179      */
  1180     protected PropertySeed<T,C,F,M> createFieldSeed(F f) {
  1181         return new FieldPropertySeed<T,C,F,M>(this, f);
  1184     /**
  1185      * Creates a new {@link GetterSetterPropertySeed} object.
  1186      */
  1187     protected PropertySeed<T,C,F,M> createAccessorSeed(M getter, M setter) {
  1188         return new GetterSetterPropertySeed<T,C,F,M>(this, getter,setter);
  1191     public final boolean isElement() {
  1192         return elementName!=null;
  1195     public boolean isAbstract() {
  1196         return nav().isAbstract(clazz);
  1199     public boolean isOrdered() {
  1200         return propOrder!=null;
  1203     public final boolean isFinal() {
  1204         return nav().isFinal(clazz);
  1207     public final boolean hasSubClasses() {
  1208         return hasSubClasses;
  1211     public final boolean hasAttributeWildcard() {
  1212         return declaresAttributeWildcard() || inheritsAttributeWildcard();
  1215     public final boolean inheritsAttributeWildcard() {
  1216         return getInheritedAttributeWildcard()!=null;
  1219     public final boolean declaresAttributeWildcard() {
  1220         return attributeWildcard!=null;
  1223     /**
  1224      * Gets the {@link PropertySeed} object for the inherited attribute wildcard.
  1225      */
  1226     private PropertySeed<T,C,F,M> getInheritedAttributeWildcard() {
  1227         for( ClassInfoImpl<T,C,F,M> c=getBaseClass(); c!=null; c=c.getBaseClass() )
  1228             if(c.attributeWildcard!=null)
  1229                 return c.attributeWildcard;
  1230         return null;
  1233     public final QName getElementName() {
  1234         return elementName;
  1237     public final QName getTypeName() {
  1238         return typeName;
  1241     public final boolean isSimpleType() {
  1242         List<? extends PropertyInfo> props = getProperties();
  1243         if(props.size()!=1)     return false;
  1244         return props.get(0).kind()==PropertyKind.VALUE;
  1247     /**
  1248      * Called after all the {@link TypeInfo}s are collected into the {@link #owner}.
  1249      */
  1250     @Override
  1251     /*package*/ void link() {
  1252         getProperties();    // make sure properties!=null
  1254         // property name collision cehck
  1255         Map<String,PropertyInfoImpl> names = new HashMap<String,PropertyInfoImpl>();
  1256         for( PropertyInfoImpl<T,C,F,M> p : properties ) {
  1257             p.link();
  1258             PropertyInfoImpl old = names.put(p.getName(),p);
  1259             if(old!=null) {
  1260                 builder.reportError(new IllegalAnnotationException(
  1261                     Messages.PROPERTY_COLLISION.format(p.getName()),
  1262                     p, old ));
  1265         super.link();
  1268     public Location getLocation() {
  1269         return nav().getClassLocation(clazz);
  1272     /**
  1273      *  XmlType allows specification of factoryClass and
  1274      *  factoryMethod.  There are to be used if no default
  1275      *  constructor is found.
  1277      * @return
  1278      *      true if the factory method was found. False if not.
  1279      */
  1280     private  boolean hasFactoryConstructor(XmlType t){
  1281         if (t == null) return false;
  1283         String method = t.factoryMethod();
  1284         T fClass = reader().getClassValue(t, "factoryClass");
  1285         if (method.length() > 0){
  1286             if(nav().isSameType(fClass, nav().ref(XmlType.DEFAULT.class))){
  1287                 fClass = nav().use(clazz);
  1289             for(M m: nav().getDeclaredMethods(nav().asDecl(fClass))){
  1290                 //- Find the zero-arg public static method with the required return type
  1291                 if (nav().getMethodName(m).equals(method) &&
  1292                     nav().isSameType(nav().getReturnType(m), nav().use(clazz)) &&
  1293                     nav().getMethodParameters(m).length == 0 &&
  1294                     nav().isStaticMethod(m)){
  1295                     factoryMethod = m;
  1296                     break;
  1299             if (factoryMethod == null){
  1300                 builder.reportError(new IllegalAnnotationException(
  1301                 Messages.NO_FACTORY_METHOD.format(nav().getClassName(nav().asDecl(fClass)), method), this ));
  1303         } else if(!nav().isSameType(fClass, nav().ref(XmlType.DEFAULT.class))){
  1304             builder.reportError(new IllegalAnnotationException(
  1305                 Messages.FACTORY_CLASS_NEEDS_FACTORY_METHOD.format(nav().getClassName(nav().asDecl(fClass))), this ));
  1307         return factoryMethod != null;
  1310     public Method getFactoryMethod(){
  1311         return (Method) factoryMethod;
  1314     @Override
  1315     public String toString() {
  1316         return "ClassInfo("+clazz+')';
  1319     private static final String[] DEFAULT_ORDER = new String[0];

mercurial