Tue, 06 Mar 2012 16:09:35 -0800
7150322: Stop using drop source bundles in jaxws
Reviewed-by: darcy, ohrstrom
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.runtime;
28 import java.io.IOException;
29 import java.lang.ref.WeakReference;
30 import java.lang.reflect.Field;
31 import java.lang.reflect.Method;
32 import java.lang.reflect.Type;
33 import java.util.Arrays;
34 import java.util.Collection;
35 import java.util.Collections;
36 import java.util.Comparator;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.LinkedHashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Map.Entry;
43 import java.util.Set;
44 import java.util.TreeSet;
45 import javax.xml.bind.Binder;
46 import javax.xml.bind.JAXBContext;
47 import javax.xml.bind.JAXBElement;
48 import javax.xml.bind.JAXBException;
49 import javax.xml.bind.JAXBIntrospector;
50 import javax.xml.bind.Marshaller;
51 import javax.xml.bind.SchemaOutputResolver;
52 import javax.xml.bind.Unmarshaller;
53 import javax.xml.bind.Validator;
54 import javax.xml.bind.annotation.XmlAttachmentRef;
55 import javax.xml.bind.annotation.XmlList;
56 import javax.xml.bind.annotation.XmlNs;
57 import javax.xml.bind.annotation.XmlSchema;
58 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
59 import javax.xml.namespace.QName;
60 import javax.xml.parsers.DocumentBuilder;
61 import javax.xml.parsers.DocumentBuilderFactory;
62 import javax.xml.parsers.FactoryConfigurationError;
63 import javax.xml.parsers.ParserConfigurationException;
64 import javax.xml.transform.Result;
65 import javax.xml.transform.Transformer;
66 import javax.xml.transform.TransformerConfigurationException;
67 import javax.xml.transform.TransformerFactory;
68 import javax.xml.transform.sax.SAXResult;
69 import javax.xml.transform.sax.SAXTransformerFactory;
70 import javax.xml.transform.sax.TransformerHandler;
72 import com.sun.istack.internal.NotNull;
73 import com.sun.istack.internal.Pool;
74 import com.sun.xml.internal.bind.api.AccessorException;
75 import com.sun.xml.internal.bind.api.Bridge;
76 import com.sun.xml.internal.bind.api.BridgeContext;
77 import com.sun.xml.internal.bind.api.CompositeStructure;
78 import com.sun.xml.internal.bind.api.ErrorListener;
79 import com.sun.xml.internal.bind.api.JAXBRIContext;
80 import com.sun.xml.internal.bind.api.RawAccessor;
81 import com.sun.xml.internal.bind.api.TypeReference;
82 import com.sun.xml.internal.bind.unmarshaller.DOMScanner;
83 import com.sun.xml.internal.bind.util.Which;
84 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
85 import com.sun.xml.internal.bind.v2.model.annotation.RuntimeAnnotationReader;
86 import com.sun.xml.internal.bind.v2.model.annotation.RuntimeInlineAnnotationReader;
87 import com.sun.xml.internal.bind.v2.model.core.Adapter;
88 import com.sun.xml.internal.bind.v2.model.core.NonElement;
89 import com.sun.xml.internal.bind.v2.model.core.Ref;
90 import com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl;
91 import com.sun.xml.internal.bind.v2.model.impl.RuntimeModelBuilder;
92 import com.sun.xml.internal.bind.v2.model.nav.Navigator;
93 import com.sun.xml.internal.bind.v2.model.nav.ReflectionNavigator;
94 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeArrayInfo;
95 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeBuiltinLeafInfo;
96 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo;
97 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElementInfo;
98 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeEnumLeafInfo;
99 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeLeafInfo;
100 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfo;
101 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfoSet;
102 import com.sun.xml.internal.bind.v2.runtime.output.Encoded;
103 import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty;
104 import com.sun.xml.internal.bind.v2.runtime.property.Property;
105 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
106 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
107 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.TagName;
108 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl;
109 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
110 import com.sun.xml.internal.bind.v2.schemagen.XmlSchemaGenerator;
111 import com.sun.xml.internal.bind.v2.util.EditDistance;
112 import com.sun.xml.internal.bind.v2.util.QNameMap;
113 import com.sun.xml.internal.txw2.output.ResultFactory;
115 import org.w3c.dom.Document;
116 import org.w3c.dom.Element;
117 import org.w3c.dom.Node;
118 import org.xml.sax.SAXException;
119 import org.xml.sax.SAXParseException;
120 import org.xml.sax.helpers.DefaultHandler;
122 /**
123 * This class provides the implementation of JAXBContext.
124 *
125 */
126 public final class JAXBContextImpl extends JAXBRIContext {
128 /**
129 * All the bridge classes.
130 */
131 private final Map<TypeReference,Bridge> bridges = new LinkedHashMap<TypeReference,Bridge>();
133 /**
134 * Shared instance of {@link TransformerFactory}.
135 * Lock before use, because a {@link TransformerFactory} is not thread-safe
136 * whereas {@link JAXBContextImpl} is.
137 * Lazily created.
138 */
139 private volatile static SAXTransformerFactory tf;
141 /**
142 * Shared instance of {@link DocumentBuilder}.
143 * Lock before use. Lazily created.
144 */
145 private static DocumentBuilder db;
147 private final QNameMap<JaxBeanInfo> rootMap = new QNameMap<JaxBeanInfo>();
148 private final HashMap<QName,JaxBeanInfo> typeMap = new HashMap<QName,JaxBeanInfo>();
150 /**
151 * Map from JAXB-bound {@link Class} to its {@link JaxBeanInfo}.
152 */
153 private final Map<Class,JaxBeanInfo> beanInfoMap = new LinkedHashMap<Class,JaxBeanInfo>();
155 /**
156 * All created {@link JaxBeanInfo}s.
157 * Updated from each {@link JaxBeanInfo}s constructors to avoid infinite recursion
158 * for a cyclic reference.
159 *
160 * <p>
161 * This map is only used while the {@link JAXBContextImpl} is built and set to null
162 * to avoid keeping references too long.
163 */
164 protected Map<RuntimeTypeInfo,JaxBeanInfo> beanInfos = new LinkedHashMap<RuntimeTypeInfo, JaxBeanInfo>();
166 private final Map<Class/*scope*/,Map<QName,ElementBeanInfoImpl>> elements = new LinkedHashMap<Class, Map<QName, ElementBeanInfoImpl>>();
168 /**
169 * Pool of {@link Marshaller}s.
170 */
171 public final Pool<Marshaller> marshallerPool = new Pool.Impl<Marshaller>() {
172 protected @NotNull Marshaller create() {
173 return createMarshaller();
174 }
175 };
177 public final Pool<Unmarshaller> unmarshallerPool = new Pool.Impl<Unmarshaller>() {
178 protected @NotNull Unmarshaller create() {
179 return createUnmarshaller();
180 }
181 };
183 /**
184 * Used to assign indices to known names in this grammar.
185 * Reset to null once the build phase is completed.
186 */
187 public NameBuilder nameBuilder = new NameBuilder();
189 /**
190 * Keeps the list of known names.
191 * This field is set once the build pahse is completed.
192 */
193 public final NameList nameList;
195 /**
196 * Input to the JAXBContext.newInstance, so that we can recreate
197 * {@link RuntimeTypeInfoSet} whenever we need.
198 */
199 private final String defaultNsUri;
200 private final Class[] classes;
202 /**
203 * true to reorder attributes lexicographically in preparation of the c14n support.
204 */
205 protected final boolean c14nSupport;
207 /**
208 * Flag that user has provided a custom AccessorFactory for JAXB to use
209 */
210 public final boolean xmlAccessorFactorySupport;
212 /**
213 * @see JAXBRIContext#TREAT_EVERYTHING_NILLABLE
214 */
215 public final boolean allNillable;
217 /**
218 * Store properties, so that they can be recovered in the run (is here because of JSON encoding of Jersey).
219 */
220 public final boolean retainPropertyInfo;
222 /**
223 * Supress reflection accessor warnings.
224 */
225 public final boolean supressAccessorWarnings;
227 /**
228 * Improved xsi type handling.
229 */
230 public final boolean improvedXsiTypeHandling;
232 private WeakReference<RuntimeTypeInfoSet> typeInfoSetCache;
234 private @NotNull RuntimeAnnotationReader annotationReader;
236 private /*almost final*/ boolean hasSwaRef;
237 private final @NotNull Map<Class,Class> subclassReplacements;
239 /**
240 * If true, we aim for faster {@link JAXBContext} instanciation performance,
241 * instead of going after efficient sustained unmarshalling/marshalling performance.
242 *
243 * @since 2.0.4
244 */
245 public final boolean fastBoot;
247 private Set<XmlNs> xmlNsSet = null;
249 /**
250 * Returns declared XmlNs annotations (from package-level annotation XmlSchema
251 *
252 * @return set of all present XmlNs annotations
253 */
254 public Set<XmlNs> getXmlNsSet() {
255 return xmlNsSet;
256 }
258 private JAXBContextImpl(JAXBContextBuilder builder) throws JAXBException {
260 this.defaultNsUri = builder.defaultNsUri;
261 this.retainPropertyInfo = builder.retainPropertyInfo;
262 this.annotationReader = builder.annotationReader;
263 this.subclassReplacements = builder.subclassReplacements;
264 this.c14nSupport = builder.c14nSupport;
265 this.classes = builder.classes;
266 this.xmlAccessorFactorySupport = builder.xmlAccessorFactorySupport;
267 this.allNillable = builder.allNillable;
268 this.supressAccessorWarnings = builder.supressAccessorWarnings;
269 this.improvedXsiTypeHandling = builder.improvedXsiTypeHandling;
271 Collection<TypeReference> typeRefs = builder.typeRefs;
273 boolean fastB;
274 try {
275 fastB = Boolean.getBoolean(JAXBContextImpl.class.getName()+".fastBoot");
276 } catch (SecurityException e) {
277 fastB = false;
278 }
279 this.fastBoot = fastB;
281 System.arraycopy(classes,0,this.classes,0,classes.length);
283 RuntimeTypeInfoSet typeSet = getTypeInfoSet();
285 // at least prepare the empty table so that we don't have to check for null later
286 elements.put(null,new LinkedHashMap<QName, ElementBeanInfoImpl>());
288 // recognize leaf bean infos
289 for( RuntimeBuiltinLeafInfo leaf : RuntimeBuiltinLeafInfoImpl.builtinBeanInfos ) {
290 LeafBeanInfoImpl<?> bi = new LeafBeanInfoImpl(this,leaf);
291 beanInfoMap.put(leaf.getClazz(),bi);
292 for( QName t : bi.getTypeNames() )
293 typeMap.put(t,bi);
294 }
296 for (RuntimeEnumLeafInfo e : typeSet.enums().values()) {
297 JaxBeanInfo<?> bi = getOrCreate(e);
298 for (QName qn : bi.getTypeNames())
299 typeMap.put( qn, bi );
300 if(e.isElement())
301 rootMap.put( e.getElementName(), bi );
302 }
304 for (RuntimeArrayInfo a : typeSet.arrays().values()) {
305 JaxBeanInfo<?> ai = getOrCreate(a);
306 for (QName qn : ai.getTypeNames())
307 typeMap.put( qn, ai );
308 }
310 for( Entry<Class, ? extends RuntimeClassInfo> e : typeSet.beans().entrySet() ) {
311 ClassBeanInfoImpl<?> bi = getOrCreate(e.getValue());
313 XmlSchema xs = this.annotationReader.getPackageAnnotation(XmlSchema.class, e.getKey(), null);
314 if(xs != null) {
315 if(xs.xmlns() != null && xs.xmlns().length > 0) {
316 if(xmlNsSet == null)
317 xmlNsSet = new HashSet<XmlNs>();
318 xmlNsSet.addAll(Arrays.asList(xs.xmlns()));
319 }
320 }
322 if(bi.isElement())
323 rootMap.put( e.getValue().getElementName(), bi );
325 for (QName qn : bi.getTypeNames())
326 typeMap.put( qn, bi );
327 }
329 // fill in element mappings
330 for( RuntimeElementInfo n : typeSet.getAllElements() ) {
331 ElementBeanInfoImpl bi = getOrCreate(n);
332 if(n.getScope()==null)
333 rootMap.put(n.getElementName(),bi);
335 RuntimeClassInfo scope = n.getScope();
336 Class scopeClazz = scope==null?null:scope.getClazz();
337 Map<QName,ElementBeanInfoImpl> m = elements.get(scopeClazz);
338 if(m==null) {
339 m = new LinkedHashMap<QName, ElementBeanInfoImpl>();
340 elements.put(scopeClazz,m);
341 }
342 m.put(n.getElementName(),bi);
343 }
345 // this one is so that we can handle plain JAXBElements.
346 beanInfoMap.put(JAXBElement.class,new ElementBeanInfoImpl(this));
347 // another special BeanInfoImpl just for marshalling
348 beanInfoMap.put(CompositeStructure.class,new CompositeStructureBeanInfo(this));
350 getOrCreate(typeSet.getAnyTypeInfo());
352 // then link them all!
353 for (JaxBeanInfo bi : beanInfos.values())
354 bi.link(this);
356 // register primitives for boxed types just to make GrammarInfo fool-proof
357 for( Map.Entry<Class,Class> e : RuntimeUtil.primitiveToBox.entrySet() )
358 beanInfoMap.put( e.getKey(), beanInfoMap.get(e.getValue()) );
360 // build bridges
361 ReflectionNavigator nav = typeSet.getNavigator();
363 for (TypeReference tr : typeRefs) {
364 XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class);
365 Adapter<Type,Class> a=null;
366 XmlList xl = tr.get(XmlList.class);
368 // eventually compute the in-memory type
369 Class erasedType = nav.erasure(tr.type);
371 if(xjta!=null) {
372 a = new Adapter<Type,Class>(xjta.value(),nav);
373 }
374 if(tr.get(XmlAttachmentRef.class)!=null) {
375 a = new Adapter<Type,Class>(SwaRefAdapter.class,nav);
376 hasSwaRef = true;
377 }
379 if(a!=null) {
380 erasedType = nav.erasure(a.defaultType);
381 }
383 Name name = nameBuilder.createElementName(tr.tagName);
385 InternalBridge bridge;
386 if(xl==null)
387 bridge = new BridgeImpl(this, name,getBeanInfo(erasedType,true),tr);
388 else
389 bridge = new BridgeImpl(this, name,new ValueListBeanInfoImpl(this,erasedType),tr);
391 if(a!=null)
392 bridge = new BridgeAdapter(bridge,a.adapterType);
394 bridges.put(tr,bridge);
395 }
397 this.nameList = nameBuilder.conclude();
399 for (JaxBeanInfo bi : beanInfos.values())
400 bi.wrapUp();
402 // no use for them now
403 nameBuilder = null;
404 beanInfos = null;
405 }
407 /**
408 * True if this JAXBContext has {@link XmlAttachmentRef}.
409 */
410 public boolean hasSwaRef() {
411 return hasSwaRef;
412 }
414 public RuntimeTypeInfoSet getRuntimeTypeInfoSet() {
415 try {
416 return getTypeInfoSet();
417 } catch (IllegalAnnotationsException e) {
418 // impossible, once the model is constructred
419 throw new AssertionError(e);
420 }
421 }
423 /**
424 * Creates a {@link RuntimeTypeInfoSet}.
425 */
426 public RuntimeTypeInfoSet getTypeInfoSet() throws IllegalAnnotationsException {
428 // check cache
429 if(typeInfoSetCache!=null) {
430 RuntimeTypeInfoSet r = typeInfoSetCache.get();
431 if(r!=null)
432 return r;
433 }
435 final RuntimeModelBuilder builder = new RuntimeModelBuilder(this,annotationReader,subclassReplacements,defaultNsUri);
437 IllegalAnnotationsException.Builder errorHandler = new IllegalAnnotationsException.Builder();
438 builder.setErrorHandler(errorHandler);
440 for( Class c : classes ) {
441 if(c==CompositeStructure.class)
442 // CompositeStructure doesn't have TypeInfo, so skip it.
443 // We'll add JaxBeanInfo for this later automatically
444 continue;
445 builder.getTypeInfo(new Ref<Type,Class>(c));
446 }
448 this.hasSwaRef |= builder.hasSwaRef;
449 RuntimeTypeInfoSet r = builder.link();
451 errorHandler.check();
452 assert r!=null : "if no error was reported, the link must be a success";
454 typeInfoSetCache = new WeakReference<RuntimeTypeInfoSet>(r);
456 return r;
457 }
460 public ElementBeanInfoImpl getElement(Class scope, QName name) {
461 Map<QName,ElementBeanInfoImpl> m = elements.get(scope);
462 if(m!=null) {
463 ElementBeanInfoImpl bi = m.get(name);
464 if(bi!=null)
465 return bi;
466 }
467 m = elements.get(null);
468 return m.get(name);
469 }
475 private ElementBeanInfoImpl getOrCreate( RuntimeElementInfo rei ) {
476 JaxBeanInfo bi = beanInfos.get(rei);
477 if(bi!=null) return (ElementBeanInfoImpl)bi;
479 // all elements share the same type, so we can't register them to beanInfoMap
480 return new ElementBeanInfoImpl(this, rei);
481 }
483 protected JaxBeanInfo getOrCreate( RuntimeEnumLeafInfo eli ) {
484 JaxBeanInfo bi = beanInfos.get(eli);
485 if(bi!=null) return bi;
486 bi = new LeafBeanInfoImpl(this,eli);
487 beanInfoMap.put(bi.jaxbType,bi);
488 return bi;
489 }
491 protected ClassBeanInfoImpl getOrCreate( RuntimeClassInfo ci ) {
492 ClassBeanInfoImpl bi = (ClassBeanInfoImpl)beanInfos.get(ci);
493 if(bi!=null) return bi;
494 bi = new ClassBeanInfoImpl(this,ci);
495 beanInfoMap.put(bi.jaxbType,bi);
496 return bi;
497 }
499 protected JaxBeanInfo getOrCreate( RuntimeArrayInfo ai ) {
500 JaxBeanInfo abi = beanInfos.get(ai);
501 if(abi!=null) return abi;
503 abi = new ArrayBeanInfoImpl(this,ai);
505 beanInfoMap.put(ai.getType(),abi);
506 return abi;
507 }
509 public JaxBeanInfo getOrCreate(RuntimeTypeInfo e) {
510 if(e instanceof RuntimeElementInfo)
511 return getOrCreate((RuntimeElementInfo)e);
512 if(e instanceof RuntimeClassInfo)
513 return getOrCreate((RuntimeClassInfo)e);
514 if(e instanceof RuntimeLeafInfo) {
515 JaxBeanInfo bi = beanInfos.get(e); // must have been created
516 assert bi!=null;
517 return bi;
518 }
519 if(e instanceof RuntimeArrayInfo)
520 return getOrCreate((RuntimeArrayInfo)e);
521 if(e.getType()==Object.class) {
522 // anyType
523 JaxBeanInfo bi = beanInfoMap.get(Object.class);
524 if(bi==null) {
525 bi = new AnyTypeBeanInfo(this,e);
526 beanInfoMap.put(Object.class,bi);
527 }
528 return bi;
529 }
531 throw new IllegalArgumentException();
532 }
534 /**
535 * Gets the {@link JaxBeanInfo} object that can handle
536 * the given JAXB-bound object.
537 *
538 * <p>
539 * This method traverses the base classes of the given object.
540 *
541 * @return null
542 * if <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>.
543 */
544 public final JaxBeanInfo getBeanInfo(Object o) {
545 // don't allow xs:anyType beanInfo to handle all the unbound objects
546 for( Class c=o.getClass(); c!=Object.class; c=c.getSuperclass()) {
547 JaxBeanInfo bi = beanInfoMap.get(c);
548 if(bi!=null) return bi;
549 }
550 if(o instanceof Element)
551 return beanInfoMap.get(Object.class); // return the BeanInfo for xs:anyType
552 for( Class c : o.getClass().getInterfaces()) {
553 JaxBeanInfo bi = beanInfoMap.get(c);
554 if(bi!=null) return bi;
555 }
556 return null;
557 }
559 /**
560 * Gets the {@link JaxBeanInfo} object that can handle
561 * the given JAXB-bound object.
562 *
563 * @param fatal
564 * if true, the failure to look up will throw an exception.
565 * Otherwise it will just return null.
566 */
567 public final JaxBeanInfo getBeanInfo(Object o,boolean fatal) throws JAXBException {
568 JaxBeanInfo bi = getBeanInfo(o);
569 if(bi!=null) return bi;
570 if(fatal) {
571 if(o instanceof Document)
572 throw new JAXBException(Messages.ELEMENT_NEEDED_BUT_FOUND_DOCUMENT.format(o.getClass()));
573 throw new JAXBException(Messages.UNKNOWN_CLASS.format(o.getClass()));
574 }
575 return null;
576 }
578 /**
579 * Gets the {@link JaxBeanInfo} object that can handle
580 * the given JAXB-bound class.
581 *
582 * <p>
583 * This method doesn't look for base classes.
584 *
585 * @return null
586 * if <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>.
587 */
588 public final <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz) {
589 return (JaxBeanInfo<T>)beanInfoMap.get(clazz);
590 }
592 /**
593 * Gets the {@link JaxBeanInfo} object that can handle
594 * the given JAXB-bound class.
595 *
596 * @param fatal
597 * if true, the failure to look up will throw an exception.
598 * Otherwise it will just return null.
599 */
600 public final <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz,boolean fatal) throws JAXBException {
601 JaxBeanInfo<T> bi = getBeanInfo(clazz);
602 if(bi!=null) return bi;
603 if(fatal)
604 throw new JAXBException(clazz.getName()+" is not known to this context");
605 return null;
606 }
608 /**
609 * Based on the tag name, determine what object to unmarshal,
610 * and then set a new object and its loader to the current unmarshaller state.
611 *
612 * @return
613 * null if the given name pair is not recognized.
614 */
615 public final Loader selectRootLoader( UnmarshallingContext.State state, TagName tag ) {
616 JaxBeanInfo beanInfo = rootMap.get(tag.uri,tag.local);
617 if(beanInfo==null)
618 return null;
620 return beanInfo.getLoader(this,true);
621 }
623 /**
624 * Gets the {@link JaxBeanInfo} for the given named XML Schema type.
625 *
626 * @return
627 * null if the type name is not recognized. For schema
628 * languages other than XML Schema, this method always
629 * returns null.
630 */
631 public JaxBeanInfo getGlobalType(QName name) {
632 return typeMap.get(name);
633 }
635 /**
636 * Finds a type name that this context recognizes which is
637 * "closest" to the given type name.
638 *
639 * <p>
640 * This method is used for error recovery.
641 */
642 public String getNearestTypeName(QName name) {
643 String[] all = new String[typeMap.size()];
644 int i=0;
645 for (QName qn : typeMap.keySet()) {
646 if(qn.getLocalPart().equals(name.getLocalPart()))
647 return qn.toString(); // probably a match, as people often gets confused about namespace.
648 all[i++] = qn.toString();
649 }
651 String nearest = EditDistance.findNearest(name.toString(), all);
653 if(EditDistance.editDistance(nearest,name.toString())>10)
654 return null; // too far apart.
656 return nearest;
657 }
659 /**
660 * Returns the set of valid root tag names.
661 * For diagnostic use.
662 */
663 public Set<QName> getValidRootNames() {
664 Set<QName> r = new TreeSet<QName>(QNAME_COMPARATOR);
665 for (QNameMap.Entry e : rootMap.entrySet()) {
666 r.add(e.createQName());
667 }
668 return r;
669 }
671 /**
672 * Cache of UTF-8 encoded local names to improve the performance for the marshalling.
673 */
674 private Encoded[] utf8nameTable;
676 public synchronized Encoded[] getUTF8NameTable() {
677 if(utf8nameTable==null) {
678 Encoded[] x = new Encoded[nameList.localNames.length];
679 for( int i=0; i<x.length; i++ ) {
680 Encoded e = new Encoded(nameList.localNames[i]);
681 e.compact();
682 x[i] = e;
683 }
684 utf8nameTable = x;
685 }
686 return utf8nameTable;
687 }
689 public int getNumberOfLocalNames() {
690 return nameList.localNames.length;
691 }
693 public int getNumberOfElementNames() {
694 return nameList.numberOfElementNames;
695 }
697 public int getNumberOfAttributeNames() {
698 return nameList.numberOfAttributeNames;
699 }
701 /**
702 * Creates a new identity transformer.
703 */
704 static Transformer createTransformer() {
705 try {
706 if (tf==null) {
707 synchronized(JAXBContextImpl.class) {
708 if (tf==null) {
709 tf = (SAXTransformerFactory)TransformerFactory.newInstance();
710 }
711 }
712 }
713 return tf.newTransformer();
714 } catch (TransformerConfigurationException e) {
715 throw new Error(e); // impossible
716 }
717 }
719 /**
720 * Creates a new identity transformer.
721 */
722 public static TransformerHandler createTransformerHandler() {
723 try {
724 if (tf==null) {
725 synchronized(JAXBContextImpl.class) {
726 if (tf==null) {
727 tf = (SAXTransformerFactory)TransformerFactory.newInstance();
728 }
729 }
730 }
731 return tf.newTransformerHandler();
732 } catch (TransformerConfigurationException e) {
733 throw new Error(e); // impossible
734 }
735 }
737 /**
738 * Creates a new DOM document.
739 */
740 static Document createDom() {
741 synchronized(JAXBContextImpl.class) {
742 if(db==null) {
743 try {
744 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
745 dbf.setNamespaceAware(true);
746 db = dbf.newDocumentBuilder();
747 } catch (ParserConfigurationException e) {
748 // impossible
749 throw new FactoryConfigurationError(e);
750 }
751 }
752 return db.newDocument();
753 }
754 }
756 public MarshallerImpl createMarshaller() {
757 return new MarshallerImpl(this,null);
758 }
760 public UnmarshallerImpl createUnmarshaller() {
761 return new UnmarshallerImpl(this,null);
762 }
764 public Validator createValidator() {
765 throw new UnsupportedOperationException(Messages.NOT_IMPLEMENTED_IN_2_0.format());
766 }
768 @Override
769 public JAXBIntrospector createJAXBIntrospector() {
770 return new JAXBIntrospector() {
771 public boolean isElement(Object object) {
772 return getElementName(object)!=null;
773 }
775 public QName getElementName(Object jaxbElement) {
776 try {
777 return JAXBContextImpl.this.getElementName(jaxbElement);
778 } catch (JAXBException e) {
779 return null;
780 }
781 }
782 };
783 }
785 private NonElement<Type,Class> getXmlType(RuntimeTypeInfoSet tis, TypeReference tr) {
786 if(tr==null)
787 throw new IllegalArgumentException();
789 XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class);
790 XmlList xl = tr.get(XmlList.class);
792 Ref<Type,Class> ref = new Ref<Type,Class>(annotationReader, tis.getNavigator(), tr.type, xjta, xl );
794 return tis.getTypeInfo(ref);
795 }
797 @Override
798 public void generateEpisode(Result output) {
799 if(output==null)
800 throw new IllegalArgumentException();
801 createSchemaGenerator().writeEpisodeFile(ResultFactory.createSerializer(output));
802 }
804 @Override
805 @SuppressWarnings("ThrowableInitCause")
806 public void generateSchema(SchemaOutputResolver outputResolver) throws IOException {
807 if(outputResolver==null)
808 throw new IOException(Messages.NULL_OUTPUT_RESOLVER.format());
810 final SAXParseException[] e = new SAXParseException[1];
811 final SAXParseException[] w = new SAXParseException[1];
813 createSchemaGenerator().write(outputResolver, new ErrorListener() {
814 public void error(SAXParseException exception) {
815 e[0] = exception;
816 }
818 public void fatalError(SAXParseException exception) {
819 e[0] = exception;
820 }
822 public void warning(SAXParseException exception) {
823 w[0] = exception;
824 }
826 public void info(SAXParseException exception) {}
827 });
829 if (e[0]!=null) {
830 IOException x = new IOException(Messages.FAILED_TO_GENERATE_SCHEMA.format());
831 x.initCause(e[0]);
832 throw x;
833 }
834 if (w[0]!=null) {
835 IOException x = new IOException(Messages.ERROR_PROCESSING_SCHEMA.format());
836 x.initCause(w[0]);
837 throw x;
838 }
839 }
841 private XmlSchemaGenerator<Type,Class,Field,Method> createSchemaGenerator() {
842 RuntimeTypeInfoSet tis;
843 try {
844 tis = getTypeInfoSet();
845 } catch (IllegalAnnotationsException e) {
846 // this shouldn't happen because we've already
847 throw new AssertionError(e);
848 }
850 XmlSchemaGenerator<Type,Class,Field,Method> xsdgen =
851 new XmlSchemaGenerator<Type,Class,Field,Method>(tis.getNavigator(),tis);
853 // JAX-RPC uses Bridge objects that collide with
854 // @XmlRootElement.
855 // we will avoid collision here
856 Set<QName> rootTagNames = new HashSet<QName>();
857 for (RuntimeElementInfo ei : tis.getAllElements()) {
858 rootTagNames.add(ei.getElementName());
859 }
860 for (RuntimeClassInfo ci : tis.beans().values()) {
861 if(ci.isElement())
862 rootTagNames.add(ci.asElement().getElementName());
863 }
865 for (TypeReference tr : bridges.keySet()) {
866 if(rootTagNames.contains(tr.tagName))
867 continue;
869 if(tr.type==void.class || tr.type==Void.class) {
870 xsdgen.add(tr.tagName,false,null);
871 } else
872 if(tr.type==CompositeStructure.class) {
873 // this is a special class we introduced for JAX-WS that we *don't* want in the schema
874 } else {
875 NonElement<Type,Class> typeInfo = getXmlType(tis,tr);
876 xsdgen.add(tr.tagName, !Navigator.REFLECTION.isPrimitive(tr.type),typeInfo);
877 }
878 }
879 return xsdgen;
880 }
882 public QName getTypeName(TypeReference tr) {
883 try {
884 NonElement<Type,Class> xt = getXmlType(getTypeInfoSet(),tr);
885 if(xt==null) throw new IllegalArgumentException();
886 return xt.getTypeName();
887 } catch (IllegalAnnotationsException e) {
888 // impossible given that JAXBRIContext has been successfully built in the first place
889 throw new AssertionError(e);
890 }
891 }
893 /**
894 * Used for testing.
895 */
896 public SchemaOutputResolver createTestResolver() {
897 return new SchemaOutputResolver() {
898 public Result createOutput(String namespaceUri, String suggestedFileName) {
899 SAXResult r = new SAXResult(new DefaultHandler());
900 r.setSystemId(suggestedFileName);
901 return r;
902 }
903 };
904 }
906 @Override
907 public <T> Binder<T> createBinder(Class<T> domType) {
908 if(domType==Node.class)
909 return (Binder<T>)createBinder();
910 else
911 return super.createBinder(domType);
912 }
914 @Override
915 public Binder<Node> createBinder() {
916 return new BinderImpl<Node>(this,new DOMScanner());
917 }
919 public QName getElementName(Object o) throws JAXBException {
920 JaxBeanInfo bi = getBeanInfo(o,true);
921 if(!bi.isElement())
922 return null;
923 return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o));
924 }
926 public QName getElementName(Class o) throws JAXBException {
927 JaxBeanInfo bi = getBeanInfo(o,true);
928 if(!bi.isElement())
929 return null;
930 return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o));
931 }
933 public Bridge createBridge(TypeReference ref) {
934 return bridges.get(ref);
935 }
937 public @NotNull BridgeContext createBridgeContext() {
938 return new BridgeContextImpl(this);
939 }
941 public RawAccessor getElementPropertyAccessor(Class wrapperBean, String nsUri, String localName) throws JAXBException {
942 JaxBeanInfo bi = getBeanInfo(wrapperBean,true);
943 if(!(bi instanceof ClassBeanInfoImpl))
944 throw new JAXBException(wrapperBean+" is not a bean");
946 for( ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi; cb!=null; cb=cb.superClazz) {
947 for (Property p : cb.properties) {
948 final Accessor acc = p.getElementPropertyAccessor(nsUri,localName);
949 if(acc!=null)
950 return new RawAccessor() {
951 // Accessor.set/get are designed for unmarshaller/marshaller, and hence
952 // they go through an adapter behind the scene.
953 // this isn't desirable for JAX-WS, which essentially uses this method
954 // just as a reflection library. So use the "unadapted" version to
955 // achieve the desired semantics
956 public Object get(Object bean) throws AccessorException {
957 return acc.getUnadapted(bean);
958 }
960 public void set(Object bean, Object value) throws AccessorException {
961 acc.setUnadapted(bean,value);
962 }
963 };
964 }
965 }
966 throw new JAXBException(new QName(nsUri,localName)+" is not a valid property on "+wrapperBean);
967 }
969 public List<String> getKnownNamespaceURIs() {
970 return Arrays.asList(nameList.namespaceURIs);
971 }
973 public String getBuildId() {
974 Package pkg = getClass().getPackage();
975 if(pkg==null) return null;
976 return pkg.getImplementationVersion();
977 }
979 @Override
980 public String toString() {
981 StringBuilder buf = new StringBuilder(Which.which(getClass()) + " Build-Id: " + getBuildId());
982 buf.append("\nClasses known to this context:\n");
984 Set<String> names = new TreeSet<String>(); // sort them so that it's easy to read
986 for (Class key : beanInfoMap.keySet())
987 names.add(key.getName());
989 for(String name: names)
990 buf.append(" ").append(name).append('\n');
992 return buf.toString();
993 }
995 /**
996 * Gets the value of the xmime:contentType attribute on the given object, or null
997 * if for some reason it couldn't be found, including any error.
998 */
999 public String getXMIMEContentType( Object o ) {
1000 JaxBeanInfo bi = getBeanInfo(o);
1001 if(!(bi instanceof ClassBeanInfoImpl))
1002 return null;
1004 ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi;
1005 for (Property p : cb.properties) {
1006 if (p instanceof AttributeProperty) {
1007 AttributeProperty ap = (AttributeProperty) p;
1008 if(ap.attName.equals(WellKnownNamespace.XML_MIME_URI,"contentType"))
1009 try {
1010 return (String)ap.xacc.print(o);
1011 } catch (AccessorException e) {
1012 return null;
1013 } catch (SAXException e) {
1014 return null;
1015 } catch (ClassCastException e) {
1016 return null;
1017 }
1018 }
1019 }
1020 return null;
1021 }
1023 /**
1024 * Creates a {@link JAXBContextImpl} that includes the specified additional classes.
1025 */
1026 public JAXBContextImpl createAugmented(Class<?> clazz) throws JAXBException {
1027 Class[] newList = new Class[classes.length+1];
1028 System.arraycopy(classes,0,newList,0,classes.length);
1029 newList[classes.length] = clazz;
1031 JAXBContextBuilder builder = new JAXBContextBuilder(this);
1032 builder.setClasses(newList);
1033 return builder.build();
1034 }
1036 private static final Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() {
1037 public int compare(QName lhs, QName rhs) {
1038 int r = lhs.getLocalPart().compareTo(rhs.getLocalPart());
1039 if(r!=0) return r;
1041 return lhs.getNamespaceURI().compareTo(rhs.getNamespaceURI());
1042 }
1043 };
1045 public static class JAXBContextBuilder {
1047 private boolean retainPropertyInfo = false;
1048 private boolean supressAccessorWarnings = false;
1049 private String defaultNsUri = "";
1050 private @NotNull RuntimeAnnotationReader annotationReader = new RuntimeInlineAnnotationReader();
1051 private @NotNull Map<Class,Class> subclassReplacements = Collections.emptyMap();
1052 private boolean c14nSupport = false;
1053 private Class[] classes;
1054 private Collection<TypeReference> typeRefs;
1055 private boolean xmlAccessorFactorySupport = false;
1056 private boolean allNillable;
1057 private boolean improvedXsiTypeHandling = true;
1059 public JAXBContextBuilder() {};
1061 public JAXBContextBuilder(JAXBContextImpl baseImpl) {
1062 this.supressAccessorWarnings = baseImpl.supressAccessorWarnings;
1063 this.retainPropertyInfo = baseImpl.retainPropertyInfo;
1064 this.defaultNsUri = baseImpl.defaultNsUri;
1065 this.annotationReader = baseImpl.annotationReader;
1066 this.subclassReplacements = baseImpl.subclassReplacements;
1067 this.c14nSupport = baseImpl.c14nSupport;
1068 this.classes = baseImpl.classes;
1069 this.typeRefs = baseImpl.bridges.keySet();
1070 this.xmlAccessorFactorySupport = baseImpl.xmlAccessorFactorySupport;
1071 this.allNillable = baseImpl.allNillable;
1072 }
1074 public JAXBContextBuilder setRetainPropertyInfo(boolean val) {
1075 this.retainPropertyInfo = val;
1076 return this;
1077 }
1079 public JAXBContextBuilder setSupressAccessorWarnings(boolean val) {
1080 this.supressAccessorWarnings = val;
1081 return this;
1082 }
1084 public JAXBContextBuilder setC14NSupport(boolean val) {
1085 this.c14nSupport = val;
1086 return this;
1087 }
1089 public JAXBContextBuilder setXmlAccessorFactorySupport(boolean val) {
1090 this.xmlAccessorFactorySupport = val;
1091 return this;
1092 }
1094 public JAXBContextBuilder setDefaultNsUri(String val) {
1095 this.defaultNsUri = val;
1096 return this;
1097 }
1099 public JAXBContextBuilder setAllNillable(boolean val) {
1100 this.allNillable = val;
1101 return this;
1102 }
1104 public JAXBContextBuilder setClasses(Class[] val) {
1105 this.classes = val;
1106 return this;
1107 }
1109 public JAXBContextBuilder setAnnotationReader(RuntimeAnnotationReader val) {
1110 this.annotationReader = val;
1111 return this;
1112 }
1114 public JAXBContextBuilder setSubclassReplacements(Map<Class,Class> val) {
1115 this.subclassReplacements = val;
1116 return this;
1117 }
1119 public JAXBContextBuilder setTypeRefs(Collection<TypeReference> val) {
1120 this.typeRefs = val;
1121 return this;
1122 }
1124 public JAXBContextBuilder setImprovedXsiTypeHandling(boolean val) {
1125 this.improvedXsiTypeHandling = val;
1126 return this;
1127 }
1129 public JAXBContextImpl build() throws JAXBException {
1131 // fool-proof
1132 if (this.defaultNsUri == null) {
1133 this.defaultNsUri = "";
1134 }
1136 if (this.subclassReplacements == null) {
1137 this.subclassReplacements = Collections.emptyMap();
1138 }
1140 if (this.annotationReader == null) {
1141 this.annotationReader = new RuntimeInlineAnnotationReader();
1142 }
1144 if (this.typeRefs == null) {
1145 this.typeRefs = Collections.<TypeReference>emptyList();
1146 }
1148 return new JAXBContextImpl(this);
1149 }
1151 }
1153 }