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