1
10
11 package com.sun.xml.bind.v2.model.impl;
12
13 import java.lang.annotation.Annotation;
14 import java.lang.reflect.Method;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.LinkedHashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.TreeSet;
26 import java.util.AbstractList;
27
28 import javax.xml.bind.annotation.XmlAccessOrder;
29 import javax.xml.bind.annotation.XmlAccessType;
30 import javax.xml.bind.annotation.XmlAccessorOrder;
31 import javax.xml.bind.annotation.XmlAccessorType;
32 import javax.xml.bind.annotation.XmlAnyAttribute;
33 import javax.xml.bind.annotation.XmlAnyElement;
34 import javax.xml.bind.annotation.XmlAttachmentRef;
35 import javax.xml.bind.annotation.XmlAttribute;
36 import javax.xml.bind.annotation.XmlElement;
37 import javax.xml.bind.annotation.XmlElementRef;
38 import javax.xml.bind.annotation.XmlElementRefs;
39 import javax.xml.bind.annotation.XmlElementWrapper;
40 import javax.xml.bind.annotation.XmlElements;
41 import javax.xml.bind.annotation.XmlID;
42 import javax.xml.bind.annotation.XmlIDREF;
43 import javax.xml.bind.annotation.XmlInlineBinaryData;
44 import javax.xml.bind.annotation.XmlList;
45 import javax.xml.bind.annotation.XmlMimeType;
46 import javax.xml.bind.annotation.XmlMixed;
47 import javax.xml.bind.annotation.XmlRootElement;
48 import javax.xml.bind.annotation.XmlSchemaType;
49 import javax.xml.bind.annotation.XmlTransient;
50 import javax.xml.bind.annotation.XmlType;
51 import javax.xml.bind.annotation.XmlValue;
52 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
53 import javax.xml.namespace.QName;
54
55 import com.sun.istack.FinalArrayList;
56 import com.sun.xml.bind.annotation.OverrideAnnotationOf;
57 import com.sun.xml.bind.v2.model.annotation.Locatable;
58 import com.sun.xml.bind.v2.model.annotation.MethodLocatable;
59 import com.sun.xml.bind.v2.model.core.ClassInfo;
60 import com.sun.xml.bind.v2.model.core.Element;
61 import com.sun.xml.bind.v2.model.core.ID;
62 import com.sun.xml.bind.v2.model.core.NonElement;
63 import com.sun.xml.bind.v2.model.core.PropertyInfo;
64 import com.sun.xml.bind.v2.model.core.PropertyKind;
65 import com.sun.xml.bind.v2.model.core.ValuePropertyInfo;
66 import com.sun.xml.bind.v2.runtime.IllegalAnnotationException;
67 import com.sun.xml.bind.v2.runtime.Location;
68 import com.sun.xml.bind.v2.util.EditDistance;
69
70
71
77 public class ClassInfoImpl<T,C,F,M> extends TypeInfoImpl<T,C,F,M>
78 implements ClassInfo<T,C>, Element<T,C> {
79
80 protected final C clazz;
81
82
85 private final QName elementName;
86
87
90 private final QName typeName;
91
92
97 private FinalArrayList<PropertyInfoImpl<T,C,F,M>> properties;
98
99
106 private String[] propOrder;
107
108
113 private ClassInfoImpl<T,C,F,M> baseClass;
114
115 private boolean baseClassComputed = false;
116
117 private boolean hasSubClasses = false;
118
119
125 protected PropertySeed<T,C,F,M> attributeWildcard;
126
127
128
131 private M factoryMethod = null;
132
133 ClassInfoImpl(ModelBuilder<T,C,F,M> builder, Locatable upstream, C clazz) {
134 super(builder,upstream);
135 this.clazz = clazz;
136 assert clazz!=null;
137
138
139 elementName = parseElementName(clazz);
140
141
142 XmlType t = reader().getClassAnnotation(XmlType.class,clazz,this);
143 typeName = parseTypeName(clazz,t);
144
145 if(t!=null) {
146 String[] propOrder = t.propOrder();
147 if(propOrder.length==0)
148 this.propOrder = null;
149 else {
150 if(propOrder[0].length()==0)
151 this.propOrder = DEFAULT_ORDER;
152 else
153 this.propOrder = propOrder;
154 }
155 } else {
156 propOrder = DEFAULT_ORDER;
157 }
158
159
160
161 XmlAccessorOrder xao = reader().getPackageAnnotation(XmlAccessorOrder.class, clazz, this);
162 if((xao != null) && (xao.value() == XmlAccessOrder.UNDEFINED)) {
163 propOrder = null;
164 }
165
166
167 xao = reader().getClassAnnotation(XmlAccessorOrder.class, clazz, this);
168 if((xao != null) && (xao.value() == XmlAccessOrder.UNDEFINED)) {
169 propOrder = null;
170 }
171
172 if(nav().isInterface(clazz)) {
173 builder.reportError(new IllegalAnnotationException(
174 Messages.CANT_HANDLE_INTERFACE.format(nav().getClassName(clazz)), this ));
175 }
176
177
178 if (!hasFactoryConstructor(t)){
179 if(!nav().hasDefaultConstructor(clazz)){
180 if(nav().isInnerClass(clazz)) {
181 builder.reportError(new IllegalAnnotationException(
182 Messages.CANT_HANDLE_INNER_CLASS.format(nav().getClassName(clazz)), this ));
183 } else if (elementName != null) {
184 builder.reportError(new IllegalAnnotationException(
185 Messages.NO_DEFAULT_CONSTRUCTOR.format(nav().getClassName(clazz)), this ));
186 }
187 }
188 }
189 }
190
191 public ClassInfoImpl<T,C,F,M> getBaseClass() {
192 if (!baseClassComputed) {
193
194 C s = nav().getSuperClass(clazz);
195 if(s==null || s==nav().asDecl(Object.class)) {
196 baseClass = null;
197 } else {
198 NonElement<T,C> b = builder.getClassInfo(s, true, this);
199 if(b instanceof ClassInfoImpl) {
200 baseClass = (ClassInfoImpl<T,C,F,M>) b;
201 baseClass.hasSubClasses = true;
202 } else {
203 baseClass = null;
204 }
205 }
206 baseClassComputed = true;
207 }
208 return baseClass;
209 }
210
211
216 public final Element<T,C> getSubstitutionHead() {
217 ClassInfoImpl<T,C,F,M> c = getBaseClass();
218 while(c!=null && !c.isElement())
219 c = c.getBaseClass();
220 return c;
221 }
222
223 public final C getClazz() {
224 return clazz;
225 }
226
227
234 public ClassInfoImpl<T,C,F,M> getScope() {
235 return null;
236 }
237
238 public final T getType() {
239 return nav().use(clazz);
240 }
241
242
246 public boolean canBeReferencedByIDREF() {
247 for (PropertyInfo<T,C> p : getProperties()) {
248 if(p.id()== ID.ID)
249 return true;
250 }
251 ClassInfoImpl<T,C,F,M> base = getBaseClass();
252 if(base!=null)
253 return base.canBeReferencedByIDREF();
254 else
255 return false;
256 }
257
258 public final String getName() {
259 return nav().getClassName(clazz);
260 }
261
262 public <A extends Annotation> A readAnnotation(Class<A> a) {
263 return reader().getClassAnnotation(a,clazz,this);
264 }
265
266 public Element<T,C> asElement() {
267 if(isElement())
268 return this;
269 else
270 return null;
271 }
272
273 public List<? extends PropertyInfo<T,C>> getProperties() {
274 if(properties!=null) return properties;
275
276
277 XmlAccessType at = getAccessType();
278
279 properties = new FinalArrayList<PropertyInfoImpl<T,C,F,M>>();
280
281 findFieldProperties(clazz,at);
282
283 findGetterSetterProperties(at);
284
285 if(propOrder==DEFAULT_ORDER || propOrder==null) {
286 XmlAccessOrder ao = getAccessorOrder();
287 if(ao==XmlAccessOrder.ALPHABETICAL)
288 Collections.sort(properties);
289 } else {
290
291 PropertySorter sorter = new PropertySorter();
292 for (PropertyInfoImpl p : properties) {
293 sorter.checkedGet(p);
294 }
295 Collections.sort(properties,sorter);
296 sorter.checkUnusedProperties();
297 }
298
299 {
300 PropertyInfoImpl vp=null;
301 PropertyInfoImpl ep=null;
302
303 for (PropertyInfoImpl p : properties) {
304 switch(p.kind()) {
305 case ELEMENT:
306 case REFERENCE:
307 case MAP:
308 ep = p;
309 break;
310 case VALUE:
311 if(vp!=null) {
312
313 builder.reportError(new IllegalAnnotationException(
314 Messages.MULTIPLE_VALUE_PROPERTY.format(),
315 vp, p ));
316 }
317 if(getBaseClass()!=null) {
318 builder.reportError(new IllegalAnnotationException(
319 Messages.XMLVALUE_IN_DERIVED_TYPE.format(), p ));
320 }
321 vp = p;
322 break;
323 case ATTRIBUTE:
324 break;
325 default:
326 assert false;
327 }
328 }
329
330 if(ep!=null && vp!=null) {
331
332 builder.reportError(new IllegalAnnotationException(
333 Messages.ELEMENT_AND_VALUE_PROPERTY.format(),
334 vp, ep
335 ));
336 }
337 }
338
339 return properties;
340 }
341
342 private void findFieldProperties(C c, XmlAccessType at) {
343
344
345 C sc = nav().getSuperClass(c);
346 if (shouldRecurseSuperClass(sc)) {
347 findFieldProperties(sc,at);
348 }
349
350 for( F f : nav().getDeclaredFields(c) ) {
351 Annotation[] annotations = reader().getAllFieldAnnotations(f,this);
352 boolean isDummy = reader().hasFieldAnnotation(OverrideAnnotationOf.class, f);
353
354 if( nav().isTransient(f) ) {
355
356 if(hasJAXBAnnotation(annotations))
357 builder.reportError(new IllegalAnnotationException(
358 Messages.TRANSIENT_FIELD_NOT_BINDABLE.format(nav().getFieldName(f)),
359 getSomeJAXBAnnotation(annotations)));
360 } else
361 if( nav().isStaticField(f) ) {
362
363 if(hasJAXBAnnotation(annotations))
364 addProperty(createFieldSeed(f),annotations, false);
365 } else {
366 if(at==XmlAccessType.FIELD
367 ||(at==XmlAccessType.PUBLIC_MEMBER && nav().isPublicField(f))
368 || hasJAXBAnnotation(annotations)) {
369 if (isDummy) {
370 ClassInfo<T, C> top = getBaseClass();
371 while ((top != null) && (top.getProperty("content") == null)) {
372 top = top.getBaseClass();
373 }
374 DummyPropertyInfo prop = (DummyPropertyInfo) top.getProperty("content");
375 PropertySeed seed = createFieldSeed(f);
376 ((DummyPropertyInfo)prop).addType(createReferenceProperty(seed));
377 } else {
378 addProperty(createFieldSeed(f), annotations, false);
379 }
380 }
381 checkFieldXmlLocation(f);
382 }
383 }
384 }
385
386 public final boolean hasValueProperty() {
387 ClassInfoImpl<T, C, F, M> bc = getBaseClass();
388 if(bc!=null && bc.hasValueProperty())
389 return true;
390
391 for (PropertyInfo p : getProperties()) {
392 if (p instanceof ValuePropertyInfo) return true;
393 }
394
395 return false;
396 }
397
398 public PropertyInfo<T,C> getProperty(String name) {
399 for( PropertyInfo<T,C> p: getProperties() ) {
400 if(p.getName().equals(name))
401 return p;
402 }
403 return null;
404 }
405
406
409 protected void checkFieldXmlLocation(F f) {
410 }
411
412
415 private <T extends Annotation> T getClassOrPackageAnnotation(Class<T> type) {
416 T t = reader().getClassAnnotation(type,clazz,this);
417 if(t!=null)
418 return t;
419
420 return reader().getPackageAnnotation(type,clazz,this);
421 }
422
423
427 private XmlAccessType getAccessType() {
428 XmlAccessorType xat = getClassOrPackageAnnotation(XmlAccessorType.class);
429 if(xat!=null)
430 return xat.value();
431 else
432 return XmlAccessType.PUBLIC_MEMBER;
433 }
434
435
438 private XmlAccessOrder getAccessorOrder() {
439 XmlAccessorOrder xao = getClassOrPackageAnnotation(XmlAccessorOrder.class);
440 if(xao!=null)
441 return xao.value();
442 else
443 return XmlAccessOrder.UNDEFINED;
444 }
445
446
452 private final class PropertySorter extends HashMap<String,Integer> implements Comparator<PropertyInfoImpl> {
453
456 PropertyInfoImpl[] used = new PropertyInfoImpl[propOrder.length];
457
458
462 private Set<String> collidedNames;
463
464 PropertySorter() {
465 super(propOrder.length);
466 for( String name : propOrder )
467 if(put(name,size())!=null) {
468
469 builder.reportError(new IllegalAnnotationException(
470 Messages.DUPLICATE_ENTRY_IN_PROP_ORDER.format(name),ClassInfoImpl.this));
471 }
472 }
473
474 public int compare(PropertyInfoImpl o1, PropertyInfoImpl o2) {
475 int lhs = checkedGet(o1);
476 int rhs = checkedGet(o2);
477
478 return lhs-rhs;
479 }
480
481 private int checkedGet(PropertyInfoImpl p) {
482 Integer i = get(p.getName());
483 if(i==null) {
484
485 if (p.kind().isOrdered)
486 builder.reportError(new IllegalAnnotationException(
487 Messages.PROPERTY_MISSING_FROM_ORDER.format(p.getName()),p));
488
489
490 i = size();
491 put(p.getName(),i);
492 }
493
494
495 int ii = i;
496 if(ii<used.length) {
497 if(used[ii]!=null && used[ii]!=p) {
498 if(collidedNames==null) collidedNames = new HashSet<String>();
499
500 if(collidedNames.add(p.getName()))
501
502 builder.reportError(new IllegalAnnotationException(
503 Messages.DUPLICATE_PROPERTIES.format(p.getName()),p,used[ii]));
504 }
505 used[ii] = p;
506 }
507
508 return i;
509 }
510
511
514 public void checkUnusedProperties() {
515 for( int i=0; i<used.length; i++ )
516 if(used[i]==null) {
517 String unusedName = propOrder[i];
518 String nearest = EditDistance.findNearest(unusedName, new AbstractList<String>() {
519 public String get(int index) {
520 return properties.get(index).getName();
521 }
522
523 public int size() {
524 return properties.size();
525 }
526 });
527 boolean isOverriding = (i > (properties.size()-1)) ? false : properties.get(i).hasAnnotation(OverrideAnnotationOf.class);
528 if (!isOverriding) {
529 builder.reportError(new IllegalAnnotationException(
530 Messages.PROPERTY_ORDER_CONTAINS_UNUSED_ENTRY.format(unusedName,nearest),ClassInfoImpl.this));
531 }
532 }
533 }
534 }
535
536 public boolean hasProperties() {
537 return !properties.isEmpty();
538 }
539
540
541
544 private static <T> T pickOne( T... args ) {
545 for( T arg : args )
546 if(arg!=null)
547 return arg;
548 return null;
549 }
550
551 private static <T> List<T> makeSet( T... args ) {
552 List<T> l = new FinalArrayList<T>();
553 for( T arg : args )
554 if(arg!=null) l.add(arg);
555 return l;
556 }
557
558 private static final class ConflictException extends Exception {
559 final List<Annotation> annotations;
560
561 public ConflictException(List<Annotation> one) {
562 this.annotations = one;
563 }
564 }
565
566 private static final class DuplicateException extends Exception {
567 final Annotation a1,a2;
568 public DuplicateException(Annotation a1, Annotation a2) {
569 this.a1 = a1;
570 this.a2 = a2;
571 }
572 }
573
574
577 private static enum SecondaryAnnotation {
578 JAVA_TYPE (0x01, XmlJavaTypeAdapter.class),
579 ID_IDREF (0x02, XmlID.class, XmlIDREF.class),
580 BINARY (0x04, XmlInlineBinaryData.class, XmlMimeType.class, XmlAttachmentRef.class),
581 ELEMENT_WRAPPER (0x08, XmlElementWrapper.class),
582 LIST (0x10, XmlList.class),
583 SCHEMA_TYPE (0x20, XmlSchemaType.class);
584
585
589 final int bitMask;
590
593 final Class<? extends Annotation>[] members;
594
595 SecondaryAnnotation(int bitMask, Class<? extends Annotation>... members) {
596 this.bitMask = bitMask;
597 this.members = members;
598 }
599 }
600
601 private static final SecondaryAnnotation[] SECONDARY_ANNOTATIONS = SecondaryAnnotation.values();
602
603
609 private static enum PropertyGroup {
610 TRANSIENT (false,false,false,false,false,false),
611 ANY_ATTRIBUTE (true, false,false,false,false,false),
612 ATTRIBUTE (true, true, true, false,true, true ),
613 VALUE (true, true, true, false,true, true ),
614 ELEMENT (true, true, true, true, true, true ),
615 ELEMENT_REF (true, false,false,true, false,false),
616 MAP (false,false,false,true, false,false);
617
618
623 final int allowedsecondaryAnnotations;
624
625 PropertyGroup(boolean... bits) {
626 int mask = 0;
627 assert bits.length==SECONDARY_ANNOTATIONS.length;
628 for( int i=0; i<bits.length; i++ ) {
629 if(bits[i])
630 mask |= SECONDARY_ANNOTATIONS[i].bitMask;
631 }
632 allowedsecondaryAnnotations = ~mask;
633 }
634
635 boolean allows(SecondaryAnnotation a) {
636 return (allowedsecondaryAnnotations&a.bitMask)==0;
637 }
638 }
639
640 private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
641
642
645 private static final HashMap<Class,Integer> ANNOTATION_NUMBER_MAP = new HashMap<Class,Integer>();
646 static {
647 Class[] annotations = {
648 XmlTransient.class,
649 XmlAnyAttribute.class,
650 XmlAttribute.class,
651 XmlValue.class,
652 XmlElement.class,
653 XmlElements.class,
654 XmlElementRef.class,
655 XmlElementRefs.class,
656 XmlAnyElement.class,
657 XmlMixed.class,
658 OverrideAnnotationOf.class,
659 };
660
661 HashMap<Class,Integer> m = ANNOTATION_NUMBER_MAP;
662
663
664 for( Class c : annotations )
665 m.put(c, m.size() );
666
667
668 int index = 20;
669 for( SecondaryAnnotation sa : SECONDARY_ANNOTATIONS ) {
670 for( Class member : sa.members )
671 m.put(member,index);
672 index++;
673 }
674 }
675
676 private void checkConflict(Annotation a, Annotation b) throws DuplicateException {
677 assert b!=null;
678 if(a!=null)
679 throw new DuplicateException(a,b);
680 }
681
682
694 private void addProperty( PropertySeed<T,C,F,M> seed, Annotation[] annotations, boolean dummy ) {
695
696
697
698
699
700
701
702 XmlTransient t = null;
703 XmlAnyAttribute aa = null;
704 XmlAttribute a = null;
705 XmlValue v = null;
706 XmlElement e1 = null;
707 XmlElements e2 = null;
708 XmlElementRef r1 = null;
709 XmlElementRefs r2 = null;
710 XmlAnyElement xae = null;
711 XmlMixed mx = null;
712 OverrideAnnotationOf ov = null;
713
714
715 int secondaryAnnotations = 0;
716
717 try {
718 for( Annotation ann : annotations ) {
719 Integer index = ANNOTATION_NUMBER_MAP.get(ann.annotationType());
720 if(index==null) continue;
721 switch(index) {
722 case 0: checkConflict(t ,ann); t = (XmlTransient) ann; break;
723 case 1: checkConflict(aa ,ann); aa = (XmlAnyAttribute) ann; break;
724 case 2: checkConflict(a ,ann); a = (XmlAttribute) ann; break;
725 case 3: checkConflict(v ,ann); v = (XmlValue) ann; break;
726 case 4: checkConflict(e1 ,ann); e1 = (XmlElement) ann; break;
727 case 5: checkConflict(e2 ,ann); e2 = (XmlElements) ann; break;
728 case 6: checkConflict(r1 ,ann); r1 = (XmlElementRef) ann; break;
729 case 7: checkConflict(r2 ,ann); r2 = (XmlElementRefs) ann; break;
730 case 8: checkConflict(xae,ann); xae = (XmlAnyElement) ann; break;
731 case 9: checkConflict(mx, ann); mx = (XmlMixed) ann; break;
732 case 10: checkConflict(ov, ann); ov = (OverrideAnnotationOf) ann; break;
733 default:
734
735 secondaryAnnotations |= (1<<(index-20));
736 break;
737 }
738 }
739
740
741
742 PropertyGroup group = null;
743 int groupCount = 0;
744
745 if(t!=null) {
746 group = PropertyGroup.TRANSIENT;
747 groupCount++;
748 }
749 if(aa!=null) {
750 group = PropertyGroup.ANY_ATTRIBUTE;
751 groupCount++;
752 }
753 if(a!=null) {
754 group = PropertyGroup.ATTRIBUTE;
755 groupCount++;
756 }
757 if(v!=null) {
758 group = PropertyGroup.VALUE;
759 groupCount++;
760 }
761 if(e1!=null || e2!=null) {
762 group = PropertyGroup.ELEMENT;
763 groupCount++;
764 }
765 if(r1!=null || r2!=null || xae!=null || mx!=null || ov != null) {
766 group = PropertyGroup.ELEMENT_REF;
767 groupCount++;
768 }
769
770 if(groupCount>1) {
771
772 List<Annotation> err = makeSet(t,aa,a,v,pickOne(e1,e2),pickOne(r1,r2,xae));
773 throw new ConflictException(err);
774 }
775
776 if(group==null) {
777
778
779 assert groupCount==0;
780
781
782 if(nav().isSubClassOf( seed.getRawType(), nav().ref(Map.class) )
783 && !seed.hasAnnotation(XmlJavaTypeAdapter.class))
784 group = PropertyGroup.MAP;
785 else
786 group = PropertyGroup.ELEMENT;
787 } else if (group.equals(PropertyGroup.ELEMENT)) {
788 if (nav().isSubClassOf( seed.getRawType(), nav().ref(Map.class)) && !seed.hasAnnotation(XmlJavaTypeAdapter.class)) {
789 group = PropertyGroup.MAP;
790 }
791 }
792
793
794
795 if( (secondaryAnnotations&group.allowedsecondaryAnnotations)!=0 ) {
796
797 for( SecondaryAnnotation sa : SECONDARY_ANNOTATIONS ) {
798 if(group.allows(sa))
799 continue;
800 for( Class<? extends Annotation> m : sa.members ) {
801 Annotation offender = seed.readAnnotation(m);
802 if(offender!=null) {
803
804 builder.reportError(new IllegalAnnotationException(
805 Messages.ANNOTATION_NOT_ALLOWED.format(m.getSimpleName()),offender));
806 return;
807 }
808 }
809 }
810
811 assert false;
812 }
813
814
815 switch(group) {
816 case TRANSIENT:
817 return;
818 case ANY_ATTRIBUTE:
819
820 if(attributeWildcard!=null) {
821 builder.reportError(new IllegalAnnotationException(
822 Messages.TWO_ATTRIBUTE_WILDCARDS.format(
823 nav().getClassName(getClazz())),aa,attributeWildcard));
824 return;
825 }
826 attributeWildcard = seed;
827
828 if(inheritsAttributeWildcard()) {
829 builder.reportError(new IllegalAnnotationException(
830 Messages.SUPER_CLASS_HAS_WILDCARD.format(),
831 aa,getInheritedAttributeWildcard()));
832 return;
833 }
834
835
836 if(!nav().isSubClassOf(seed.getRawType(),nav().ref(Map.class))) {
837 builder.reportError(new IllegalAnnotationException(
838 Messages.INVALID_ATTRIBUTE_WILDCARD_TYPE.format(nav().getTypeName(seed.getRawType())),
839 aa,getInheritedAttributeWildcard()));
840 return;
841 }
842
843
844 return;
845 case ATTRIBUTE:
846 properties.add(createAttributeProperty(seed));
847 return;
848 case VALUE:
849 properties.add(createValueProperty(seed));
850 return;
851 case ELEMENT:
852 properties.add(createElementProperty(seed));
853 return;
854 case ELEMENT_REF:
855 properties.add(createReferenceProperty(seed));
856 return;
857 case MAP:
858 properties.add(createMapProperty(seed));
859 return;
860 default:
861 assert false;
862 }
863 } catch( ConflictException x ) {
864
865 List<Annotation> err = x.annotations;
866
867 builder.reportError(new IllegalAnnotationException(
868 Messages.MUTUALLY_EXCLUSIVE_ANNOTATIONS.format(
869 nav().getClassName(getClazz())+'#'+seed.getName(),
870 err.get(0).annotationType().getName(), err.get(1).annotationType().getName()),
871 err.get(0), err.get(1) ));
872
873
874 } catch( DuplicateException e ) {
875
876 builder.reportError(new IllegalAnnotationException(
877 Messages.DUPLICATE_ANNOTATIONS.format(e.a1.annotationType().getName()),
878 e.a1, e.a2 ));
879
880
881 }
882 }
883
884 protected ReferencePropertyInfoImpl<T,C,F,M> createReferenceProperty(PropertySeed<T,C,F,M> seed) {
885 return new ReferencePropertyInfoImpl<T,C,F,M>(this,seed);
886 }
887
888 protected AttributePropertyInfoImpl<T,C,F,M> createAttributeProperty(PropertySeed<T,C,F,M> seed) {
889 return new AttributePropertyInfoImpl<T,C,F,M>(this,seed);
890 }
891
892 protected ValuePropertyInfoImpl<T,C,F,M> createValueProperty(PropertySeed<T,C,F,M> seed) {
893 return new ValuePropertyInfoImpl<T,C,F,M>(this,seed);
894 }
895
896 protected ElementPropertyInfoImpl<T,C,F,M> createElementProperty(PropertySeed<T,C,F,M> seed) {
897 return new ElementPropertyInfoImpl<T,C,F,M>(this,seed);
898 }
899
900 protected MapPropertyInfoImpl<T,C,F,M> createMapProperty(PropertySeed<T,C,F,M> seed) {
901 return new MapPropertyInfoImpl<T,C,F,M>(this,seed);
902 }
903
904
905
908 private void findGetterSetterProperties(XmlAccessType at) {
909
910
911 Map<String,M> getters = new LinkedHashMap<String,M>();
912 Map<String,M> setters = new LinkedHashMap<String,M>();
913
914 C c = clazz;
915 do {
916 collectGetterSetters(clazz, getters, setters);
917
918
919 c = nav().getSuperClass(c);
920 } while(shouldRecurseSuperClass(c));
921
922
923
924 Set<String> complete = new TreeSet<String>(getters.keySet());
925 complete.retainAll(setters.keySet());
926
927 resurrect(getters, complete);
928 resurrect(setters, complete);
929
930
931 for (String name : complete) {
932 M getter = getters.get(name);
933 M setter = setters.get(name);
934
935 Annotation[] ga = getter!=null ? reader().getAllMethodAnnotations(getter,new MethodLocatable<M>(this,getter,nav())) : EMPTY_ANNOTATIONS;
936 Annotation[] sa = setter!=null ? reader().getAllMethodAnnotations(setter,new MethodLocatable<M>(this,setter,nav())) : EMPTY_ANNOTATIONS;
937
938 boolean hasAnnotation = hasJAXBAnnotation(ga) || hasJAXBAnnotation(sa);
939 boolean isOverriding = false;
940 if(!hasAnnotation) {
941
942
943 isOverriding = (getter!=null && nav().isOverriding(getter,c))
944 && (setter!=null && nav().isOverriding(setter,c));
945 }
946
947 if((at==XmlAccessType.PROPERTY && !isOverriding)
948 || (at==XmlAccessType.PUBLIC_MEMBER && isConsideredPublic(getter) && isConsideredPublic(setter) && !isOverriding)
949 || hasAnnotation) {
950
951 if(getter!=null && setter!=null
952 && !nav().isSameType(nav().getReturnType(getter), nav().getMethodParameters(setter)[0])) {
953
954 builder.reportError(new IllegalAnnotationException(
955 Messages.GETTER_SETTER_INCOMPATIBLE_TYPE.format(
956 nav().getTypeName(nav().getReturnType(getter)),
957 nav().getTypeName(nav().getMethodParameters(setter)[0])
958 ),
959 new MethodLocatable<M>( this, getter, nav()),
960 new MethodLocatable<M>( this, setter, nav())));
961 continue;
962 }
963
964
965 Annotation[] r;
966 if(ga.length==0) {
967 r = sa;
968 } else
969 if(sa.length==0) {
970 r = ga;
971 } else {
972 r = new Annotation[ga.length+sa.length];
973 System.arraycopy(ga,0,r,0,ga.length);
974 System.arraycopy(sa,0,r,ga.length,sa.length);
975 }
976
977 addProperty(createAccessorSeed(getter, setter), r, false);
978 }
979 }
980
981 getters.keySet().removeAll(complete);
982 setters.keySet().removeAll(complete);
983
984
985
986
987
988
989
990
991
992 }
993
994 private void collectGetterSetters(C c, Map<String,M> getters, Map<String,M> setters) {
995
996
997
998
999 C sc = nav().getSuperClass(c);
1000 if(shouldRecurseSuperClass(sc))
1001 collectGetterSetters(sc,getters,setters);
1002
1003 Collection<? extends M> methods = nav().getDeclaredMethods(c);
1004 Map<String,List<M>> allSetters = new LinkedHashMap<String,List<M>>();
1005 for( M method : methods ) {
1006 boolean used = false;
1007
1008 if(nav().isBridgeMethod(method))
1009 continue;
1010
1011 String name = nav().getMethodName(method);
1012 int arity = nav().getMethodParameters(method).length;
1013
1014 if(nav().isStaticMethod(method)) {
1015 ensureNoAnnotation(method);
1016 continue;
1017 }
1018
1019
1020 String propName = getPropertyNameFromGetMethod(name);
1021 if(propName!=null && arity==0) {
1022 getters.put(propName,method);
1023 used = true;
1024 }
1025
1026
1027 propName = getPropertyNameFromSetMethod(name);
1028 if(propName!=null && arity==1) {
1029 List<M> propSetters = allSetters.get(propName);
1030 if(null == propSetters){
1031 propSetters = new ArrayList<M>();
1032 allSetters.put(propName, propSetters);
1033 }
1034 propSetters.add(method);
1035 used = true;
1036 }
1037
1038 if(!used)
1039 ensureNoAnnotation(method);
1040 }
1041
1042
1043 for (Map.Entry<String,M> entry : getters.entrySet()) {
1044 String propName = entry.getKey();
1045 M getter = entry.getValue();
1046 List<M> propSetters = allSetters.remove(propName);
1047 if (null == propSetters) {
1048
1049 continue;
1050 }
1051 T getterType = nav().getReturnType(getter);
1052 for (M setter : propSetters) {
1053 T setterType = nav().getMethodParameters(setter)[0];
1054 if (nav().isSameType(setterType, getterType)) {
1055 setters.put(propName, setter);
1056 break;
1057 }
1058 }
1059 }
1060
1061
1062 for (Map.Entry<String,List<M>> e : allSetters.entrySet()) {
1063 setters.put(e.getKey(),e.getValue().get(0));
1064 }
1065 }
1066
1067
1070 private boolean shouldRecurseSuperClass(C sc) {
1071 return sc!=null
1072 && (builder.isReplaced(sc) || reader().hasClassAnnotation(sc, XmlTransient.class));
1073 }
1074
1075
1078 private boolean isConsideredPublic(M m) {
1079 return m ==null || nav().isPublicMethod(m);
1080 }
1081
1082
1086 private void resurrect(Map<String, M> methods, Set<String> complete) {
1087 for (Map.Entry<String, M> e : methods.entrySet()) {
1088 if(complete.contains(e.getKey()))
1089 continue;
1090 if(hasJAXBAnnotation(reader().getAllMethodAnnotations(e.getValue(),this)))
1091 complete.add(e.getKey());
1092 }
1093 }
1094
1095
1099 private void ensureNoAnnotation(M method) {
1100 Annotation[] annotations = reader().getAllMethodAnnotations(method,this);
1101 for( Annotation a : annotations ) {
1102 if(isJAXBAnnotation(a)) {
1103 builder.reportError(new IllegalAnnotationException(
1104 Messages.ANNOTATION_ON_WRONG_METHOD.format(),
1105 a));
1106 return;
1107 }
1108 }
1109 }
1110
1111
1114 private static boolean isJAXBAnnotation(Annotation a) {
1115 return ANNOTATION_NUMBER_MAP.containsKey(a.annotationType());
1116 }
1117
1118
1121 private static boolean hasJAXBAnnotation(Annotation[] annotations) {
1122 return getSomeJAXBAnnotation(annotations)!=null;
1123 }
1124
1125 private static Annotation getSomeJAXBAnnotation(Annotation[] annotations) {
1126 for( Annotation a : annotations )
1127 if(isJAXBAnnotation(a))
1128 return a;
1129 return null;
1130 }
1131
1132
1133
1139 private static String getPropertyNameFromGetMethod(String name) {
1140 if(name.startsWith("get") && name.length()>3)
1141 return name.substring(3);
1142 if(name.startsWith("is") && name.length()>2)
1143 return name.substring(2);
1144 return null;
1145 }
1146
1147
1153 private static String getPropertyNameFromSetMethod(String name) {
1154 if(name.startsWith("set") && name.length()>3)
1155 return name.substring(3);
1156 return null;
1157 }
1158
1159
1165 protected PropertySeed<T,C,F,M> createFieldSeed(F f) {
1166 return new FieldPropertySeed<T,C,F,M>(this, f);
1167 }
1168
1169
1172 protected PropertySeed<T,C,F,M> createAccessorSeed(M getter, M setter) {
1173 return new GetterSetterPropertySeed<T,C,F,M>(this, getter,setter);
1174 }
1175
1176 public final boolean isElement() {
1177 return elementName!=null;
1178 }
1179
1180 public boolean isAbstract() {
1181 return nav().isAbstract(clazz);
1182 }
1183
1184 public boolean isOrdered() {
1185 return propOrder!=null;
1186 }
1187
1188 public final boolean isFinal() {
1189 return nav().isFinal(clazz);
1190 }
1191
1192 public final boolean hasSubClasses() {
1193 return hasSubClasses;
1194 }
1195
1196 public final boolean hasAttributeWildcard() {
1197 return declaresAttributeWildcard() || inheritsAttributeWildcard();
1198 }
1199
1200 public final boolean inheritsAttributeWildcard() {
1201 return getInheritedAttributeWildcard()!=null;
1202 }
1203
1204 public final boolean declaresAttributeWildcard() {
1205 return attributeWildcard!=null;
1206 }
1207
1208
1211 private PropertySeed<T,C,F,M> getInheritedAttributeWildcard() {
1212 for( ClassInfoImpl<T,C,F,M> c=getBaseClass(); c!=null; c=c.getBaseClass() )
1213 if(c.attributeWildcard!=null)
1214 return c.attributeWildcard;
1215 return null;
1216 }
1217
1218 public final QName getElementName() {
1219 return elementName;
1220 }
1221
1222 public final QName getTypeName() {
1223 return typeName;
1224 }
1225
1226 public final boolean isSimpleType() {
1227 List<? extends PropertyInfo> props = getProperties();
1228 if(props.size()!=1) return false;
1229 return props.get(0).kind()==PropertyKind.VALUE;
1230 }
1231
1232
1235 @Override
1236 void link() {
1237 getProperties();
1238
1239
1240 Map<String,PropertyInfoImpl> names = new HashMap<String,PropertyInfoImpl>();
1241 for( PropertyInfoImpl<T,C,F,M> p : properties ) {
1242 p.link();
1243 PropertyInfoImpl old = names.put(p.getName(),p);
1244 if(old!=null) {
1245 builder.reportError(new IllegalAnnotationException(
1246 Messages.PROPERTY_COLLISION.format(p.getName()),
1247 p, old ));
1248 }
1249 }
1250 super.link();
1251 }
1252
1253 public Location getLocation() {
1254 return nav().getClassLocation(clazz);
1255 }
1256
1257
1265 private boolean hasFactoryConstructor(XmlType t){
1266 if (t == null) return false;
1267
1268 String method = t.factoryMethod();
1269 T fClass = reader().getClassValue(t, "factoryClass");
1270 if (method.length() > 0){
1271 if(nav().isSameType(fClass, nav().ref(XmlType.DEFAULT.class))){
1272 fClass = nav().use(clazz);
1273 }
1274 for(M m: nav().getDeclaredMethods(nav().asDecl(fClass))){
1275
1276 if (nav().getMethodName(m).equals(method) &&
1277 nav().isSameType(nav().getReturnType(m), nav().use(clazz)) &&
1278 nav().getMethodParameters(m).length == 0 &&
1279 nav().isStaticMethod(m)){
1280 factoryMethod = m;
1281 break;
1282 }
1283 }
1284 if (factoryMethod == null){
1285 builder.reportError(new IllegalAnnotationException(
1286 Messages.NO_FACTORY_METHOD.format(nav().getClassName(nav().asDecl(fClass)), method), this ));
1287 }
1288 } else if(!nav().isSameType(fClass, nav().ref(XmlType.DEFAULT.class))){
1289 builder.reportError(new IllegalAnnotationException(
1290 Messages.FACTORY_CLASS_NEEDS_FACTORY_METHOD.format(nav().getClassName(nav().asDecl(fClass))), this ));
1291 }
1292 return factoryMethod != null;
1293 }
1294
1295 public Method getFactoryMethod(){
1296 return (Method) factoryMethod;
1297 }
1298
1299 @Override
1300 public String toString() {
1301 return "ClassInfo("+clazz+')';
1302 }
1303
1304 private static final String[] DEFAULT_ORDER = new String[0];
1305 }
1306