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, 2012, 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.Collections;
29 import java.util.LinkedHashSet;
30 import java.util.Set;
32 import javax.xml.bind.JAXBElement;
33 import javax.xml.bind.annotation.XmlAnyElement;
34 import javax.xml.bind.annotation.XmlElementRef;
35 import javax.xml.bind.annotation.XmlElementRefs;
36 import javax.xml.bind.annotation.XmlMixed;
37 import javax.xml.bind.annotation.XmlSchema;
38 import javax.xml.bind.annotation.XmlNsForm;
39 import javax.xml.namespace.QName;
41 import com.sun.xml.internal.bind.v2.model.annotation.AnnotationReader;
42 import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
43 import com.sun.xml.internal.bind.v2.model.core.Element;
44 import com.sun.xml.internal.bind.v2.model.core.ElementInfo;
45 import com.sun.xml.internal.bind.v2.model.core.NonElement;
46 import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
47 import com.sun.xml.internal.bind.v2.model.core.ReferencePropertyInfo;
48 import com.sun.xml.internal.bind.v2.model.core.WildcardMode;
49 import com.sun.xml.internal.bind.v2.model.nav.Navigator;
50 import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
51 import java.util.Iterator;
53 /**
54 * Implementation of {@link ReferencePropertyInfo}.
55 *
56 * @author Kohsuke Kawaguchi
57 */
58 class ReferencePropertyInfoImpl<T,C,F,M>
59 extends ERPropertyInfoImpl<T,C,F,M>
60 implements ReferencePropertyInfo<T,C>, DummyPropertyInfo<T, C, F, M>
61 {
62 /**
63 * Lazily computed.
64 * @see #getElements()
65 */
66 private Set<Element<T,C>> types;
67 private Set<ReferencePropertyInfoImpl<T,C,F,M>> subTypes = new LinkedHashSet<ReferencePropertyInfoImpl<T,C,F,M>>();
69 private final boolean isMixed;
71 private final WildcardMode wildcard;
72 private final C domHandler;
73 /**
74 * Lazily computed.
75 * @see #isRequired()
76 */
77 private Boolean isRequired;
79 public ReferencePropertyInfoImpl(
80 ClassInfoImpl<T,C,F,M> classInfo,
81 PropertySeed<T,C,F,M> seed) {
83 super(classInfo, seed);
85 isMixed = seed.readAnnotation(XmlMixed.class) != null;
87 XmlAnyElement xae = seed.readAnnotation(XmlAnyElement.class);
88 if(xae==null) {
89 wildcard = null;
90 domHandler = null;
91 } else {
92 wildcard = xae.lax()?WildcardMode.LAX:WildcardMode.SKIP;
93 domHandler = nav().asDecl(reader().getClassValue(xae,"value"));
94 }
95 }
97 public Set<? extends Element<T,C>> ref() {
98 return getElements();
99 }
101 public PropertyKind kind() {
102 return PropertyKind.REFERENCE;
103 }
105 public Set<? extends Element<T,C>> getElements() {
106 if(types==null)
107 calcTypes(false);
108 assert types!=null;
109 return types;
110 }
112 /**
113 * Compute {@link #types}.
114 *
115 * @param last
116 * if true, every {@link XmlElementRef} must yield at least one type.
117 */
118 private void calcTypes(boolean last) {
119 XmlElementRef[] ann;
120 types = new LinkedHashSet<Element<T,C>>();
121 XmlElementRefs refs = seed.readAnnotation(XmlElementRefs.class);
122 XmlElementRef ref = seed.readAnnotation(XmlElementRef.class);
124 if(refs!=null && ref!=null) {
125 parent.builder.reportError(new IllegalAnnotationException(
126 Messages.MUTUALLY_EXCLUSIVE_ANNOTATIONS.format(
127 nav().getClassName(parent.getClazz())+'#'+seed.getName(),
128 ref.annotationType().getName(), refs.annotationType().getName()),
129 ref, refs ));
130 }
132 if(refs!=null)
133 ann = refs.value();
134 else {
135 if(ref!=null)
136 ann = new XmlElementRef[]{ref};
137 else
138 ann = null;
139 }
141 isRequired = !isCollection(); // this is by default, to remain compatible with 2.1
143 if(ann!=null) {
144 Navigator<T,C,F,M> nav = nav();
145 AnnotationReader<T,C,F,M> reader = reader();
147 final T defaultType = nav.ref(XmlElementRef.DEFAULT.class);
148 final C je = nav.asDecl(JAXBElement.class);
150 for( XmlElementRef r : ann ) {
151 boolean yield;
152 T type = reader.getClassValue(r,"type");
153 if(nav().isSameType(type, defaultType))
154 type = nav.erasure(getIndividualType());
155 if(nav.getBaseClass(type,je)!=null)
156 yield = addGenericElement(r);
157 else
158 yield = addAllSubtypes(type);
160 // essentially "isRequired &= isRequired(r)" except that we'd like to skip evaluating isRequird(r)
161 // if the value is already false.
162 if(isRequired && !isRequired(r))
163 isRequired = false;
165 if(last && !yield) {
166 // a reference didn't produce any type.
167 // diagnose the problem
168 if(nav().isSameType(type, nav.ref(JAXBElement.class))) {
169 // no XmlElementDecl
170 parent.builder.reportError(new IllegalAnnotationException(
171 Messages.NO_XML_ELEMENT_DECL.format(
172 getEffectiveNamespaceFor(r), r.name()),
173 this
174 ));
175 } else {
176 parent.builder.reportError(new IllegalAnnotationException(
177 Messages.INVALID_XML_ELEMENT_REF.format(type),this));
178 }
180 // reporting one error would do.
181 // often the element ref field is using @XmlElementRefs
182 // to point to multiple JAXBElements.
183 // reporting one error for each @XmlElemetnRef is thus often redundant.
184 return;
185 }
186 }
187 }
189 for (ReferencePropertyInfoImpl<T, C, F, M> info : subTypes) {
190 PropertySeed sd = info.seed;
191 refs = sd.readAnnotation(XmlElementRefs.class);
192 ref = sd.readAnnotation(XmlElementRef.class);
194 if (refs != null && ref != null) {
195 parent.builder.reportError(new IllegalAnnotationException(
196 Messages.MUTUALLY_EXCLUSIVE_ANNOTATIONS.format(
197 nav().getClassName(parent.getClazz())+'#'+seed.getName(),
198 ref.annotationType().getName(), refs.annotationType().getName()),
199 ref, refs ));
200 }
202 if (refs != null) {
203 ann = refs.value();
204 } else {
205 if (ref != null) {
206 ann = new XmlElementRef[]{ref};
207 } else {
208 ann = null;
209 }
210 }
212 if (ann != null) {
213 Navigator<T,C,F,M> nav = nav();
214 AnnotationReader<T,C,F,M> reader = reader();
216 final T defaultType = nav.ref(XmlElementRef.DEFAULT.class);
217 final C je = nav.asDecl(JAXBElement.class);
219 for( XmlElementRef r : ann ) {
220 boolean yield;
221 T type = reader.getClassValue(r,"type");
222 if (nav().isSameType(type, defaultType)) {
223 type = nav.erasure(getIndividualType());
224 }
225 if (nav.getBaseClass(type,je) != null) {
226 yield = addGenericElement(r, info);
228 } else {
229 yield = addAllSubtypes(type);
230 }
232 if(last && !yield) {
233 // a reference didn't produce any type.
234 // diagnose the problem
235 if(nav().isSameType(type, nav.ref(JAXBElement.class))) {
236 // no XmlElementDecl
237 parent.builder.reportError(new IllegalAnnotationException(
238 Messages.NO_XML_ELEMENT_DECL.format(
239 getEffectiveNamespaceFor(r), r.name()),
240 this
241 ));
242 } else {
243 parent.builder.reportError(new IllegalAnnotationException(
244 Messages.INVALID_XML_ELEMENT_REF.format(),this));
245 }
247 // reporting one error would do.
248 // often the element ref field is using @XmlElementRefs
249 // to point to multiple JAXBElements.
250 // reporting one error for each @XmlElemetnRef is thus often redundant.
251 return;
252 }
253 }
254 }
255 }
257 types = Collections.unmodifiableSet(types);
258 }
260 public boolean isRequired() {
261 if(isRequired==null)
262 calcTypes(false);
263 return isRequired;
264 }
266 /**
267 * If we find out that we are working with 2.1 API, remember the fact so that
268 * we don't waste time generating exceptions every time we call {@link #isRequired(XmlElementRef)}.
269 */
270 private static boolean is2_2 = true;
272 /**
273 * Reads the value of {@code XmlElementRef.required()}.
274 *
275 * If we are working as 2.1 RI, this defaults to true.
276 */
277 private boolean isRequired(XmlElementRef ref) {
278 if(!is2_2) return true;
280 try {
281 return ref.required();
282 } catch(LinkageError e) {
283 is2_2 = false;
284 return true; // the value defaults to true
285 }
286 }
288 /**
289 * @return
290 * true if the reference yields at least one type
291 */
292 private boolean addGenericElement(XmlElementRef r) {
293 String nsUri = getEffectiveNamespaceFor(r);
294 // TODO: check spec. defaulting of localName.
295 return addGenericElement(parent.owner.getElementInfo(parent.getClazz(),new QName(nsUri,r.name())));
296 }
298 private boolean addGenericElement(XmlElementRef r, ReferencePropertyInfoImpl<T,C,F,M> info) {
299 String nsUri = info.getEffectiveNamespaceFor(r);
300 ElementInfo ei = parent.owner.getElementInfo(info.parent.getClazz(), new QName(nsUri, r.name()));
301 types.add(ei);
302 return true;
303 }
305 private String getEffectiveNamespaceFor(XmlElementRef r) {
306 String nsUri = r.namespace();
308 XmlSchema xs = reader().getPackageAnnotation( XmlSchema.class, parent.getClazz(), this );
309 if(xs!=null && xs.attributeFormDefault()== XmlNsForm.QUALIFIED) {
310 // JAX-RPC doesn't want the default namespace URI swapping to take effect to
311 // local "unqualified" elements. UGLY.
312 if(nsUri.length()==0)
313 nsUri = parent.builder.defaultNsUri;
314 }
316 return nsUri;
317 }
319 private boolean addGenericElement(ElementInfo<T,C> ei) {
320 if(ei==null)
321 return false;
322 types.add(ei);
323 for( ElementInfo<T,C> subst : ei.getSubstitutionMembers() )
324 addGenericElement(subst);
325 return true;
326 }
328 private boolean addAllSubtypes(T type) {
329 Navigator<T,C,F,M> nav = nav();
331 // this allows the explicitly referenced type to be sucked in to the model
332 NonElement<T,C> t = parent.builder.getClassInfo(nav.asDecl(type),this);
333 if(!(t instanceof ClassInfo))
334 // this is leaf.
335 return false;
337 boolean result = false;
339 ClassInfo<T,C> c = (ClassInfo<T,C>) t;
340 if(c.isElement()) {
341 types.add(c.asElement());
342 result = true;
343 }
345 // look for other possible types
346 for( ClassInfo<T,C> ci : parent.owner.beans().values() ) {
347 if(ci.isElement() && nav.isSubClassOf(ci.getType(),type)) {
348 types.add(ci.asElement());
349 result = true;
350 }
351 }
353 // don't allow local elements to substitute.
354 for( ElementInfo<T,C> ei : parent.owner.getElementMappings(null).values()) {
355 if(nav.isSubClassOf(ei.getType(),type)) {
356 types.add(ei);
357 result = true;
358 }
359 }
361 return result;
362 }
365 @Override
366 protected void link() {
367 super.link();
369 // until we get the whole thing into TypeInfoSet,
370 // we never really know what are all the possible types that can be assigned on this field.
371 // so recompute this value when we have all the information.
372 calcTypes(true);
374 }
376 public final void addType(PropertyInfoImpl<T,C,F,M> info) {
377 //noinspection unchecked
378 subTypes.add((ReferencePropertyInfoImpl)info);
379 }
381 public final boolean isMixed() {
382 return isMixed;
383 }
385 public final WildcardMode getWildcard() {
386 return wildcard;
387 }
389 public final C getDOMHandler() {
390 return domHandler;
391 }
392 }