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