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.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;
  84 
  85 
  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> {
  94 
  95     protected final C clazz;
  96 
  97     /**
  98      * @see #getElementName()
  99      */
 100     private final QName elementName;
 101 
 102     /**
 103      * @see #getTypeName()
 104      */
 105     private final QName typeName;
 106 
 107     /**
 108      * Lazily created.
 109      *
 110      * @see #getProperties()
 111      */
 112     private FinalArrayList<PropertyInfoImpl<T,C,F,M>> properties;
 113 
 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;
 122 
 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;
 129 
 130     private boolean baseClassComputed = false;
 131 
 132     private boolean hasSubClasses = false;
 133 
 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;
 141 
 142 
 143     /**
 144      * @see #getFactoryMethod()
 145      */
 146     private M factoryMethod = null;
 147 
 148     ClassInfoImpl(ModelBuilder<T,C,F,M> builder, Locatable upstream, C clazz) {
 149         super(builder,upstream);
 150         this.clazz = clazz;
 151         assert clazz!=null;
 152 
 153         // compute the element name
 154         elementName = parseElementName(clazz);
 155 
 156         // compute the type name
 157         XmlType t = reader().getClassAnnotation(XmlType.class,clazz,this);
 158         typeName = parseTypeName(clazz,t);
 159 
 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         }
 173 
 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         }
 180 
 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         }
 186 
 187         if(nav().isInterface(clazz)) {
 188             builder.reportError(new IllegalAnnotationException(
 189                 Messages.CANT_HANDLE_INTERFACE.format(nav().getClassName(clazz)), this ));
 190         }
 191 
 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     }
 205 
 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     }
 225 
 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     }
 237 
 238     public final C getClazz() {
 239         return clazz;
 240     }
 241 
 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     }
 252 
 253     public final T getType() {
 254         return nav().use(clazz);
 255     }
 256 
 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     }
 272 
 273     public final String getName() {
 274         return nav().getClassName(clazz);
 275     }
 276 
 277     public <A extends Annotation> A readAnnotation(Class<A> a) {
 278         return reader().getClassAnnotation(a,clazz,this);
 279     }
 280 
 281     public Element<T,C> asElement() {
 282         if(isElement())
 283             return this;
 284         else
 285             return null;
 286     }
 287 
 288     public List<? extends PropertyInfo<T,C>> getProperties() {
 289         if(properties!=null)    return properties;
 290 
 291         // check the access type first
 292         XmlAccessType at = getAccessType();
 293 
 294         properties = new FinalArrayList<PropertyInfoImpl<T,C,F,M>>();
 295 
 296         findFieldProperties(clazz,at);
 297 
 298         findGetterSetterProperties(at);
 299 
 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         }
 313 
 314         {// additional error checks
 315             PropertyInfoImpl vp=null; // existing value property
 316             PropertyInfoImpl ep=null; // existing element property
 317 
 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             }
 344 
 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         }
 353 
 354         return properties;
 355     }
 356 
 357     private void findFieldProperties(C c, XmlAccessType at) {
 358 
 359         // always find properties from the super class first
 360         C sc = nav().getSuperClass(c);
 361         if (shouldRecurseSuperClass(sc)) {
 362             findFieldProperties(sc,at);
 363         }
 364 
 365         for( F f : nav().getDeclaredFields(c) ) {
 366             Annotation[] annotations = reader().getAllFieldAnnotations(f,this);
 367             boolean isDummy = reader().hasFieldAnnotation(OverrideAnnotationOf.class, f);
 368 
 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     }
 400 
 401     public final boolean hasValueProperty() {
 402         ClassInfoImpl<T, C, F, M> bc = getBaseClass();
 403         if(bc!=null && bc.hasValueProperty())
 404             return true;
 405 
 406         for (PropertyInfo p : getProperties()) {
 407             if (p instanceof ValuePropertyInfo) return true;
 408         }
 409 
 410         return false;
 411         }
 412 
 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     }
 420 
 421     /**
 422      * This hook is used by {@link RuntimeClassInfoImpl} to look for {@link com.sun.xml.internal.bind.annotation.XmlLocation}.
 423      */
 424     protected void checkFieldXmlLocation(F f) {
 425     }
 426 
 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     }
 437 
 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     }
 449 
 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     }
 460 
 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];
 472 
 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;
 478 
 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         }
 488 
 489         public int compare(PropertyInfoImpl o1, PropertyInfoImpl o2) {
 490             int lhs = checkedGet(o1);
 491             int rhs = checkedGet(o2);
 492 
 493             return lhs-rhs;
 494         }
 495 
 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));
 503 
 504                 // give it an order to recover from an error
 505                 i = size();
 506                 put(p.getName(),i);
 507             }
 508 
 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>();
 514 
 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             }
 522 
 523             return i;
 524         }
 525 
 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                         }
 537 
 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     }
 550 
 551     public boolean hasProperties() {
 552         return !properties.isEmpty();
 553     }
 554 
 555 
 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     }
 565 
 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     }
 572 
 573     private static final class ConflictException extends Exception {
 574         final List<Annotation> annotations;
 575 
 576         public ConflictException(List<Annotation> one) {
 577             this.annotations = one;
 578         }
 579     }
 580 
 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     }
 588 
 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);
 599 
 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;
 609 
 610         SecondaryAnnotation(int bitMask, Class<? extends Annotation>... members) {
 611             this.bitMask = bitMask;
 612             this.members = members;
 613         }
 614     }
 615 
 616     private static final SecondaryAnnotation[] SECONDARY_ANNOTATIONS = SecondaryAnnotation.values();
 617 
 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);
 632 
 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;
 639 
 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         }
 649 
 650         boolean allows(SecondaryAnnotation a) {
 651             return (allowedsecondaryAnnotations&a.bitMask)==0;
 652         }
 653     }
 654 
 655     private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
 656 
 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         };
 675 
 676         HashMap<Class,Integer> m = ANNOTATION_NUMBER_MAP;
 677 
 678         // characterizing annotations
 679         for( Class c : annotations )
 680             m.put(c, m.size() );
 681 
 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     }
 690 
 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     }
 696 
 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)
 712 
 713 
 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;
 728 
 729         // encountered secondary annotations are accumulated into a bit mask
 730         int secondaryAnnotations = 0;
 731 
 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             }
 754 
 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;
 759 
 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             }
 784 
 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             }
 790 
 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;
 795 
 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             }
 807 
 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             }
 828 
 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;
 842 
 843                 if(inheritsAttributeWildcard()) {
 844                     builder.reportError(new IllegalAnnotationException(
 845                         Messages.SUPER_CLASS_HAS_WILDCARD.format(),
 846                             aa,getInheritedAttributeWildcard()));
 847                     return;
 848                 }
 849 
 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                 }
 857 
 858 
 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;
 881 
 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) ));
 887 
 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
 895 
 896         }
 897     }
 898 
 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     }
 902 
 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     }
 906 
 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     }
 910 
 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     }
 914 
 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     }
 918 
 919 
 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>();
 928 
 929         C c = clazz;
 930         do {
 931             collectGetterSetters(clazz, getters, setters);
 932 
 933             // take super classes into account if they have @XmlTransient
 934             c = nav().getSuperClass(c);
 935         } while(shouldRecurseSuperClass(c));
 936 
 937 
 938         // compute the intersection
 939         Set<String> complete = new TreeSet<String>(getters.keySet());
 940         complete.retainAll(setters.keySet());
 941 
 942         resurrect(getters, complete);
 943         resurrect(setters, complete);
 944 
 945         // then look for read/write properties.
 946         for (String name : complete) {
 947             M getter = getters.get(name);
 948             M setter = setters.get(name);
 949 
 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;
 952 
 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             }
 961 
 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                 }
 978 
 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                 }
 991 
 992                 addProperty(createAccessorSeed(getter, setter), r, false);
 993             }
 994         }
 995         // done with complete pairs
 996         getters.keySet().removeAll(complete);
 997         setters.keySet().removeAll(complete);
 998 
 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.
1007     }
1008 
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);
1017 
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
1022 
1023             if(nav().isBridgeMethod(method))
1024                 continue;   // ignore
1025 
1026             String name = nav().getMethodName(method);
1027             int arity = nav().getMethodParameters(method).length;
1028 
1029             if(nav().isStaticMethod(method)) {
1030                 ensureNoAnnotation(method);
1031                 continue;
1032             }
1033 
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;
1039             }
1040 
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);
1048                     }
1049                     propSetters.add(method);
1050                 used = true; // used check performed later
1051             }
1052 
1053             if(!used)
1054                 ensureNoAnnotation(method);
1055         }
1056 
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;
1065             }
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;
1072                 }
1073             }
1074         }
1075 
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));
1079         }
1080     }
1081 
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));
1088     }
1089 
1090     /**
1091      * Returns true if the method is considered 'public'.
1092      */
1093     private boolean isConsideredPublic(M m) {
1094         return m ==null || nav().isPublicMethod(m);
1095     }
1096 
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());
1107         }
1108     }
1109 
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;
1122             }
1123         }
1124     }
1125 
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());
1131     }
1132 
1133     /**
1134      * Returns true if the array contains a JAXB annotation.
1135      */
1136     private static boolean hasJAXBAnnotation(Annotation[] annotations) {
1137         return getSomeJAXBAnnotation(annotations)!=null;
1138     }
1139 
1140     private static Annotation getSomeJAXBAnnotation(Annotation[] annotations) {
1141         for( Annotation a : annotations )
1142             if(isJAXBAnnotation(a))
1143                 return a;
1144         return null;
1145     }
1146 
1147 
1148     /**
1149      * Returns "Foo" from "getFoo" or "isFoo".
1150      *
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;
1160     }
1161 
1162     /**
1163      * Returns "Foo" from "setFoo".
1164      *
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;
1172     }
1173 
1174     /**
1175      * Creates a new {@link FieldPropertySeed} object.
1176      *
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);
1182     }
1183 
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);
1189     }
1190 
1191     public final boolean isElement() {
1192         return elementName!=null;
1193     }
1194 
1195     public boolean isAbstract() {
1196         return nav().isAbstract(clazz);
1197     }
1198 
1199     public boolean isOrdered() {
1200         return propOrder!=null;
1201     }
1202 
1203     public final boolean isFinal() {
1204         return nav().isFinal(clazz);
1205     }
1206 
1207     public final boolean hasSubClasses() {
1208         return hasSubClasses;
1209     }
1210 
1211     public final boolean hasAttributeWildcard() {
1212         return declaresAttributeWildcard() || inheritsAttributeWildcard();
1213     }
1214 
1215     public final boolean inheritsAttributeWildcard() {
1216         return getInheritedAttributeWildcard()!=null;
1217     }
1218 
1219     public final boolean declaresAttributeWildcard() {
1220         return attributeWildcard!=null;
1221     }
1222 
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;
1231     }
1232 
1233     public final QName getElementName() {
1234         return elementName;
1235     }
1236 
1237     public final QName getTypeName() {
1238         return typeName;
1239     }
1240 
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;
1245     }
1246 
1247     /**
1248      * Called after all the {@link com.sun.xml.internal.bind.v2.model.core.TypeInfo}s are collected into the {@link #owner}.
1249      */
1250     @Override
1251     /*package*/ void link() {
1252         getProperties();    // make sure properties!=null
1253 
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 ));
1263             }
1264         }
1265         super.link();
1266     }
1267 
1268     public Location getLocation() {
1269         return nav().getClassLocation(clazz);
1270     }
1271 
1272     /**
1273      *  XmlType allows specification of factoryClass and
1274      *  factoryMethod.  There are to be used if no default
1275      *  constructor is found.
1276      *
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;
1282 
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);
1288             }
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;
1297                 }
1298             }
1299             if (factoryMethod == null){
1300                 builder.reportError(new IllegalAnnotationException(
1301                 Messages.NO_FACTORY_METHOD.format(nav().getClassName(nav().asDecl(fClass)), method), this ));
1302             }
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 ));
1306         }
1307         return factoryMethod != null;
1308     }
1309 
1310     public Method getFactoryMethod(){
1311         return (Method) factoryMethod;
1312     }
1313 
1314     @Override
1315     public String toString() {
1316         return "ClassInfo("+clazz+')';
1317     }
1318 
1319     private static final String[] DEFAULT_ORDER = new String[0];
1320 }
--- EOF ---