Fri, 24 Oct 2014 15:02:28 +0200
8054367: More references for endpoints
Summary: fix also reviewed by Iaroslav.Savytskyi@oracle.com, Alexander.Fomin@oracle.com
Reviewed-by: mullan, skoivu
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 | } |