Fri, 22 Nov 2013 21:11:19 +0100
8010935: Better XML handling
8027378: Two closed/javax/xml/8005432 fails with jdk7u51b04
8028382: Two javax/xml/8005433 tests still fail after the fix JDK-8028147
Summary: base fix + fixes for test regressions; fix also reviewed by Maxim Soloviev, Alexander Fomin
Reviewed-by: mchung, mgrebac, mullan
1 /*
2 * Copyright (c) 1997, 2013, 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.SAXTransformerFactory;
69 import javax.xml.transform.sax.TransformerHandler;
71 import com.sun.istack.internal.NotNull;
72 import com.sun.istack.internal.Pool;
73 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
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.model.annotation.RuntimeAnnotationReader;
85 import com.sun.xml.internal.bind.v2.model.annotation.RuntimeInlineAnnotationReader;
86 import com.sun.xml.internal.bind.v2.model.core.Adapter;
87 import com.sun.xml.internal.bind.v2.model.core.NonElement;
88 import com.sun.xml.internal.bind.v2.model.core.Ref;
89 import com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl;
90 import com.sun.xml.internal.bind.v2.model.impl.RuntimeModelBuilder;
91 import com.sun.xml.internal.bind.v2.model.nav.Navigator;
92 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeArrayInfo;
93 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeBuiltinLeafInfo;
94 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo;
95 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElementInfo;
96 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeEnumLeafInfo;
97 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeLeafInfo;
98 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfo;
99 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfoSet;
100 import com.sun.xml.internal.bind.v2.runtime.output.Encoded;
101 import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty;
102 import com.sun.xml.internal.bind.v2.runtime.property.Property;
103 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
104 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
105 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.TagName;
106 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl;
107 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
108 import com.sun.xml.internal.bind.v2.schemagen.XmlSchemaGenerator;
109 import com.sun.xml.internal.bind.v2.util.EditDistance;
110 import com.sun.xml.internal.bind.v2.util.QNameMap;
111 import com.sun.xml.internal.bind.v2.util.XmlFactory;
112 import com.sun.xml.internal.txw2.output.ResultFactory;
114 import org.w3c.dom.Document;
115 import org.w3c.dom.Element;
116 import org.w3c.dom.Node;
117 import org.xml.sax.SAXException;
118 import org.xml.sax.SAXParseException;
120 /**
121 * This class provides the implementation of JAXBContext.
122 *
123 */
124 public final class JAXBContextImpl extends JAXBRIContext {
126 /**
127 * All the bridge classes.
128 */
129 private final Map<TypeReference,Bridge> bridges = new LinkedHashMap<TypeReference,Bridge>();
131 /**
132 * Shared instance of {@link TransformerFactory}.
133 * Lock before use, because a {@link TransformerFactory} is not thread-safe
134 * whereas {@link JAXBContextImpl} is.
135 * Lazily created.
136 */
137 private volatile static SAXTransformerFactory tf;
139 /**
140 * Shared instance of {@link DocumentBuilder}.
141 * Lock before use. Lazily created.
142 */
143 private static DocumentBuilder db;
145 private final QNameMap<JaxBeanInfo> rootMap = new QNameMap<JaxBeanInfo>();
146 private final HashMap<QName,JaxBeanInfo> typeMap = new HashMap<QName,JaxBeanInfo>();
148 /**
149 * Map from JAXB-bound {@link Class} to its {@link JaxBeanInfo}.
150 */
151 private final Map<Class,JaxBeanInfo> beanInfoMap = new LinkedHashMap<Class,JaxBeanInfo>();
153 /**
154 * All created {@link JaxBeanInfo}s.
155 * Updated from each {@link JaxBeanInfo}s constructors to avoid infinite recursion
156 * for a cyclic reference.
157 *
158 * <p>
159 * This map is only used while the {@link JAXBContextImpl} is built and set to null
160 * to avoid keeping references too long.
161 */
162 protected Map<RuntimeTypeInfo,JaxBeanInfo> beanInfos = new LinkedHashMap<RuntimeTypeInfo, JaxBeanInfo>();
164 private final Map<Class/*scope*/,Map<QName,ElementBeanInfoImpl>> elements = new LinkedHashMap<Class, Map<QName, ElementBeanInfoImpl>>();
166 /**
167 * Pool of {@link Marshaller}s.
168 */
169 public final Pool<Marshaller> marshallerPool = new Pool.Impl<Marshaller>() {
170 protected @NotNull Marshaller create() {
171 return createMarshaller();
172 }
173 };
175 public final Pool<Unmarshaller> unmarshallerPool = new Pool.Impl<Unmarshaller>() {
176 protected @NotNull Unmarshaller create() {
177 return createUnmarshaller();
178 }
179 };
181 /**
182 * Used to assign indices to known names in this grammar.
183 * Reset to null once the build phase is completed.
184 */
185 public NameBuilder nameBuilder = new NameBuilder();
187 /**
188 * Keeps the list of known names.
189 * This field is set once the build pahse is completed.
190 */
191 public final NameList nameList;
193 /**
194 * Input to the JAXBContext.newInstance, so that we can recreate
195 * {@link RuntimeTypeInfoSet} whenever we need.
196 */
197 private final String defaultNsUri;
198 private final Class[] classes;
200 /**
201 * true to reorder attributes lexicographically in preparation of the c14n support.
202 */
203 protected final boolean c14nSupport;
205 /**
206 * Flag that user has provided a custom AccessorFactory for JAXB to use
207 */
208 public final boolean xmlAccessorFactorySupport;
210 /**
211 * @see JAXBRIContext#TREAT_EVERYTHING_NILLABLE
212 */
213 public final boolean allNillable;
215 /**
216 * Store properties, so that they can be recovered in the run (is here because of JSON encoding of Jersey).
217 */
218 public final boolean retainPropertyInfo;
220 /**
221 * Suppress reflection accessor warnings.
222 */
223 public final boolean supressAccessorWarnings;
225 /**
226 * Improved xsi type handling.
227 */
228 public final boolean improvedXsiTypeHandling;
230 /**
231 * Disable security processing.
232 */
233 public final boolean disableSecurityProcessing;
235 private WeakReference<RuntimeTypeInfoSet> typeInfoSetCache;
237 private @NotNull RuntimeAnnotationReader annotationReader;
239 private /*almost final*/ boolean hasSwaRef;
240 private final @NotNull Map<Class,Class> subclassReplacements;
242 /**
243 * If true, we aim for faster {@link JAXBContext} instantiation performance,
244 * instead of going after efficient sustained unmarshalling/marshalling performance.
245 *
246 * @since 2.0.4
247 */
248 public final boolean fastBoot;
250 private Set<XmlNs> xmlNsSet = null;
252 /**
253 * Returns declared XmlNs annotations (from package-level annotation XmlSchema
254 *
255 * @return set of all present XmlNs annotations
256 */
257 public Set<XmlNs> getXmlNsSet() {
258 return xmlNsSet;
259 }
261 private JAXBContextImpl(JAXBContextBuilder builder) throws JAXBException {
263 this.defaultNsUri = builder.defaultNsUri;
264 this.retainPropertyInfo = builder.retainPropertyInfo;
265 this.annotationReader = builder.annotationReader;
266 this.subclassReplacements = builder.subclassReplacements;
267 this.c14nSupport = builder.c14nSupport;
268 this.classes = builder.classes;
269 this.xmlAccessorFactorySupport = builder.xmlAccessorFactorySupport;
270 this.allNillable = builder.allNillable;
271 this.supressAccessorWarnings = builder.supressAccessorWarnings;
272 this.improvedXsiTypeHandling = builder.improvedXsiTypeHandling;
273 this.disableSecurityProcessing = builder.disableSecurityProcessing;
275 Collection<TypeReference> typeRefs = builder.typeRefs;
277 boolean fastB;
278 try {
279 fastB = Boolean.getBoolean(JAXBContextImpl.class.getName()+".fastBoot");
280 } catch (SecurityException e) {
281 fastB = false;
282 }
283 this.fastBoot = fastB;
285 RuntimeTypeInfoSet typeSet = getTypeInfoSet();
287 // at least prepare the empty table so that we don't have to check for null later
288 elements.put(null,new LinkedHashMap<QName, ElementBeanInfoImpl>());
290 // recognize leaf bean infos
291 for( RuntimeBuiltinLeafInfo leaf : RuntimeBuiltinLeafInfoImpl.builtinBeanInfos ) {
292 LeafBeanInfoImpl<?> bi = new LeafBeanInfoImpl(this,leaf);
293 beanInfoMap.put(leaf.getClazz(),bi);
294 for( QName t : bi.getTypeNames() )
295 typeMap.put(t,bi);
296 }
298 for (RuntimeEnumLeafInfo e : typeSet.enums().values()) {
299 JaxBeanInfo<?> bi = getOrCreate(e);
300 for (QName qn : bi.getTypeNames())
301 typeMap.put( qn, bi );
302 if(e.isElement())
303 rootMap.put( e.getElementName(), bi );
304 }
306 for (RuntimeArrayInfo a : typeSet.arrays().values()) {
307 JaxBeanInfo<?> ai = getOrCreate(a);
308 for (QName qn : ai.getTypeNames())
309 typeMap.put( qn, ai );
310 }
312 for( Entry<Class, ? extends RuntimeClassInfo> e : typeSet.beans().entrySet() ) {
313 ClassBeanInfoImpl<?> bi = getOrCreate(e.getValue());
315 XmlSchema xs = this.annotationReader.getPackageAnnotation(XmlSchema.class, e.getKey(), null);
316 if(xs != null) {
317 if(xs.xmlns() != null && xs.xmlns().length > 0) {
318 if(xmlNsSet == null)
319 xmlNsSet = new HashSet<XmlNs>();
320 xmlNsSet.addAll(Arrays.asList(xs.xmlns()));
321 }
322 }
324 if(bi.isElement())
325 rootMap.put( e.getValue().getElementName(), bi );
327 for (QName qn : bi.getTypeNames())
328 typeMap.put( qn, bi );
329 }
331 // fill in element mappings
332 for( RuntimeElementInfo n : typeSet.getAllElements() ) {
333 ElementBeanInfoImpl bi = getOrCreate(n);
334 if(n.getScope()==null)
335 rootMap.put(n.getElementName(),bi);
337 RuntimeClassInfo scope = n.getScope();
338 Class scopeClazz = scope==null?null:scope.getClazz();
339 Map<QName,ElementBeanInfoImpl> m = elements.get(scopeClazz);
340 if(m==null) {
341 m = new LinkedHashMap<QName, ElementBeanInfoImpl>();
342 elements.put(scopeClazz,m);
343 }
344 m.put(n.getElementName(),bi);
345 }
347 // this one is so that we can handle plain JAXBElements.
348 beanInfoMap.put(JAXBElement.class,new ElementBeanInfoImpl(this));
349 // another special BeanInfoImpl just for marshalling
350 beanInfoMap.put(CompositeStructure.class,new CompositeStructureBeanInfo(this));
352 getOrCreate(typeSet.getAnyTypeInfo());
354 // then link them all!
355 for (JaxBeanInfo bi : beanInfos.values())
356 bi.link(this);
358 // register primitives for boxed types just to make GrammarInfo fool-proof
359 for( Map.Entry<Class,Class> e : RuntimeUtil.primitiveToBox.entrySet() )
360 beanInfoMap.put( e.getKey(), beanInfoMap.get(e.getValue()) );
362 // build bridges
363 Navigator<Type, Class, Field, Method> nav = typeSet.getNavigator();
365 for (TypeReference tr : typeRefs) {
366 XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class);
367 Adapter<Type,Class> a=null;
368 XmlList xl = tr.get(XmlList.class);
370 // eventually compute the in-memory type
371 Class erasedType = (Class) nav.erasure(tr.type);
373 if(xjta!=null) {
374 a = new Adapter<Type,Class>(xjta.value(),nav);
375 }
376 if(tr.get(XmlAttachmentRef.class)!=null) {
377 a = new Adapter<Type,Class>(SwaRefAdapter.class,nav);
378 hasSwaRef = true;
379 }
381 if(a!=null) {
382 erasedType = (Class) nav.erasure(a.defaultType);
383 }
385 Name name = nameBuilder.createElementName(tr.tagName);
387 InternalBridge bridge;
388 if(xl==null)
389 bridge = new BridgeImpl(this, name,getBeanInfo(erasedType,true),tr);
390 else
391 bridge = new BridgeImpl(this, name,new ValueListBeanInfoImpl(this,erasedType),tr);
393 if(a!=null)
394 bridge = new BridgeAdapter(bridge,a.adapterType);
396 bridges.put(tr,bridge);
397 }
399 this.nameList = nameBuilder.conclude();
401 for (JaxBeanInfo bi : beanInfos.values())
402 bi.wrapUp();
404 // no use for them now
405 nameBuilder = null;
406 beanInfos = null;
407 }
409 /**
410 * True if this JAXBContext has {@link XmlAttachmentRef}.
411 */
412 public boolean hasSwaRef() {
413 return hasSwaRef;
414 }
416 public RuntimeTypeInfoSet getRuntimeTypeInfoSet() {
417 try {
418 return getTypeInfoSet();
419 } catch (IllegalAnnotationsException e) {
420 // impossible, once the model is constructred
421 throw new AssertionError(e);
422 }
423 }
425 /**
426 * Creates a {@link RuntimeTypeInfoSet}.
427 */
428 public RuntimeTypeInfoSet getTypeInfoSet() throws IllegalAnnotationsException {
430 // check cache
431 if(typeInfoSetCache!=null) {
432 RuntimeTypeInfoSet r = typeInfoSetCache.get();
433 if(r!=null)
434 return r;
435 }
437 final RuntimeModelBuilder builder = new RuntimeModelBuilder(this,annotationReader,subclassReplacements,defaultNsUri);
439 IllegalAnnotationsException.Builder errorHandler = new IllegalAnnotationsException.Builder();
440 builder.setErrorHandler(errorHandler);
442 for( Class c : classes ) {
443 if(c==CompositeStructure.class)
444 // CompositeStructure doesn't have TypeInfo, so skip it.
445 // We'll add JaxBeanInfo for this later automatically
446 continue;
447 builder.getTypeInfo(new Ref<Type,Class>(c));
448 }
450 this.hasSwaRef |= builder.hasSwaRef;
451 RuntimeTypeInfoSet r = builder.link();
453 errorHandler.check();
454 assert r!=null : "if no error was reported, the link must be a success";
456 typeInfoSetCache = new WeakReference<RuntimeTypeInfoSet>(r);
458 return r;
459 }
462 public ElementBeanInfoImpl getElement(Class scope, QName name) {
463 Map<QName,ElementBeanInfoImpl> m = elements.get(scope);
464 if(m!=null) {
465 ElementBeanInfoImpl bi = m.get(name);
466 if(bi!=null)
467 return bi;
468 }
469 m = elements.get(null);
470 return m.get(name);
471 }
477 private ElementBeanInfoImpl getOrCreate( RuntimeElementInfo rei ) {
478 JaxBeanInfo bi = beanInfos.get(rei);
479 if(bi!=null) return (ElementBeanInfoImpl)bi;
481 // all elements share the same type, so we can't register them to beanInfoMap
482 return new ElementBeanInfoImpl(this, rei);
483 }
485 protected JaxBeanInfo getOrCreate( RuntimeEnumLeafInfo eli ) {
486 JaxBeanInfo bi = beanInfos.get(eli);
487 if(bi!=null) return bi;
488 bi = new LeafBeanInfoImpl(this,eli);
489 beanInfoMap.put(bi.jaxbType,bi);
490 return bi;
491 }
493 protected ClassBeanInfoImpl getOrCreate( RuntimeClassInfo ci ) {
494 ClassBeanInfoImpl bi = (ClassBeanInfoImpl)beanInfos.get(ci);
495 if(bi!=null) return bi;
496 bi = new ClassBeanInfoImpl(this,ci);
497 beanInfoMap.put(bi.jaxbType,bi);
498 return bi;
499 }
501 protected JaxBeanInfo getOrCreate( RuntimeArrayInfo ai ) {
502 JaxBeanInfo abi = beanInfos.get(ai);
503 if(abi!=null) return abi;
505 abi = new ArrayBeanInfoImpl(this,ai);
507 beanInfoMap.put(ai.getType(),abi);
508 return abi;
509 }
511 public JaxBeanInfo getOrCreate(RuntimeTypeInfo e) {
512 if(e instanceof RuntimeElementInfo)
513 return getOrCreate((RuntimeElementInfo)e);
514 if(e instanceof RuntimeClassInfo)
515 return getOrCreate((RuntimeClassInfo)e);
516 if(e instanceof RuntimeLeafInfo) {
517 JaxBeanInfo bi = beanInfos.get(e); // must have been created
518 assert bi!=null;
519 return bi;
520 }
521 if(e instanceof RuntimeArrayInfo)
522 return getOrCreate((RuntimeArrayInfo)e);
523 if(e.getType()==Object.class) {
524 // anyType
525 JaxBeanInfo bi = beanInfoMap.get(Object.class);
526 if(bi==null) {
527 bi = new AnyTypeBeanInfo(this,e);
528 beanInfoMap.put(Object.class,bi);
529 }
530 return bi;
531 }
533 throw new IllegalArgumentException();
534 }
536 /**
537 * Gets the {@link JaxBeanInfo} object that can handle
538 * the given JAXB-bound object.
539 *
540 * <p>
541 * This method traverses the base classes of the given object.
542 *
543 * @return null
544 * if <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>.
545 */
546 public final JaxBeanInfo getBeanInfo(Object o) {
547 // don't allow xs:anyType beanInfo to handle all the unbound objects
548 for( Class c=o.getClass(); c!=Object.class; c=c.getSuperclass()) {
549 JaxBeanInfo bi = beanInfoMap.get(c);
550 if(bi!=null) return bi;
551 }
552 if(o instanceof Element)
553 return beanInfoMap.get(Object.class); // return the BeanInfo for xs:anyType
554 for( Class c : o.getClass().getInterfaces()) {
555 JaxBeanInfo bi = beanInfoMap.get(c);
556 if(bi!=null) return bi;
557 }
558 return null;
559 }
561 /**
562 * Gets the {@link JaxBeanInfo} object that can handle
563 * the given JAXB-bound object.
564 *
565 * @param fatal
566 * if true, the failure to look up will throw an exception.
567 * Otherwise it will just return null.
568 */
569 public final JaxBeanInfo getBeanInfo(Object o,boolean fatal) throws JAXBException {
570 JaxBeanInfo bi = getBeanInfo(o);
571 if(bi!=null) return bi;
572 if(fatal) {
573 if(o instanceof Document)
574 throw new JAXBException(Messages.ELEMENT_NEEDED_BUT_FOUND_DOCUMENT.format(o.getClass()));
575 throw new JAXBException(Messages.UNKNOWN_CLASS.format(o.getClass()));
576 }
577 return null;
578 }
580 /**
581 * Gets the {@link JaxBeanInfo} object that can handle
582 * the given JAXB-bound class.
583 *
584 * <p>
585 * This method doesn't look for base classes.
586 *
587 * @return null
588 * if <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>.
589 */
590 public final <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz) {
591 return (JaxBeanInfo<T>)beanInfoMap.get(clazz);
592 }
594 /**
595 * Gets the {@link JaxBeanInfo} object that can handle
596 * the given JAXB-bound class.
597 *
598 * @param fatal
599 * if true, the failure to look up will throw an exception.
600 * Otherwise it will just return null.
601 */
602 public final <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz,boolean fatal) throws JAXBException {
603 JaxBeanInfo<T> bi = getBeanInfo(clazz);
604 if(bi!=null) return bi;
605 if(fatal)
606 throw new JAXBException(clazz.getName()+" is not known to this context");
607 return null;
608 }
610 /**
611 * Based on the tag name, determine what object to unmarshal,
612 * and then set a new object and its loader to the current unmarshaller state.
613 *
614 * @return
615 * null if the given name pair is not recognized.
616 */
617 public final Loader selectRootLoader( UnmarshallingContext.State state, TagName tag ) {
618 JaxBeanInfo beanInfo = rootMap.get(tag.uri,tag.local);
619 if(beanInfo==null)
620 return null;
622 return beanInfo.getLoader(this,true);
623 }
625 /**
626 * Gets the {@link JaxBeanInfo} for the given named XML Schema type.
627 *
628 * @return
629 * null if the type name is not recognized. For schema
630 * languages other than XML Schema, this method always
631 * returns null.
632 */
633 public JaxBeanInfo getGlobalType(QName name) {
634 return typeMap.get(name);
635 }
637 /**
638 * Finds a type name that this context recognizes which is
639 * "closest" to the given type name.
640 *
641 * <p>
642 * This method is used for error recovery.
643 */
644 public String getNearestTypeName(QName name) {
645 String[] all = new String[typeMap.size()];
646 int i=0;
647 for (QName qn : typeMap.keySet()) {
648 if(qn.getLocalPart().equals(name.getLocalPart()))
649 return qn.toString(); // probably a match, as people often gets confused about namespace.
650 all[i++] = qn.toString();
651 }
653 String nearest = EditDistance.findNearest(name.toString(), all);
655 if(EditDistance.editDistance(nearest,name.toString())>10)
656 return null; // too far apart.
658 return nearest;
659 }
661 /**
662 * Returns the set of valid root tag names.
663 * For diagnostic use.
664 */
665 public Set<QName> getValidRootNames() {
666 Set<QName> r = new TreeSet<QName>(QNAME_COMPARATOR);
667 for (QNameMap.Entry e : rootMap.entrySet()) {
668 r.add(e.createQName());
669 }
670 return r;
671 }
673 /**
674 * Cache of UTF-8 encoded local names to improve the performance for the marshalling.
675 */
676 private Encoded[] utf8nameTable;
678 public synchronized Encoded[] getUTF8NameTable() {
679 if(utf8nameTable==null) {
680 Encoded[] x = new Encoded[nameList.localNames.length];
681 for( int i=0; i<x.length; i++ ) {
682 Encoded e = new Encoded(nameList.localNames[i]);
683 e.compact();
684 x[i] = e;
685 }
686 utf8nameTable = x;
687 }
688 return utf8nameTable;
689 }
691 public int getNumberOfLocalNames() {
692 return nameList.localNames.length;
693 }
695 public int getNumberOfElementNames() {
696 return nameList.numberOfElementNames;
697 }
699 public int getNumberOfAttributeNames() {
700 return nameList.numberOfAttributeNames;
701 }
703 /**
704 * Creates a new identity transformer.
705 */
706 static Transformer createTransformer(boolean disableSecureProcessing) {
707 try {
708 if (tf==null) {
709 synchronized(JAXBContextImpl.class) {
710 if (tf==null) {
711 tf = (SAXTransformerFactory)XmlFactory.createTransformerFactory(disableSecureProcessing);
712 }
713 }
714 }
715 return tf.newTransformer();
716 } catch (TransformerConfigurationException e) {
717 throw new Error(e); // impossible
718 }
719 }
721 /**
722 * Creates a new identity transformer.
723 */
724 public static TransformerHandler createTransformerHandler(boolean disableSecureProcessing) {
725 try {
726 if (tf==null) {
727 synchronized(JAXBContextImpl.class) {
728 if (tf==null) {
729 tf = (SAXTransformerFactory)XmlFactory.createTransformerFactory(disableSecureProcessing);
730 }
731 }
732 }
733 return tf.newTransformerHandler();
734 } catch (TransformerConfigurationException e) {
735 throw new Error(e); // impossible
736 }
737 }
739 /**
740 * Creates a new DOM document.
741 */
742 static Document createDom(boolean disableSecurityProcessing) {
743 synchronized(JAXBContextImpl.class) {
744 if(db==null) {
745 try {
746 DocumentBuilderFactory dbf = XmlFactory.createDocumentBuilderFactory(disableSecurityProcessing);
747 db = dbf.newDocumentBuilder();
748 } catch (ParserConfigurationException e) {
749 // impossible
750 throw new FactoryConfigurationError(e);
751 }
752 }
753 return db.newDocument();
754 }
755 }
757 public MarshallerImpl createMarshaller() {
758 return new MarshallerImpl(this,null);
759 }
761 public UnmarshallerImpl createUnmarshaller() {
762 return new UnmarshallerImpl(this,null);
763 }
765 public Validator createValidator() {
766 throw new UnsupportedOperationException(Messages.NOT_IMPLEMENTED_IN_2_0.format());
767 }
769 @Override
770 public JAXBIntrospector createJAXBIntrospector() {
771 return new JAXBIntrospector() {
772 public boolean isElement(Object object) {
773 return getElementName(object)!=null;
774 }
776 public QName getElementName(Object jaxbElement) {
777 try {
778 return JAXBContextImpl.this.getElementName(jaxbElement);
779 } catch (JAXBException e) {
780 return null;
781 }
782 }
783 };
784 }
786 private NonElement<Type,Class> getXmlType(RuntimeTypeInfoSet tis, TypeReference tr) {
787 if(tr==null)
788 throw new IllegalArgumentException();
790 XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class);
791 XmlList xl = tr.get(XmlList.class);
793 Ref<Type,Class> ref = new Ref<Type,Class>(annotationReader, tis.getNavigator(), tr.type, xjta, xl );
795 return tis.getTypeInfo(ref);
796 }
798 @Override
799 public void generateEpisode(Result output) {
800 if(output==null)
801 throw new IllegalArgumentException();
802 createSchemaGenerator().writeEpisodeFile(ResultFactory.createSerializer(output));
803 }
805 @Override
806 @SuppressWarnings("ThrowableInitCause")
807 public void generateSchema(SchemaOutputResolver outputResolver) throws IOException {
808 if(outputResolver==null)
809 throw new IOException(Messages.NULL_OUTPUT_RESOLVER.format());
811 final SAXParseException[] e = new SAXParseException[1];
812 final SAXParseException[] w = new SAXParseException[1];
814 createSchemaGenerator().write(outputResolver, new ErrorListener() {
815 public void error(SAXParseException exception) {
816 e[0] = exception;
817 }
819 public void fatalError(SAXParseException exception) {
820 e[0] = exception;
821 }
823 public void warning(SAXParseException exception) {
824 w[0] = exception;
825 }
827 public void info(SAXParseException exception) {}
828 });
830 if (e[0]!=null) {
831 IOException x = new IOException(Messages.FAILED_TO_GENERATE_SCHEMA.format());
832 x.initCause(e[0]);
833 throw x;
834 }
835 if (w[0]!=null) {
836 IOException x = new IOException(Messages.ERROR_PROCESSING_SCHEMA.format());
837 x.initCause(w[0]);
838 throw x;
839 }
840 }
842 private XmlSchemaGenerator<Type,Class,Field,Method> createSchemaGenerator() {
843 RuntimeTypeInfoSet tis;
844 try {
845 tis = getTypeInfoSet();
846 } catch (IllegalAnnotationsException e) {
847 // this shouldn't happen because we've already
848 throw new AssertionError(e);
849 }
851 XmlSchemaGenerator<Type,Class,Field,Method> xsdgen =
852 new XmlSchemaGenerator<Type,Class,Field,Method>(tis.getNavigator(),tis);
854 // JAX-RPC uses Bridge objects that collide with
855 // @XmlRootElement.
856 // we will avoid collision here
857 Set<QName> rootTagNames = new HashSet<QName>();
858 for (RuntimeElementInfo ei : tis.getAllElements()) {
859 rootTagNames.add(ei.getElementName());
860 }
861 for (RuntimeClassInfo ci : tis.beans().values()) {
862 if(ci.isElement())
863 rootTagNames.add(ci.asElement().getElementName());
864 }
866 for (TypeReference tr : bridges.keySet()) {
867 if(rootTagNames.contains(tr.tagName))
868 continue;
870 if(tr.type==void.class || tr.type==Void.class) {
871 xsdgen.add(tr.tagName,false,null);
872 } else
873 if(tr.type==CompositeStructure.class) {
874 // this is a special class we introduced for JAX-WS that we *don't* want in the schema
875 } else {
876 NonElement<Type,Class> typeInfo = getXmlType(tis,tr);
877 xsdgen.add(tr.tagName, !tis.getNavigator().isPrimitive(tr.type),typeInfo);
878 }
879 }
880 return xsdgen;
881 }
883 public QName getTypeName(TypeReference tr) {
884 try {
885 NonElement<Type,Class> xt = getXmlType(getTypeInfoSet(),tr);
886 if(xt==null) throw new IllegalArgumentException();
887 return xt.getTypeName();
888 } catch (IllegalAnnotationsException e) {
889 // impossible given that JAXBRIContext has been successfully built in the first place
890 throw new AssertionError(e);
891 }
892 }
894 @Override
895 public <T> Binder<T> createBinder(Class<T> domType) {
896 if(domType==Node.class)
897 return (Binder<T>)createBinder();
898 else
899 return super.createBinder(domType);
900 }
902 @Override
903 public Binder<Node> createBinder() {
904 return new BinderImpl<Node>(this,new DOMScanner());
905 }
907 public QName getElementName(Object o) throws JAXBException {
908 JaxBeanInfo bi = getBeanInfo(o,true);
909 if(!bi.isElement())
910 return null;
911 return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o));
912 }
914 public QName getElementName(Class o) throws JAXBException {
915 JaxBeanInfo bi = getBeanInfo(o,true);
916 if(!bi.isElement())
917 return null;
918 return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o));
919 }
921 public Bridge createBridge(TypeReference ref) {
922 return bridges.get(ref);
923 }
925 public @NotNull BridgeContext createBridgeContext() {
926 return new BridgeContextImpl(this);
927 }
929 public RawAccessor getElementPropertyAccessor(Class wrapperBean, String nsUri, String localName) throws JAXBException {
930 JaxBeanInfo bi = getBeanInfo(wrapperBean,true);
931 if(!(bi instanceof ClassBeanInfoImpl))
932 throw new JAXBException(wrapperBean+" is not a bean");
934 for( ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi; cb!=null; cb=cb.superClazz) {
935 for (Property p : cb.properties) {
936 final Accessor acc = p.getElementPropertyAccessor(nsUri,localName);
937 if(acc!=null)
938 return new RawAccessor() {
939 // Accessor.set/get are designed for unmarshaller/marshaller, and hence
940 // they go through an adapter behind the scene.
941 // this isn't desirable for JAX-WS, which essentially uses this method
942 // just as a reflection library. So use the "unadapted" version to
943 // achieve the desired semantics
944 public Object get(Object bean) throws AccessorException {
945 return acc.getUnadapted(bean);
946 }
948 public void set(Object bean, Object value) throws AccessorException {
949 acc.setUnadapted(bean,value);
950 }
951 };
952 }
953 }
954 throw new JAXBException(new QName(nsUri,localName)+" is not a valid property on "+wrapperBean);
955 }
957 public List<String> getKnownNamespaceURIs() {
958 return Arrays.asList(nameList.namespaceURIs);
959 }
961 public String getBuildId() {
962 Package pkg = getClass().getPackage();
963 if(pkg==null) return null;
964 return pkg.getImplementationVersion();
965 }
967 @Override
968 public String toString() {
969 StringBuilder buf = new StringBuilder(Which.which(getClass()) + " Build-Id: " + getBuildId());
970 buf.append("\nClasses known to this context:\n");
972 Set<String> names = new TreeSet<String>(); // sort them so that it's easy to read
974 for (Class key : beanInfoMap.keySet())
975 names.add(key.getName());
977 for(String name: names)
978 buf.append(" ").append(name).append('\n');
980 return buf.toString();
981 }
983 /**
984 * Gets the value of the xmime:contentType attribute on the given object, or null
985 * if for some reason it couldn't be found, including any error.
986 */
987 public String getXMIMEContentType( Object o ) {
988 JaxBeanInfo bi = getBeanInfo(o);
989 if(!(bi instanceof ClassBeanInfoImpl))
990 return null;
992 ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi;
993 for (Property p : cb.properties) {
994 if (p instanceof AttributeProperty) {
995 AttributeProperty ap = (AttributeProperty) p;
996 if(ap.attName.equals(WellKnownNamespace.XML_MIME_URI,"contentType"))
997 try {
998 return (String)ap.xacc.print(o);
999 } catch (AccessorException e) {
1000 return null;
1001 } catch (SAXException e) {
1002 return null;
1003 } catch (ClassCastException e) {
1004 return null;
1005 }
1006 }
1007 }
1008 return null;
1009 }
1011 /**
1012 * Creates a {@link JAXBContextImpl} that includes the specified additional classes.
1013 */
1014 public JAXBContextImpl createAugmented(Class<?> clazz) throws JAXBException {
1015 Class[] newList = new Class[classes.length+1];
1016 System.arraycopy(classes,0,newList,0,classes.length);
1017 newList[classes.length] = clazz;
1019 JAXBContextBuilder builder = new JAXBContextBuilder(this);
1020 builder.setClasses(newList);
1021 return builder.build();
1022 }
1024 private static final Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() {
1025 public int compare(QName lhs, QName rhs) {
1026 int r = lhs.getLocalPart().compareTo(rhs.getLocalPart());
1027 if(r!=0) return r;
1029 return lhs.getNamespaceURI().compareTo(rhs.getNamespaceURI());
1030 }
1031 };
1033 public static class JAXBContextBuilder {
1035 private boolean retainPropertyInfo = false;
1036 private boolean supressAccessorWarnings = false;
1037 private String defaultNsUri = "";
1038 private @NotNull RuntimeAnnotationReader annotationReader = new RuntimeInlineAnnotationReader();
1039 private @NotNull Map<Class,Class> subclassReplacements = Collections.emptyMap();
1040 private boolean c14nSupport = false;
1041 private Class[] classes;
1042 private Collection<TypeReference> typeRefs;
1043 private boolean xmlAccessorFactorySupport = false;
1044 private boolean allNillable;
1045 private boolean improvedXsiTypeHandling = true;
1046 private boolean disableSecurityProcessing = true;
1048 public JAXBContextBuilder() {};
1050 public JAXBContextBuilder(JAXBContextImpl baseImpl) {
1051 this.supressAccessorWarnings = baseImpl.supressAccessorWarnings;
1052 this.retainPropertyInfo = baseImpl.retainPropertyInfo;
1053 this.defaultNsUri = baseImpl.defaultNsUri;
1054 this.annotationReader = baseImpl.annotationReader;
1055 this.subclassReplacements = baseImpl.subclassReplacements;
1056 this.c14nSupport = baseImpl.c14nSupport;
1057 this.classes = baseImpl.classes;
1058 this.typeRefs = baseImpl.bridges.keySet();
1059 this.xmlAccessorFactorySupport = baseImpl.xmlAccessorFactorySupport;
1060 this.allNillable = baseImpl.allNillable;
1061 this.disableSecurityProcessing = baseImpl.disableSecurityProcessing;
1062 }
1064 public JAXBContextBuilder setRetainPropertyInfo(boolean val) {
1065 this.retainPropertyInfo = val;
1066 return this;
1067 }
1069 public JAXBContextBuilder setSupressAccessorWarnings(boolean val) {
1070 this.supressAccessorWarnings = val;
1071 return this;
1072 }
1074 public JAXBContextBuilder setC14NSupport(boolean val) {
1075 this.c14nSupport = val;
1076 return this;
1077 }
1079 public JAXBContextBuilder setXmlAccessorFactorySupport(boolean val) {
1080 this.xmlAccessorFactorySupport = val;
1081 return this;
1082 }
1084 public JAXBContextBuilder setDefaultNsUri(String val) {
1085 this.defaultNsUri = val;
1086 return this;
1087 }
1089 public JAXBContextBuilder setAllNillable(boolean val) {
1090 this.allNillable = val;
1091 return this;
1092 }
1094 public JAXBContextBuilder setClasses(Class[] val) {
1095 this.classes = val;
1096 return this;
1097 }
1099 public JAXBContextBuilder setAnnotationReader(RuntimeAnnotationReader val) {
1100 this.annotationReader = val;
1101 return this;
1102 }
1104 public JAXBContextBuilder setSubclassReplacements(Map<Class,Class> val) {
1105 this.subclassReplacements = val;
1106 return this;
1107 }
1109 public JAXBContextBuilder setTypeRefs(Collection<TypeReference> val) {
1110 this.typeRefs = val;
1111 return this;
1112 }
1114 public JAXBContextBuilder setImprovedXsiTypeHandling(boolean val) {
1115 this.improvedXsiTypeHandling = val;
1116 return this;
1117 }
1119 public JAXBContextBuilder setDisableSecurityProcessing(boolean val) {
1120 this.disableSecurityProcessing = val;
1121 return this;
1122 }
1124 public JAXBContextImpl build() throws JAXBException {
1126 // fool-proof
1127 if (this.defaultNsUri == null) {
1128 this.defaultNsUri = "";
1129 }
1131 if (this.subclassReplacements == null) {
1132 this.subclassReplacements = Collections.emptyMap();
1133 }
1135 if (this.annotationReader == null) {
1136 this.annotationReader = new RuntimeInlineAnnotationReader();
1137 }
1139 if (this.typeRefs == null) {
1140 this.typeRefs = Collections.<TypeReference>emptyList();
1141 }
1143 return new JAXBContextImpl(this);
1144 }
1146 }
1148 }