Thu, 31 Aug 2017 15:18:52 +0800
merge
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.lang.reflect.ParameterizedType;
29 import java.lang.reflect.Type;
30 import java.util.HashMap;
31 import java.util.Map;
32 import java.util.logging.Level;
33 import java.util.logging.Logger;
35 import javax.xml.bind.JAXBElement;
36 import javax.xml.bind.annotation.XmlAttachmentRef;
37 import javax.xml.bind.annotation.XmlRegistry;
38 import javax.xml.bind.annotation.XmlSchema;
39 import javax.xml.bind.annotation.XmlSeeAlso;
40 import javax.xml.bind.annotation.XmlTransient;
41 import javax.xml.namespace.QName;
43 import com.sun.xml.internal.bind.util.Which;
44 import com.sun.xml.internal.bind.v2.model.annotation.AnnotationReader;
45 import com.sun.xml.internal.bind.v2.model.annotation.ClassLocatable;
46 import com.sun.xml.internal.bind.v2.model.annotation.Locatable;
47 import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
48 import com.sun.xml.internal.bind.v2.model.core.ErrorHandler;
49 import com.sun.xml.internal.bind.v2.model.core.LeafInfo;
50 import com.sun.xml.internal.bind.v2.model.core.NonElement;
51 import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
52 import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
53 import com.sun.xml.internal.bind.v2.model.core.Ref;
54 import com.sun.xml.internal.bind.v2.model.core.RegistryInfo;
55 import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
56 import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
57 import com.sun.xml.internal.bind.v2.model.nav.Navigator;
58 import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo;
59 import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
60 import com.sun.xml.internal.bind.WhiteSpaceProcessor;
62 /**
63 * Builds a {@link TypeInfoSet} (a set of JAXB properties)
64 * by using {@link ElementInfoImpl} and {@link ClassInfoImpl}.
65 * from annotated Java classes.
66 *
67 * <p>
68 * This class uses {@link Navigator} and {@link AnnotationReader} to
69 * work with arbitrary annotation source and arbitrary Java model.
70 * For this purpose this class is parameterized.
71 *
72 * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
73 */
74 public class ModelBuilder<T,C,F,M> implements ModelBuilderI<T,C,F,M> {
75 private static final Logger logger;
77 /**
78 * {@link TypeInfo}s that are built will go into this set.
79 */
80 final TypeInfoSetImpl<T,C,F,M> typeInfoSet;
82 public final AnnotationReader<T,C,F,M> reader;
84 public final Navigator<T,C,F,M> nav;
86 /**
87 * Used to detect collisions among global type names.
88 */
89 private final Map<QName,TypeInfo> typeNames = new HashMap<QName,TypeInfo>();
91 /**
92 * JAXB doesn't want to use namespaces unless we are told to, but WS-I BP
93 * conformace requires JAX-RPC to always use a non-empty namespace URI.
94 * (see http://www.ws-i.org/Profiles/BasicProfile-1.0-2004-04-16.html#WSDLTYPES R2105)
95 *
96 * <p>
97 * To work around this issue, we allow the use of the empty namespaces to be
98 * replaced by a particular designated namespace URI.
99 *
100 * <p>
101 * This field keeps the value of that replacing namespace URI.
102 * When there's no replacement, this field is set to "".
103 */
104 public final String defaultNsUri;
107 /**
108 * Packages whose registries are already added.
109 */
110 /*package*/ final Map<String,RegistryInfoImpl<T,C,F,M>> registries
111 = new HashMap<String,RegistryInfoImpl<T,C,F,M>>();
113 private final Map<C,C> subclassReplacements;
115 /**
116 * @see #setErrorHandler
117 */
118 private ErrorHandler errorHandler;
119 private boolean hadError;
121 /**
122 * Set to true if the model includes {@link XmlAttachmentRef}. JAX-WS
123 * needs to know this information.
124 */
125 public boolean hasSwaRef;
127 private final ErrorHandler proxyErrorHandler = new ErrorHandler() {
128 public void error(IllegalAnnotationException e) {
129 reportError(e);
130 }
131 };
133 public ModelBuilder(
134 AnnotationReader<T, C, F, M> reader,
135 Navigator<T, C, F, M> navigator,
136 Map<C, C> subclassReplacements,
137 String defaultNamespaceRemap
138 ) {
140 this.reader = reader;
141 this.nav = navigator;
142 this.subclassReplacements = subclassReplacements;
143 if(defaultNamespaceRemap==null)
144 defaultNamespaceRemap = "";
145 this.defaultNsUri = defaultNamespaceRemap;
146 reader.setErrorHandler(proxyErrorHandler);
147 typeInfoSet = createTypeInfoSet();
148 }
150 /**
151 * Makes sure that we are running with 2.1 JAXB API,
152 * and report an error if not.
153 */
154 static {
155 try {
156 XmlSchema s = null;
157 s.location();
158 } catch (NullPointerException e) {
159 // as epxected
160 } catch (NoSuchMethodError e) {
161 // this is not a 2.1 API. Where is it being loaded from?
162 Messages res;
163 if (SecureLoader.getClassClassLoader(XmlSchema.class) == null) {
164 res = Messages.INCOMPATIBLE_API_VERSION_MUSTANG;
165 } else {
166 res = Messages.INCOMPATIBLE_API_VERSION;
167 }
169 throw new LinkageError( res.format(
170 Which.which(XmlSchema.class),
171 Which.which(ModelBuilder.class)
172 ));
173 }
174 }
176 /**
177 * Makes sure that we don't have conflicting 1.0 runtime,
178 * and report an error if we do.
179 */
180 static {
181 try {
182 WhiteSpaceProcessor.isWhiteSpace("xyz");
183 } catch (NoSuchMethodError e) {
184 // we seem to be getting 1.0 runtime
185 throw new LinkageError( Messages.RUNNING_WITH_1_0_RUNTIME.format(
186 Which.which(WhiteSpaceProcessor.class),
187 Which.which(ModelBuilder.class)
188 ));
189 }
190 }
192 /**
193 * Logger init
194 */
195 static {
196 logger = Logger.getLogger(ModelBuilder.class.getName());
197 }
199 protected TypeInfoSetImpl<T,C,F,M> createTypeInfoSet() {
200 return new TypeInfoSetImpl<T,C,F,M>(nav,reader,BuiltinLeafInfoImpl.createLeaves(nav));
201 }
203 /**
204 * Builds a JAXB {@link ClassInfo} model from a given class declaration
205 * and adds that to this model owner.
206 *
207 * <p>
208 * Return type is either {@link ClassInfo} or {@link LeafInfo} (for types like
209 * {@link String} or {@link Enum}-derived ones)
210 */
211 public NonElement<T,C> getClassInfo( C clazz, Locatable upstream ) {
212 return getClassInfo(clazz,false,upstream);
213 }
215 /**
216 * For limited cases where the caller needs to search for a super class.
217 * This is necessary because we don't want {@link #subclassReplacements}
218 * to kick in for the super class search, which will cause infinite recursion.
219 */
220 public NonElement<T,C> getClassInfo( C clazz, boolean searchForSuperClass, Locatable upstream ) {
221 assert clazz!=null;
222 NonElement<T,C> r = typeInfoSet.getClassInfo(clazz);
223 if(r!=null)
224 return r;
226 if(nav.isEnum(clazz)) {
227 EnumLeafInfoImpl<T,C,F,M> li = createEnumLeafInfo(clazz,upstream);
228 typeInfoSet.add(li);
229 r = li;
230 addTypeName(r);
231 } else {
232 boolean isReplaced = subclassReplacements.containsKey(clazz);
233 if(isReplaced && !searchForSuperClass) {
234 // handle it as if the replacement was specified
235 r = getClassInfo(subclassReplacements.get(clazz),upstream);
236 } else
237 if(reader.hasClassAnnotation(clazz,XmlTransient.class) || isReplaced) {
238 // handle it as if the base class was specified
239 r = getClassInfo( nav.getSuperClass(clazz), searchForSuperClass,
240 new ClassLocatable<C>(upstream,clazz,nav) );
241 } else {
242 ClassInfoImpl<T,C,F,M> ci = createClassInfo(clazz,upstream);
243 typeInfoSet.add(ci);
245 // compute the closure by eagerly expanding references
246 for( PropertyInfo<T,C> p : ci.getProperties() ) {
247 if(p.kind()== PropertyKind.REFERENCE) {
248 // make sure that we have a registry for this package
249 addToRegistry(clazz, (Locatable) p);
250 Class[] prmzdClasses = getParametrizedTypes(p);
251 if (prmzdClasses != null) {
252 for (Class prmzdClass : prmzdClasses) {
253 if (prmzdClass != clazz) {
254 addToRegistry((C) prmzdClass, (Locatable) p);
255 }
256 }
257 }
258 }
260 for( TypeInfo<T,C> t : p.ref() )
261 ; // just compute a reference should be suffice
262 }
263 ci.getBaseClass(); // same as above.
265 r = ci;
266 addTypeName(r);
267 }
268 }
271 // more reference closure expansion. @XmlSeeAlso
272 XmlSeeAlso sa = reader.getClassAnnotation(XmlSeeAlso.class, clazz, upstream);
273 if(sa!=null) {
274 for( T t : reader.getClassArrayValue(sa,"value") ) {
275 getTypeInfo(t,(Locatable)sa);
276 }
277 }
280 return r;
281 }
283 /**
284 * Adding package's ObjectFactory methods to registry
285 * @param clazz which package will be used
286 * @param p location
287 */
288 private void addToRegistry(C clazz, Locatable p) {
289 String pkg = nav.getPackageName(clazz);
290 if (!registries.containsKey(pkg)) {
291 // insert the package's object factory
292 C c = nav.loadObjectFactory(clazz, pkg);
293 if (c != null)
294 addRegistry(c, p);
295 }
296 }
298 /**
299 * Getting parametrized classes of {@code JAXBElement<...>} property
300 * @param p property which parametrized types we will try to get
301 * @return null - if it's not JAXBElement property, or it's not parametrized, and array of parametrized classes in other case
302 */
303 private Class[] getParametrizedTypes(PropertyInfo p) {
304 try {
305 Type pType = ((RuntimePropertyInfo) p).getIndividualType();
306 if (pType instanceof ParameterizedType) {
307 ParameterizedType prmzdType = (ParameterizedType) pType;
308 if (prmzdType.getRawType() == JAXBElement.class) {
309 Type[] actualTypes = prmzdType.getActualTypeArguments();
310 Class[] result = new Class[actualTypes.length];
311 for (int i = 0; i < actualTypes.length; i++) {
312 result[i] = (Class) actualTypes[i];
313 }
314 return result;
315 }
316 }
317 } catch (Exception e) {
318 logger.log(Level.FINE, "Error in ModelBuilder.getParametrizedTypes. " + e.getMessage());
319 }
320 return null;
321 }
323 /**
324 * Checks the uniqueness of the type name.
325 */
326 private void addTypeName(NonElement<T, C> r) {
327 QName t = r.getTypeName();
328 if(t==null) return;
330 TypeInfo old = typeNames.put(t,r);
331 if(old!=null) {
332 // collision
333 reportError(new IllegalAnnotationException(
334 Messages.CONFLICTING_XML_TYPE_MAPPING.format(r.getTypeName()),
335 old, r ));
336 }
337 }
339 /**
340 * Have the builder recognize the type (if it hasn't done so yet),
341 * and returns a {@link NonElement} that represents it.
342 *
343 * @return
344 * always non-null.
345 */
346 public NonElement<T,C> getTypeInfo(T t,Locatable upstream) {
347 NonElement<T,C> r = typeInfoSet.getTypeInfo(t);
348 if(r!=null) return r;
350 if(nav.isArray(t)) { // no need for checking byte[], because above typeInfoset.getTypeInfo() would return non-null
351 ArrayInfoImpl<T,C,F,M> ai =
352 createArrayInfo(upstream, t);
353 addTypeName(ai);
354 typeInfoSet.add(ai);
355 return ai;
356 }
358 C c = nav.asDecl(t);
359 assert c!=null : t.toString()+" must be a leaf, but we failed to recognize it.";
360 return getClassInfo(c,upstream);
361 }
363 /**
364 * This method is used to add a root reference to a model.
365 */
366 public NonElement<T,C> getTypeInfo(Ref<T,C> ref) {
367 // TODO: handle XmlValueList
368 assert !ref.valueList;
369 C c = nav.asDecl(ref.type);
370 if(c!=null && reader.getClassAnnotation(XmlRegistry.class,c,null/*TODO: is this right?*/)!=null) {
371 if(!registries.containsKey(nav.getPackageName(c)))
372 addRegistry(c,null);
373 return null; // TODO: is this correct?
374 } else
375 return getTypeInfo(ref.type,null);
376 }
379 protected EnumLeafInfoImpl<T,C,F,M> createEnumLeafInfo(C clazz,Locatable upstream) {
380 return new EnumLeafInfoImpl<T,C,F,M>(this,upstream,clazz,nav.use(clazz));
381 }
383 protected ClassInfoImpl<T,C,F,M> createClassInfo(C clazz, Locatable upstream ) {
384 return new ClassInfoImpl<T,C,F,M>(this,upstream,clazz);
385 }
387 protected ElementInfoImpl<T,C,F,M> createElementInfo(
388 RegistryInfoImpl<T,C,F,M> registryInfo, M m) throws IllegalAnnotationException {
389 return new ElementInfoImpl<T,C,F,M>(this,registryInfo,m);
390 }
392 protected ArrayInfoImpl<T,C,F,M> createArrayInfo(Locatable upstream, T arrayType) {
393 return new ArrayInfoImpl<T, C, F, M>(this,upstream,arrayType);
394 }
397 /**
398 * Visits a class with {@link XmlRegistry} and records all the element mappings
399 * in it.
400 */
401 public RegistryInfo<T,C> addRegistry(C registryClass, Locatable upstream ) {
402 return new RegistryInfoImpl<T,C,F,M>(this,upstream,registryClass);
403 }
405 /**
406 * Gets a {@link RegistryInfo} for the given package.
407 *
408 * @return
409 * null if no registry exists for the package.
410 * unlike other getXXX methods on this class,
411 * this method is side-effect free.
412 */
413 public RegistryInfo<T,C> getRegistry(String packageName) {
414 return registries.get(packageName);
415 }
417 private boolean linked;
419 /**
420 * Called after all the classes are added to the type set
421 * to "link" them together.
422 *
423 * <p>
424 * Don't expose implementation classes in the signature.
425 *
426 * @return
427 * fully built {@link TypeInfoSet} that represents the model,
428 * or null if there was an error.
429 */
430 public TypeInfoSet<T,C,F,M> link() {
432 assert !linked;
433 linked = true;
435 for( ElementInfoImpl ei : typeInfoSet.getAllElements() )
436 ei.link();
438 for( ClassInfoImpl ci : typeInfoSet.beans().values() )
439 ci.link();
441 for( EnumLeafInfoImpl li : typeInfoSet.enums().values() )
442 li.link();
444 if(hadError)
445 return null;
446 else
447 return typeInfoSet;
448 }
450 //
451 //
452 // error handling
453 //
454 //
456 /**
457 * Sets the error handler that receives errors discovered during the model building.
458 *
459 * @param errorHandler
460 * can be null.
461 */
462 public void setErrorHandler(ErrorHandler errorHandler) {
463 this.errorHandler = errorHandler;
464 }
466 public final void reportError(IllegalAnnotationException e) {
467 hadError = true;
468 if(errorHandler!=null)
469 errorHandler.error(e);
470 }
472 public boolean isReplaced(C sc) {
473 return subclassReplacements.containsKey(sc);
474 }
476 @Override
477 public Navigator<T, C, F, M> getNavigator() {
478 return nav;
479 }
481 @Override
482 public AnnotationReader<T, C, F, M> getReader() {
483 return reader;
484 }
485 }