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
1 /*
2 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.xml.internal.bind.v2.model.impl;
28 import java.util.Collection;
29 import java.lang.annotation.Annotation;
31 import javax.activation.MimeType;
32 import javax.xml.bind.annotation.XmlAttachmentRef;
33 import javax.xml.bind.annotation.XmlElement;
34 import javax.xml.bind.annotation.XmlElementWrapper;
35 import javax.xml.bind.annotation.XmlID;
36 import javax.xml.bind.annotation.XmlIDREF;
37 import javax.xml.bind.annotation.XmlInlineBinaryData;
38 import javax.xml.bind.annotation.XmlMimeType;
39 import javax.xml.bind.annotation.XmlSchema;
40 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
41 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
42 import javax.xml.bind.annotation.adapters.XmlAdapter;
43 import javax.xml.namespace.QName;
45 import com.sun.xml.internal.bind.v2.TODO;
46 import com.sun.xml.internal.bind.v2.model.annotation.AnnotationReader;
47 import com.sun.xml.internal.bind.v2.model.annotation.Locatable;
48 import com.sun.xml.internal.bind.v2.model.core.Adapter;
49 import com.sun.xml.internal.bind.v2.model.core.ID;
50 import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
51 import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
52 import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
53 import com.sun.xml.internal.bind.v2.model.nav.Navigator;
54 import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
55 import com.sun.xml.internal.bind.v2.runtime.Location;
56 import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter;
58 /**
59 * Default partial implementation for {@link PropertyInfo}.
60 *
61 * @author Kohsuke Kawaguchi
62 */
63 abstract class PropertyInfoImpl<T,C,F,M>
64 implements PropertyInfo<T,C>, Locatable, Comparable<PropertyInfoImpl> /*by their names*/ {
66 /**
67 * Object that reads annotations.
68 */
69 protected final PropertySeed<T,C,F,M> seed;
71 private final boolean isCollection;
73 private final ID id;
75 private final MimeType expectedMimeType;
76 private final boolean inlineBinary;
77 private final QName schemaType;
79 protected final ClassInfoImpl<T,C,F,M> parent;
81 private final Adapter<T,C> adapter;
83 protected PropertyInfoImpl(ClassInfoImpl<T,C,F,M> parent, PropertySeed<T,C,F,M> spi) {
84 this.seed = spi;
85 this.parent = parent;
87 if(parent==null)
88 /*
89 Various people reported a bug where this parameter is somehow null.
90 In an attempt to catch the error better, let's do an explicit check here.
92 http://forums.java.net/jive/thread.jspa?threadID=18479
93 http://forums.java.net/jive/thread.jspa?messageID=165946
94 */
95 throw new AssertionError();
97 MimeType mt = Util.calcExpectedMediaType(seed,parent.builder);
98 if(mt!=null && !kind().canHaveXmlMimeType) {
99 parent.builder.reportError(new IllegalAnnotationException(
100 Messages.ILLEGAL_ANNOTATION.format(XmlMimeType.class.getName()),
101 seed.readAnnotation(XmlMimeType.class)
102 ));
103 mt = null;
104 }
105 this.expectedMimeType = mt;
106 this.inlineBinary = seed.hasAnnotation(XmlInlineBinaryData.class);
108 T t = seed.getRawType();
110 // check if there's an adapter applicable to the whole property
111 XmlJavaTypeAdapter xjta = getApplicableAdapter(t);
112 if(xjta!=null) {
113 isCollection = false;
114 adapter = new Adapter<T,C>(xjta,reader(),nav());
115 } else {
116 // check if the adapter is applicable to the individual item in the property
118 this.isCollection = nav().isSubClassOf(t, nav().ref(Collection.class))
119 || nav().isArrayButNotByteArray(t);
121 xjta = getApplicableAdapter(getIndividualType());
122 if(xjta==null) {
123 // ugly ugly hack, but we implement swaRef as adapter
124 XmlAttachmentRef xsa = seed.readAnnotation(XmlAttachmentRef.class);
125 if(xsa!=null) {
126 parent.builder.hasSwaRef = true;
127 adapter = new Adapter<T,C>(nav().asDecl(SwaRefAdapter.class),nav());
128 } else {
129 adapter = null;
131 // if this field has adapter annotation but not applicable,
132 // that must be an error of the user
133 xjta = seed.readAnnotation(XmlJavaTypeAdapter.class);
134 if(xjta!=null) {
135 T ad = reader().getClassValue(xjta,"value");
136 parent.builder.reportError(new IllegalAnnotationException(
137 Messages.UNMATCHABLE_ADAPTER.format(
138 nav().getTypeName(ad), nav().getTypeName(t)),
139 xjta
140 ));
141 }
142 }
143 } else {
144 adapter = new Adapter<T,C>(xjta,reader(),nav());
145 }
146 }
148 this.id = calcId();
149 this.schemaType = Util.calcSchemaType(reader(),seed,parent.clazz,
150 getIndividualType(),this);
151 }
154 public ClassInfoImpl<T,C,F,M> parent() {
155 return parent;
156 }
158 protected final Navigator<T,C,F,M> nav() {
159 return parent.nav();
160 }
161 protected final AnnotationReader<T,C,F,M> reader() {
162 return parent.reader();
163 }
165 public T getRawType() {
166 return seed.getRawType();
167 }
169 public T getIndividualType() {
170 if(adapter!=null)
171 return adapter.defaultType;
172 T raw = getRawType();
173 if(!isCollection()) {
174 return raw;
175 } else {
176 if(nav().isArrayButNotByteArray(raw))
177 return nav().getComponentType(raw);
179 T bt = nav().getBaseClass(raw, nav().asDecl(Collection.class) );
180 if(nav().isParameterizedType(bt))
181 return nav().getTypeArgument(bt,0);
182 else
183 return nav().ref(Object.class);
184 }
185 }
187 public final String getName() {
188 return seed.getName();
189 }
191 /**
192 * Checks if the given adapter is applicable to the declared property type.
193 */
194 private boolean isApplicable(XmlJavaTypeAdapter jta, T declaredType ) {
195 if(jta==null) return false;
197 T type = reader().getClassValue(jta,"type");
198 if(nav().isSameType(declaredType, type))
199 return true; // for types explicitly marked in XmlJavaTypeAdapter.type()
201 T ad = reader().getClassValue(jta,"value");
202 T ba = nav().getBaseClass(ad, nav().asDecl(XmlAdapter.class));
203 if(!nav().isParameterizedType(ba))
204 return true; // can't check type applicability. assume Object, which means applicable to any.
205 T inMemType = nav().getTypeArgument(ba, 1);
207 return nav().isSubClassOf(declaredType,inMemType);
208 }
210 private XmlJavaTypeAdapter getApplicableAdapter(T type) {
211 XmlJavaTypeAdapter jta = seed.readAnnotation(XmlJavaTypeAdapter.class);
212 if(jta!=null && isApplicable(jta,type))
213 return jta;
215 // check the applicable adapters on the package
216 XmlJavaTypeAdapters jtas = reader().getPackageAnnotation(XmlJavaTypeAdapters.class, parent.clazz, seed );
217 if(jtas!=null) {
218 for (XmlJavaTypeAdapter xjta : jtas.value()) {
219 if(isApplicable(xjta,type))
220 return xjta;
221 }
222 }
223 jta = reader().getPackageAnnotation(XmlJavaTypeAdapter.class, parent.clazz, seed );
224 if(isApplicable(jta,type))
225 return jta;
227 // then on the target class
228 C refType = nav().asDecl(type);
229 if(refType!=null) {
230 jta = reader().getClassAnnotation(XmlJavaTypeAdapter.class, refType, seed );
231 if(jta!=null && isApplicable(jta,type)) // the one on the type always apply.
232 return jta;
233 }
235 return null;
236 }
238 /**
239 * This is the default implementation of the getAdapter method
240 * defined on many of the {@link PropertyInfo}-derived classes.
241 */
242 public Adapter<T,C> getAdapter() {
243 return adapter;
244 }
247 public final String displayName() {
248 return nav().getClassName(parent.getClazz())+'#'+getName();
249 }
251 public final ID id() {
252 return id;
253 }
255 private ID calcId() {
256 if(seed.hasAnnotation(XmlID.class)) {
257 // check the type
258 if(!nav().isSameType(getIndividualType(), nav().ref(String.class)))
259 parent.builder.reportError(new IllegalAnnotationException(
260 Messages.ID_MUST_BE_STRING.format(getName()), seed )
261 );
262 return ID.ID;
263 } else
264 if(seed.hasAnnotation(XmlIDREF.class)) {
265 return ID.IDREF;
266 } else {
267 return ID.NONE;
268 }
269 }
271 public final MimeType getExpectedMimeType() {
272 return expectedMimeType;
273 }
275 public final boolean inlineBinaryData() {
276 return inlineBinary;
277 }
279 public final QName getSchemaType() {
280 return schemaType;
281 }
283 public final boolean isCollection() {
284 return isCollection;
285 }
287 /**
288 * Called after all the {@link TypeInfo}s are collected into the governing {@link TypeInfoSet}.
289 *
290 * Derived class can do additional actions to complete the model.
291 */
292 protected void link() {
293 if(id==ID.IDREF) {
294 // make sure that the refereced type has ID
295 for (TypeInfo<T,C> ti : ref()) {
296 if(!ti.canBeReferencedByIDREF())
297 parent.builder.reportError(new IllegalAnnotationException(
298 Messages.INVALID_IDREF.format(
299 parent.builder.nav.getTypeName(ti.getType())), this ));
300 }
301 }
302 }
304 /**
305 * A {@link PropertyInfoImpl} is always referenced by its enclosing class,
306 * so return that as the upstream.
307 */
308 public Locatable getUpstream() {
309 return parent;
310 }
312 public Location getLocation() {
313 return seed.getLocation();
314 }
317 //
318 //
319 // convenience methods for derived classes
320 //
321 //
324 /**
325 * Computes the tag name from a {@link XmlElement} by taking the defaulting into account.
326 */
327 protected final QName calcXmlName(XmlElement e) {
328 if(e!=null)
329 return calcXmlName(e.namespace(),e.name());
330 else
331 return calcXmlName("##default","##default");
332 }
334 /**
335 * Computes the tag name from a {@link XmlElementWrapper} by taking the defaulting into account.
336 */
337 protected final QName calcXmlName(XmlElementWrapper e) {
338 if(e!=null)
339 return calcXmlName(e.namespace(),e.name());
340 else
341 return calcXmlName("##default","##default");
342 }
344 private QName calcXmlName(String uri,String local) {
345 // compute the default
346 TODO.checkSpec();
347 if(local.length()==0 || local.equals("##default"))
348 local = seed.getName();
349 if(uri.equals("##default")) {
350 XmlSchema xs = reader().getPackageAnnotation( XmlSchema.class, parent.getClazz(), this );
351 // JAX-RPC doesn't want the default namespace URI swapping to take effect to
352 // local "unqualified" elements. UGLY.
353 if(xs!=null) {
354 switch(xs.elementFormDefault()) {
355 case QUALIFIED:
356 QName typeName = parent.getTypeName();
357 if(typeName!=null)
358 uri = typeName.getNamespaceURI();
359 else
360 uri = xs.namespace();
361 if(uri.length()==0)
362 uri = parent.builder.defaultNsUri;
363 break;
364 case UNQUALIFIED:
365 case UNSET:
366 uri = "";
367 }
368 } else {
369 uri = "";
370 }
371 }
372 return new QName(uri.intern(),local.intern());
373 }
375 public int compareTo(PropertyInfoImpl that) {
376 return this.getName().compareTo(that.getName());
377 }
379 public final <A extends Annotation> A readAnnotation(Class<A> annotationType) {
380 return seed.readAnnotation(annotationType);
381 }
383 public final boolean hasAnnotation(Class<? extends Annotation> annotationType) {
384 return seed.hasAnnotation(annotationType);
385 }
386 }