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

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

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

mercurial