ohair@286: /* ohair@286: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.bind.v2.runtime; ohair@286: ohair@286: import java.io.IOException; ohair@286: import java.lang.ref.WeakReference; ohair@286: import java.lang.reflect.Field; ohair@286: import java.lang.reflect.Method; ohair@286: import java.lang.reflect.Type; ohair@286: import java.util.Arrays; ohair@286: import java.util.Collection; ohair@286: import java.util.Collections; ohair@286: import java.util.Comparator; ohair@286: import java.util.HashMap; ohair@286: import java.util.HashSet; ohair@286: import java.util.LinkedHashMap; ohair@286: import java.util.List; ohair@286: import java.util.Map; ohair@286: import java.util.Map.Entry; ohair@286: import java.util.Set; ohair@286: import java.util.TreeSet; ohair@286: import javax.xml.bind.Binder; ohair@286: import javax.xml.bind.JAXBContext; ohair@286: import javax.xml.bind.JAXBElement; ohair@286: import javax.xml.bind.JAXBException; ohair@286: import javax.xml.bind.JAXBIntrospector; ohair@286: import javax.xml.bind.Marshaller; ohair@286: import javax.xml.bind.SchemaOutputResolver; ohair@286: import javax.xml.bind.Unmarshaller; ohair@286: import javax.xml.bind.Validator; ohair@286: import javax.xml.bind.annotation.XmlAttachmentRef; ohair@286: import javax.xml.bind.annotation.XmlList; ohair@286: import javax.xml.bind.annotation.XmlNs; ohair@286: import javax.xml.bind.annotation.XmlSchema; ohair@286: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; ohair@286: import javax.xml.namespace.QName; ohair@286: import javax.xml.parsers.DocumentBuilder; ohair@286: import javax.xml.parsers.DocumentBuilderFactory; ohair@286: import javax.xml.parsers.FactoryConfigurationError; ohair@286: import javax.xml.parsers.ParserConfigurationException; ohair@286: import javax.xml.transform.Result; ohair@286: import javax.xml.transform.Transformer; ohair@286: import javax.xml.transform.TransformerConfigurationException; ohair@286: import javax.xml.transform.TransformerFactory; ohair@286: import javax.xml.transform.sax.SAXResult; ohair@286: import javax.xml.transform.sax.SAXTransformerFactory; ohair@286: import javax.xml.transform.sax.TransformerHandler; ohair@286: ohair@286: import com.sun.istack.internal.NotNull; ohair@286: import com.sun.istack.internal.Pool; ohair@286: import com.sun.xml.internal.bind.api.AccessorException; ohair@286: import com.sun.xml.internal.bind.api.Bridge; ohair@286: import com.sun.xml.internal.bind.api.BridgeContext; ohair@286: import com.sun.xml.internal.bind.api.CompositeStructure; ohair@286: import com.sun.xml.internal.bind.api.ErrorListener; ohair@286: import com.sun.xml.internal.bind.api.JAXBRIContext; ohair@286: import com.sun.xml.internal.bind.api.RawAccessor; ohair@286: import com.sun.xml.internal.bind.api.TypeReference; ohair@286: import com.sun.xml.internal.bind.unmarshaller.DOMScanner; ohair@286: import com.sun.xml.internal.bind.util.Which; ohair@286: import com.sun.xml.internal.bind.v2.WellKnownNamespace; ohair@286: import com.sun.xml.internal.bind.v2.model.annotation.RuntimeAnnotationReader; ohair@286: import com.sun.xml.internal.bind.v2.model.annotation.RuntimeInlineAnnotationReader; ohair@286: import com.sun.xml.internal.bind.v2.model.core.Adapter; ohair@286: import com.sun.xml.internal.bind.v2.model.core.NonElement; ohair@286: import com.sun.xml.internal.bind.v2.model.core.Ref; ohair@286: import com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl; ohair@286: import com.sun.xml.internal.bind.v2.model.impl.RuntimeModelBuilder; ohair@286: import com.sun.xml.internal.bind.v2.model.nav.Navigator; ohair@286: import com.sun.xml.internal.bind.v2.model.nav.ReflectionNavigator; ohair@286: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeArrayInfo; ohair@286: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeBuiltinLeafInfo; ohair@286: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo; ohair@286: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElementInfo; ohair@286: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeEnumLeafInfo; ohair@286: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeLeafInfo; ohair@286: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfo; ohair@286: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfoSet; ohair@286: import com.sun.xml.internal.bind.v2.runtime.output.Encoded; ohair@286: import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty; ohair@286: import com.sun.xml.internal.bind.v2.runtime.property.Property; ohair@286: import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor; ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader; ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.TagName; ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl; ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext; ohair@286: import com.sun.xml.internal.bind.v2.schemagen.XmlSchemaGenerator; ohair@286: import com.sun.xml.internal.bind.v2.util.EditDistance; ohair@286: import com.sun.xml.internal.bind.v2.util.QNameMap; ohair@286: import com.sun.xml.internal.txw2.output.ResultFactory; ohair@286: ohair@286: import org.w3c.dom.Document; ohair@286: import org.w3c.dom.Element; ohair@286: import org.w3c.dom.Node; ohair@286: import org.xml.sax.SAXException; ohair@286: import org.xml.sax.SAXParseException; ohair@286: import org.xml.sax.helpers.DefaultHandler; ohair@286: ohair@286: /** ohair@286: * This class provides the implementation of JAXBContext. ohair@286: * ohair@286: */ ohair@286: public final class JAXBContextImpl extends JAXBRIContext { ohair@286: ohair@286: /** ohair@286: * All the bridge classes. ohair@286: */ ohair@286: private final Map bridges = new LinkedHashMap(); ohair@286: ohair@286: /** ohair@286: * Shared instance of {@link TransformerFactory}. ohair@286: * Lock before use, because a {@link TransformerFactory} is not thread-safe ohair@286: * whereas {@link JAXBContextImpl} is. ohair@286: * Lazily created. ohair@286: */ ohair@286: private volatile static SAXTransformerFactory tf; ohair@286: ohair@286: /** ohair@286: * Shared instance of {@link DocumentBuilder}. ohair@286: * Lock before use. Lazily created. ohair@286: */ ohair@286: private static DocumentBuilder db; ohair@286: ohair@286: private final QNameMap rootMap = new QNameMap(); ohair@286: private final HashMap typeMap = new HashMap(); ohair@286: ohair@286: /** ohair@286: * Map from JAXB-bound {@link Class} to its {@link JaxBeanInfo}. ohair@286: */ ohair@286: private final Map beanInfoMap = new LinkedHashMap(); ohair@286: ohair@286: /** ohair@286: * All created {@link JaxBeanInfo}s. ohair@286: * Updated from each {@link JaxBeanInfo}s constructors to avoid infinite recursion ohair@286: * for a cyclic reference. ohair@286: * ohair@286: *

ohair@286: * This map is only used while the {@link JAXBContextImpl} is built and set to null ohair@286: * to avoid keeping references too long. ohair@286: */ ohair@286: protected Map beanInfos = new LinkedHashMap(); ohair@286: ohair@286: private final Map> elements = new LinkedHashMap>(); ohair@286: ohair@286: /** ohair@286: * Pool of {@link Marshaller}s. ohair@286: */ ohair@286: public final Pool marshallerPool = new Pool.Impl() { ohair@286: protected @NotNull Marshaller create() { ohair@286: return createMarshaller(); ohair@286: } ohair@286: }; ohair@286: ohair@286: public final Pool unmarshallerPool = new Pool.Impl() { ohair@286: protected @NotNull Unmarshaller create() { ohair@286: return createUnmarshaller(); ohair@286: } ohair@286: }; ohair@286: ohair@286: /** ohair@286: * Used to assign indices to known names in this grammar. ohair@286: * Reset to null once the build phase is completed. ohair@286: */ ohair@286: public NameBuilder nameBuilder = new NameBuilder(); ohair@286: ohair@286: /** ohair@286: * Keeps the list of known names. ohair@286: * This field is set once the build pahse is completed. ohair@286: */ ohair@286: public final NameList nameList; ohair@286: ohair@286: /** ohair@286: * Input to the JAXBContext.newInstance, so that we can recreate ohair@286: * {@link RuntimeTypeInfoSet} whenever we need. ohair@286: */ ohair@286: private final String defaultNsUri; ohair@286: private final Class[] classes; ohair@286: ohair@286: /** ohair@286: * true to reorder attributes lexicographically in preparation of the c14n support. ohair@286: */ ohair@286: protected final boolean c14nSupport; ohair@286: ohair@286: /** ohair@286: * Flag that user has provided a custom AccessorFactory for JAXB to use ohair@286: */ ohair@286: public final boolean xmlAccessorFactorySupport; ohair@286: ohair@286: /** ohair@286: * @see JAXBRIContext#TREAT_EVERYTHING_NILLABLE ohair@286: */ ohair@286: public final boolean allNillable; ohair@286: ohair@286: /** ohair@286: * Store properties, so that they can be recovered in the run (is here because of JSON encoding of Jersey). ohair@286: */ ohair@286: public final boolean retainPropertyInfo; ohair@286: ohair@286: /** ohair@286: * Supress reflection accessor warnings. ohair@286: */ ohair@286: public final boolean supressAccessorWarnings; ohair@286: ohair@286: /** ohair@286: * Improved xsi type handling. ohair@286: */ ohair@286: public final boolean improvedXsiTypeHandling; ohair@286: ohair@286: private WeakReference typeInfoSetCache; ohair@286: ohair@286: private @NotNull RuntimeAnnotationReader annotationReader; ohair@286: ohair@286: private /*almost final*/ boolean hasSwaRef; ohair@286: private final @NotNull Map subclassReplacements; ohair@286: ohair@286: /** ohair@286: * If true, we aim for faster {@link JAXBContext} instanciation performance, ohair@286: * instead of going after efficient sustained unmarshalling/marshalling performance. ohair@286: * ohair@286: * @since 2.0.4 ohair@286: */ ohair@286: public final boolean fastBoot; ohair@286: ohair@286: private Set xmlNsSet = null; ohair@286: ohair@286: /** ohair@286: * Returns declared XmlNs annotations (from package-level annotation XmlSchema ohair@286: * ohair@286: * @return set of all present XmlNs annotations ohair@286: */ ohair@286: public Set getXmlNsSet() { ohair@286: return xmlNsSet; ohair@286: } ohair@286: ohair@286: private JAXBContextImpl(JAXBContextBuilder builder) throws JAXBException { ohair@286: ohair@286: this.defaultNsUri = builder.defaultNsUri; ohair@286: this.retainPropertyInfo = builder.retainPropertyInfo; ohair@286: this.annotationReader = builder.annotationReader; ohair@286: this.subclassReplacements = builder.subclassReplacements; ohair@286: this.c14nSupport = builder.c14nSupport; ohair@286: this.classes = builder.classes; ohair@286: this.xmlAccessorFactorySupport = builder.xmlAccessorFactorySupport; ohair@286: this.allNillable = builder.allNillable; ohair@286: this.supressAccessorWarnings = builder.supressAccessorWarnings; ohair@286: this.improvedXsiTypeHandling = builder.improvedXsiTypeHandling; ohair@286: ohair@286: Collection typeRefs = builder.typeRefs; ohair@286: ohair@286: boolean fastB; ohair@286: try { ohair@286: fastB = Boolean.getBoolean(JAXBContextImpl.class.getName()+".fastBoot"); ohair@286: } catch (SecurityException e) { ohair@286: fastB = false; ohair@286: } ohair@286: this.fastBoot = fastB; ohair@286: ohair@286: System.arraycopy(classes,0,this.classes,0,classes.length); ohair@286: ohair@286: RuntimeTypeInfoSet typeSet = getTypeInfoSet(); ohair@286: ohair@286: // at least prepare the empty table so that we don't have to check for null later ohair@286: elements.put(null,new LinkedHashMap()); ohair@286: ohair@286: // recognize leaf bean infos ohair@286: for( RuntimeBuiltinLeafInfo leaf : RuntimeBuiltinLeafInfoImpl.builtinBeanInfos ) { ohair@286: LeafBeanInfoImpl bi = new LeafBeanInfoImpl(this,leaf); ohair@286: beanInfoMap.put(leaf.getClazz(),bi); ohair@286: for( QName t : bi.getTypeNames() ) ohair@286: typeMap.put(t,bi); ohair@286: } ohair@286: ohair@286: for (RuntimeEnumLeafInfo e : typeSet.enums().values()) { ohair@286: JaxBeanInfo bi = getOrCreate(e); ohair@286: for (QName qn : bi.getTypeNames()) ohair@286: typeMap.put( qn, bi ); ohair@286: if(e.isElement()) ohair@286: rootMap.put( e.getElementName(), bi ); ohair@286: } ohair@286: ohair@286: for (RuntimeArrayInfo a : typeSet.arrays().values()) { ohair@286: JaxBeanInfo ai = getOrCreate(a); ohair@286: for (QName qn : ai.getTypeNames()) ohair@286: typeMap.put( qn, ai ); ohair@286: } ohair@286: ohair@286: for( Entry e : typeSet.beans().entrySet() ) { ohair@286: ClassBeanInfoImpl bi = getOrCreate(e.getValue()); ohair@286: ohair@286: XmlSchema xs = this.annotationReader.getPackageAnnotation(XmlSchema.class, e.getKey(), null); ohair@286: if(xs != null) { ohair@286: if(xs.xmlns() != null && xs.xmlns().length > 0) { ohair@286: if(xmlNsSet == null) ohair@286: xmlNsSet = new HashSet(); ohair@286: xmlNsSet.addAll(Arrays.asList(xs.xmlns())); ohair@286: } ohair@286: } ohair@286: ohair@286: if(bi.isElement()) ohair@286: rootMap.put( e.getValue().getElementName(), bi ); ohair@286: ohair@286: for (QName qn : bi.getTypeNames()) ohair@286: typeMap.put( qn, bi ); ohair@286: } ohair@286: ohair@286: // fill in element mappings ohair@286: for( RuntimeElementInfo n : typeSet.getAllElements() ) { ohair@286: ElementBeanInfoImpl bi = getOrCreate(n); ohair@286: if(n.getScope()==null) ohair@286: rootMap.put(n.getElementName(),bi); ohair@286: ohair@286: RuntimeClassInfo scope = n.getScope(); ohair@286: Class scopeClazz = scope==null?null:scope.getClazz(); ohair@286: Map m = elements.get(scopeClazz); ohair@286: if(m==null) { ohair@286: m = new LinkedHashMap(); ohair@286: elements.put(scopeClazz,m); ohair@286: } ohair@286: m.put(n.getElementName(),bi); ohair@286: } ohair@286: ohair@286: // this one is so that we can handle plain JAXBElements. ohair@286: beanInfoMap.put(JAXBElement.class,new ElementBeanInfoImpl(this)); ohair@286: // another special BeanInfoImpl just for marshalling ohair@286: beanInfoMap.put(CompositeStructure.class,new CompositeStructureBeanInfo(this)); ohair@286: ohair@286: getOrCreate(typeSet.getAnyTypeInfo()); ohair@286: ohair@286: // then link them all! ohair@286: for (JaxBeanInfo bi : beanInfos.values()) ohair@286: bi.link(this); ohair@286: ohair@286: // register primitives for boxed types just to make GrammarInfo fool-proof ohair@286: for( Map.Entry e : RuntimeUtil.primitiveToBox.entrySet() ) ohair@286: beanInfoMap.put( e.getKey(), beanInfoMap.get(e.getValue()) ); ohair@286: ohair@286: // build bridges ohair@286: ReflectionNavigator nav = typeSet.getNavigator(); ohair@286: ohair@286: for (TypeReference tr : typeRefs) { ohair@286: XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class); ohair@286: Adapter a=null; ohair@286: XmlList xl = tr.get(XmlList.class); ohair@286: ohair@286: // eventually compute the in-memory type ohair@286: Class erasedType = nav.erasure(tr.type); ohair@286: ohair@286: if(xjta!=null) { ohair@286: a = new Adapter(xjta.value(),nav); ohair@286: } ohair@286: if(tr.get(XmlAttachmentRef.class)!=null) { ohair@286: a = new Adapter(SwaRefAdapter.class,nav); ohair@286: hasSwaRef = true; ohair@286: } ohair@286: ohair@286: if(a!=null) { ohair@286: erasedType = nav.erasure(a.defaultType); ohair@286: } ohair@286: ohair@286: Name name = nameBuilder.createElementName(tr.tagName); ohair@286: ohair@286: InternalBridge bridge; ohair@286: if(xl==null) ohair@286: bridge = new BridgeImpl(this, name,getBeanInfo(erasedType,true),tr); ohair@286: else ohair@286: bridge = new BridgeImpl(this, name,new ValueListBeanInfoImpl(this,erasedType),tr); ohair@286: ohair@286: if(a!=null) ohair@286: bridge = new BridgeAdapter(bridge,a.adapterType); ohair@286: ohair@286: bridges.put(tr,bridge); ohair@286: } ohair@286: ohair@286: this.nameList = nameBuilder.conclude(); ohair@286: ohair@286: for (JaxBeanInfo bi : beanInfos.values()) ohair@286: bi.wrapUp(); ohair@286: ohair@286: // no use for them now ohair@286: nameBuilder = null; ohair@286: beanInfos = null; ohair@286: } ohair@286: ohair@286: /** ohair@286: * True if this JAXBContext has {@link XmlAttachmentRef}. ohair@286: */ ohair@286: public boolean hasSwaRef() { ohair@286: return hasSwaRef; ohair@286: } ohair@286: ohair@286: public RuntimeTypeInfoSet getRuntimeTypeInfoSet() { ohair@286: try { ohair@286: return getTypeInfoSet(); ohair@286: } catch (IllegalAnnotationsException e) { ohair@286: // impossible, once the model is constructred ohair@286: throw new AssertionError(e); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Creates a {@link RuntimeTypeInfoSet}. ohair@286: */ ohair@286: public RuntimeTypeInfoSet getTypeInfoSet() throws IllegalAnnotationsException { ohair@286: ohair@286: // check cache ohair@286: if(typeInfoSetCache!=null) { ohair@286: RuntimeTypeInfoSet r = typeInfoSetCache.get(); ohair@286: if(r!=null) ohair@286: return r; ohair@286: } ohair@286: ohair@286: final RuntimeModelBuilder builder = new RuntimeModelBuilder(this,annotationReader,subclassReplacements,defaultNsUri); ohair@286: ohair@286: IllegalAnnotationsException.Builder errorHandler = new IllegalAnnotationsException.Builder(); ohair@286: builder.setErrorHandler(errorHandler); ohair@286: ohair@286: for( Class c : classes ) { ohair@286: if(c==CompositeStructure.class) ohair@286: // CompositeStructure doesn't have TypeInfo, so skip it. ohair@286: // We'll add JaxBeanInfo for this later automatically ohair@286: continue; ohair@286: builder.getTypeInfo(new Ref(c)); ohair@286: } ohair@286: ohair@286: this.hasSwaRef |= builder.hasSwaRef; ohair@286: RuntimeTypeInfoSet r = builder.link(); ohair@286: ohair@286: errorHandler.check(); ohair@286: assert r!=null : "if no error was reported, the link must be a success"; ohair@286: ohair@286: typeInfoSetCache = new WeakReference(r); ohair@286: ohair@286: return r; ohair@286: } ohair@286: ohair@286: ohair@286: public ElementBeanInfoImpl getElement(Class scope, QName name) { ohair@286: Map m = elements.get(scope); ohair@286: if(m!=null) { ohair@286: ElementBeanInfoImpl bi = m.get(name); ohair@286: if(bi!=null) ohair@286: return bi; ohair@286: } ohair@286: m = elements.get(null); ohair@286: return m.get(name); ohair@286: } ohair@286: ohair@286: ohair@286: ohair@286: ohair@286: ohair@286: private ElementBeanInfoImpl getOrCreate( RuntimeElementInfo rei ) { ohair@286: JaxBeanInfo bi = beanInfos.get(rei); ohair@286: if(bi!=null) return (ElementBeanInfoImpl)bi; ohair@286: ohair@286: // all elements share the same type, so we can't register them to beanInfoMap ohair@286: return new ElementBeanInfoImpl(this, rei); ohair@286: } ohair@286: ohair@286: protected JaxBeanInfo getOrCreate( RuntimeEnumLeafInfo eli ) { ohair@286: JaxBeanInfo bi = beanInfos.get(eli); ohair@286: if(bi!=null) return bi; ohair@286: bi = new LeafBeanInfoImpl(this,eli); ohair@286: beanInfoMap.put(bi.jaxbType,bi); ohair@286: return bi; ohair@286: } ohair@286: ohair@286: protected ClassBeanInfoImpl getOrCreate( RuntimeClassInfo ci ) { ohair@286: ClassBeanInfoImpl bi = (ClassBeanInfoImpl)beanInfos.get(ci); ohair@286: if(bi!=null) return bi; ohair@286: bi = new ClassBeanInfoImpl(this,ci); ohair@286: beanInfoMap.put(bi.jaxbType,bi); ohair@286: return bi; ohair@286: } ohair@286: ohair@286: protected JaxBeanInfo getOrCreate( RuntimeArrayInfo ai ) { ohair@286: JaxBeanInfo abi = beanInfos.get(ai); ohair@286: if(abi!=null) return abi; ohair@286: ohair@286: abi = new ArrayBeanInfoImpl(this,ai); ohair@286: ohair@286: beanInfoMap.put(ai.getType(),abi); ohair@286: return abi; ohair@286: } ohair@286: ohair@286: public JaxBeanInfo getOrCreate(RuntimeTypeInfo e) { ohair@286: if(e instanceof RuntimeElementInfo) ohair@286: return getOrCreate((RuntimeElementInfo)e); ohair@286: if(e instanceof RuntimeClassInfo) ohair@286: return getOrCreate((RuntimeClassInfo)e); ohair@286: if(e instanceof RuntimeLeafInfo) { ohair@286: JaxBeanInfo bi = beanInfos.get(e); // must have been created ohair@286: assert bi!=null; ohair@286: return bi; ohair@286: } ohair@286: if(e instanceof RuntimeArrayInfo) ohair@286: return getOrCreate((RuntimeArrayInfo)e); ohair@286: if(e.getType()==Object.class) { ohair@286: // anyType ohair@286: JaxBeanInfo bi = beanInfoMap.get(Object.class); ohair@286: if(bi==null) { ohair@286: bi = new AnyTypeBeanInfo(this,e); ohair@286: beanInfoMap.put(Object.class,bi); ohair@286: } ohair@286: return bi; ohair@286: } ohair@286: ohair@286: throw new IllegalArgumentException(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the {@link JaxBeanInfo} object that can handle ohair@286: * the given JAXB-bound object. ohair@286: * ohair@286: *

ohair@286: * This method traverses the base classes of the given object. ohair@286: * ohair@286: * @return null ohair@286: * if c isn't a JAXB-bound class and fatal==false. ohair@286: */ ohair@286: public final JaxBeanInfo getBeanInfo(Object o) { ohair@286: // don't allow xs:anyType beanInfo to handle all the unbound objects ohair@286: for( Class c=o.getClass(); c!=Object.class; c=c.getSuperclass()) { ohair@286: JaxBeanInfo bi = beanInfoMap.get(c); ohair@286: if(bi!=null) return bi; ohair@286: } ohair@286: if(o instanceof Element) ohair@286: return beanInfoMap.get(Object.class); // return the BeanInfo for xs:anyType ohair@286: for( Class c : o.getClass().getInterfaces()) { ohair@286: JaxBeanInfo bi = beanInfoMap.get(c); ohair@286: if(bi!=null) return bi; ohair@286: } ohair@286: return null; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the {@link JaxBeanInfo} object that can handle ohair@286: * the given JAXB-bound object. ohair@286: * ohair@286: * @param fatal ohair@286: * if true, the failure to look up will throw an exception. ohair@286: * Otherwise it will just return null. ohair@286: */ ohair@286: public final JaxBeanInfo getBeanInfo(Object o,boolean fatal) throws JAXBException { ohair@286: JaxBeanInfo bi = getBeanInfo(o); ohair@286: if(bi!=null) return bi; ohair@286: if(fatal) { ohair@286: if(o instanceof Document) ohair@286: throw new JAXBException(Messages.ELEMENT_NEEDED_BUT_FOUND_DOCUMENT.format(o.getClass())); ohair@286: throw new JAXBException(Messages.UNKNOWN_CLASS.format(o.getClass())); ohair@286: } ohair@286: return null; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the {@link JaxBeanInfo} object that can handle ohair@286: * the given JAXB-bound class. ohair@286: * ohair@286: *

ohair@286: * This method doesn't look for base classes. ohair@286: * ohair@286: * @return null ohair@286: * if c isn't a JAXB-bound class and fatal==false. ohair@286: */ ohair@286: public final JaxBeanInfo getBeanInfo(Class clazz) { ohair@286: return (JaxBeanInfo)beanInfoMap.get(clazz); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the {@link JaxBeanInfo} object that can handle ohair@286: * the given JAXB-bound class. ohair@286: * ohair@286: * @param fatal ohair@286: * if true, the failure to look up will throw an exception. ohair@286: * Otherwise it will just return null. ohair@286: */ ohair@286: public final JaxBeanInfo getBeanInfo(Class clazz,boolean fatal) throws JAXBException { ohair@286: JaxBeanInfo bi = getBeanInfo(clazz); ohair@286: if(bi!=null) return bi; ohair@286: if(fatal) ohair@286: throw new JAXBException(clazz.getName()+" is not known to this context"); ohair@286: return null; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Based on the tag name, determine what object to unmarshal, ohair@286: * and then set a new object and its loader to the current unmarshaller state. ohair@286: * ohair@286: * @return ohair@286: * null if the given name pair is not recognized. ohair@286: */ ohair@286: public final Loader selectRootLoader( UnmarshallingContext.State state, TagName tag ) { ohair@286: JaxBeanInfo beanInfo = rootMap.get(tag.uri,tag.local); ohair@286: if(beanInfo==null) ohair@286: return null; ohair@286: ohair@286: return beanInfo.getLoader(this,true); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the {@link JaxBeanInfo} for the given named XML Schema type. ohair@286: * ohair@286: * @return ohair@286: * null if the type name is not recognized. For schema ohair@286: * languages other than XML Schema, this method always ohair@286: * returns null. ohair@286: */ ohair@286: public JaxBeanInfo getGlobalType(QName name) { ohair@286: return typeMap.get(name); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Finds a type name that this context recognizes which is ohair@286: * "closest" to the given type name. ohair@286: * ohair@286: *

ohair@286: * This method is used for error recovery. ohair@286: */ ohair@286: public String getNearestTypeName(QName name) { ohair@286: String[] all = new String[typeMap.size()]; ohair@286: int i=0; ohair@286: for (QName qn : typeMap.keySet()) { ohair@286: if(qn.getLocalPart().equals(name.getLocalPart())) ohair@286: return qn.toString(); // probably a match, as people often gets confused about namespace. ohair@286: all[i++] = qn.toString(); ohair@286: } ohair@286: ohair@286: String nearest = EditDistance.findNearest(name.toString(), all); ohair@286: ohair@286: if(EditDistance.editDistance(nearest,name.toString())>10) ohair@286: return null; // too far apart. ohair@286: ohair@286: return nearest; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the set of valid root tag names. ohair@286: * For diagnostic use. ohair@286: */ ohair@286: public Set getValidRootNames() { ohair@286: Set r = new TreeSet(QNAME_COMPARATOR); ohair@286: for (QNameMap.Entry e : rootMap.entrySet()) { ohair@286: r.add(e.createQName()); ohair@286: } ohair@286: return r; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Cache of UTF-8 encoded local names to improve the performance for the marshalling. ohair@286: */ ohair@286: private Encoded[] utf8nameTable; ohair@286: ohair@286: public synchronized Encoded[] getUTF8NameTable() { ohair@286: if(utf8nameTable==null) { ohair@286: Encoded[] x = new Encoded[nameList.localNames.length]; ohair@286: for( int i=0; i getXmlType(RuntimeTypeInfoSet tis, TypeReference tr) { ohair@286: if(tr==null) ohair@286: throw new IllegalArgumentException(); ohair@286: ohair@286: XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class); ohair@286: XmlList xl = tr.get(XmlList.class); ohair@286: ohair@286: Ref ref = new Ref(annotationReader, tis.getNavigator(), tr.type, xjta, xl ); ohair@286: ohair@286: return tis.getTypeInfo(ref); ohair@286: } ohair@286: ohair@286: @Override ohair@286: public void generateEpisode(Result output) { ohair@286: if(output==null) ohair@286: throw new IllegalArgumentException(); ohair@286: createSchemaGenerator().writeEpisodeFile(ResultFactory.createSerializer(output)); ohair@286: } ohair@286: ohair@286: @Override ohair@286: @SuppressWarnings("ThrowableInitCause") ohair@286: public void generateSchema(SchemaOutputResolver outputResolver) throws IOException { ohair@286: if(outputResolver==null) ohair@286: throw new IOException(Messages.NULL_OUTPUT_RESOLVER.format()); ohair@286: ohair@286: final SAXParseException[] e = new SAXParseException[1]; ohair@286: final SAXParseException[] w = new SAXParseException[1]; ohair@286: ohair@286: createSchemaGenerator().write(outputResolver, new ErrorListener() { ohair@286: public void error(SAXParseException exception) { ohair@286: e[0] = exception; ohair@286: } ohair@286: ohair@286: public void fatalError(SAXParseException exception) { ohair@286: e[0] = exception; ohair@286: } ohair@286: ohair@286: public void warning(SAXParseException exception) { ohair@286: w[0] = exception; ohair@286: } ohair@286: ohair@286: public void info(SAXParseException exception) {} ohair@286: }); ohair@286: ohair@286: if (e[0]!=null) { ohair@286: IOException x = new IOException(Messages.FAILED_TO_GENERATE_SCHEMA.format()); ohair@286: x.initCause(e[0]); ohair@286: throw x; ohair@286: } ohair@286: if (w[0]!=null) { ohair@286: IOException x = new IOException(Messages.ERROR_PROCESSING_SCHEMA.format()); ohair@286: x.initCause(w[0]); ohair@286: throw x; ohair@286: } ohair@286: } ohair@286: ohair@286: private XmlSchemaGenerator createSchemaGenerator() { ohair@286: RuntimeTypeInfoSet tis; ohair@286: try { ohair@286: tis = getTypeInfoSet(); ohair@286: } catch (IllegalAnnotationsException e) { ohair@286: // this shouldn't happen because we've already ohair@286: throw new AssertionError(e); ohair@286: } ohair@286: ohair@286: XmlSchemaGenerator xsdgen = ohair@286: new XmlSchemaGenerator(tis.getNavigator(),tis); ohair@286: ohair@286: // JAX-RPC uses Bridge objects that collide with ohair@286: // @XmlRootElement. ohair@286: // we will avoid collision here ohair@286: Set rootTagNames = new HashSet(); ohair@286: for (RuntimeElementInfo ei : tis.getAllElements()) { ohair@286: rootTagNames.add(ei.getElementName()); ohair@286: } ohair@286: for (RuntimeClassInfo ci : tis.beans().values()) { ohair@286: if(ci.isElement()) ohair@286: rootTagNames.add(ci.asElement().getElementName()); ohair@286: } ohair@286: ohair@286: for (TypeReference tr : bridges.keySet()) { ohair@286: if(rootTagNames.contains(tr.tagName)) ohair@286: continue; ohair@286: ohair@286: if(tr.type==void.class || tr.type==Void.class) { ohair@286: xsdgen.add(tr.tagName,false,null); ohair@286: } else ohair@286: if(tr.type==CompositeStructure.class) { ohair@286: // this is a special class we introduced for JAX-WS that we *don't* want in the schema ohair@286: } else { ohair@286: NonElement typeInfo = getXmlType(tis,tr); ohair@286: xsdgen.add(tr.tagName, !Navigator.REFLECTION.isPrimitive(tr.type),typeInfo); ohair@286: } ohair@286: } ohair@286: return xsdgen; ohair@286: } ohair@286: ohair@286: public QName getTypeName(TypeReference tr) { ohair@286: try { ohair@286: NonElement xt = getXmlType(getTypeInfoSet(),tr); ohair@286: if(xt==null) throw new IllegalArgumentException(); ohair@286: return xt.getTypeName(); ohair@286: } catch (IllegalAnnotationsException e) { ohair@286: // impossible given that JAXBRIContext has been successfully built in the first place ohair@286: throw new AssertionError(e); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Used for testing. ohair@286: */ ohair@286: public SchemaOutputResolver createTestResolver() { ohair@286: return new SchemaOutputResolver() { ohair@286: public Result createOutput(String namespaceUri, String suggestedFileName) { ohair@286: SAXResult r = new SAXResult(new DefaultHandler()); ohair@286: r.setSystemId(suggestedFileName); ohair@286: return r; ohair@286: } ohair@286: }; ohair@286: } ohair@286: ohair@286: @Override ohair@286: public Binder createBinder(Class domType) { ohair@286: if(domType==Node.class) ohair@286: return (Binder)createBinder(); ohair@286: else ohair@286: return super.createBinder(domType); ohair@286: } ohair@286: ohair@286: @Override ohair@286: public Binder createBinder() { ohair@286: return new BinderImpl(this,new DOMScanner()); ohair@286: } ohair@286: ohair@286: public QName getElementName(Object o) throws JAXBException { ohair@286: JaxBeanInfo bi = getBeanInfo(o,true); ohair@286: if(!bi.isElement()) ohair@286: return null; ohair@286: return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o)); ohair@286: } ohair@286: ohair@286: public QName getElementName(Class o) throws JAXBException { ohair@286: JaxBeanInfo bi = getBeanInfo(o,true); ohair@286: if(!bi.isElement()) ohair@286: return null; ohair@286: return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o)); ohair@286: } ohair@286: ohair@286: public Bridge createBridge(TypeReference ref) { ohair@286: return bridges.get(ref); ohair@286: } ohair@286: ohair@286: public @NotNull BridgeContext createBridgeContext() { ohair@286: return new BridgeContextImpl(this); ohair@286: } ohair@286: ohair@286: public RawAccessor getElementPropertyAccessor(Class wrapperBean, String nsUri, String localName) throws JAXBException { ohair@286: JaxBeanInfo bi = getBeanInfo(wrapperBean,true); ohair@286: if(!(bi instanceof ClassBeanInfoImpl)) ohair@286: throw new JAXBException(wrapperBean+" is not a bean"); ohair@286: ohair@286: for( ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi; cb!=null; cb=cb.superClazz) { ohair@286: for (Property p : cb.properties) { ohair@286: final Accessor acc = p.getElementPropertyAccessor(nsUri,localName); ohair@286: if(acc!=null) ohair@286: return new RawAccessor() { ohair@286: // Accessor.set/get are designed for unmarshaller/marshaller, and hence ohair@286: // they go through an adapter behind the scene. ohair@286: // this isn't desirable for JAX-WS, which essentially uses this method ohair@286: // just as a reflection library. So use the "unadapted" version to ohair@286: // achieve the desired semantics ohair@286: public Object get(Object bean) throws AccessorException { ohair@286: return acc.getUnadapted(bean); ohair@286: } ohair@286: ohair@286: public void set(Object bean, Object value) throws AccessorException { ohair@286: acc.setUnadapted(bean,value); ohair@286: } ohair@286: }; ohair@286: } ohair@286: } ohair@286: throw new JAXBException(new QName(nsUri,localName)+" is not a valid property on "+wrapperBean); ohair@286: } ohair@286: ohair@286: public List getKnownNamespaceURIs() { ohair@286: return Arrays.asList(nameList.namespaceURIs); ohair@286: } ohair@286: ohair@286: public String getBuildId() { ohair@286: Package pkg = getClass().getPackage(); ohair@286: if(pkg==null) return null; ohair@286: return pkg.getImplementationVersion(); ohair@286: } ohair@286: ohair@286: @Override ohair@286: public String toString() { ohair@286: StringBuilder buf = new StringBuilder(Which.which(getClass()) + " Build-Id: " + getBuildId()); ohair@286: buf.append("\nClasses known to this context:\n"); ohair@286: ohair@286: Set names = new TreeSet(); // sort them so that it's easy to read ohair@286: ohair@286: for (Class key : beanInfoMap.keySet()) ohair@286: names.add(key.getName()); ohair@286: ohair@286: for(String name: names) ohair@286: buf.append(" ").append(name).append('\n'); ohair@286: ohair@286: return buf.toString(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the value of the xmime:contentType attribute on the given object, or null ohair@286: * if for some reason it couldn't be found, including any error. ohair@286: */ ohair@286: public String getXMIMEContentType( Object o ) { ohair@286: JaxBeanInfo bi = getBeanInfo(o); ohair@286: if(!(bi instanceof ClassBeanInfoImpl)) ohair@286: return null; ohair@286: ohair@286: ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi; ohair@286: for (Property p : cb.properties) { ohair@286: if (p instanceof AttributeProperty) { ohair@286: AttributeProperty ap = (AttributeProperty) p; ohair@286: if(ap.attName.equals(WellKnownNamespace.XML_MIME_URI,"contentType")) ohair@286: try { ohair@286: return (String)ap.xacc.print(o); ohair@286: } catch (AccessorException e) { ohair@286: return null; ohair@286: } catch (SAXException e) { ohair@286: return null; ohair@286: } catch (ClassCastException e) { ohair@286: return null; ohair@286: } ohair@286: } ohair@286: } ohair@286: return null; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Creates a {@link JAXBContextImpl} that includes the specified additional classes. ohair@286: */ ohair@286: public JAXBContextImpl createAugmented(Class clazz) throws JAXBException { ohair@286: Class[] newList = new Class[classes.length+1]; ohair@286: System.arraycopy(classes,0,newList,0,classes.length); ohair@286: newList[classes.length] = clazz; ohair@286: ohair@286: JAXBContextBuilder builder = new JAXBContextBuilder(this); ohair@286: builder.setClasses(newList); ohair@286: return builder.build(); ohair@286: } ohair@286: ohair@286: private static final Comparator QNAME_COMPARATOR = new Comparator() { ohair@286: public int compare(QName lhs, QName rhs) { ohair@286: int r = lhs.getLocalPart().compareTo(rhs.getLocalPart()); ohair@286: if(r!=0) return r; ohair@286: ohair@286: return lhs.getNamespaceURI().compareTo(rhs.getNamespaceURI()); ohair@286: } ohair@286: }; ohair@286: ohair@286: public static class JAXBContextBuilder { ohair@286: ohair@286: private boolean retainPropertyInfo = false; ohair@286: private boolean supressAccessorWarnings = false; ohair@286: private String defaultNsUri = ""; ohair@286: private @NotNull RuntimeAnnotationReader annotationReader = new RuntimeInlineAnnotationReader(); ohair@286: private @NotNull Map subclassReplacements = Collections.emptyMap(); ohair@286: private boolean c14nSupport = false; ohair@286: private Class[] classes; ohair@286: private Collection typeRefs; ohair@286: private boolean xmlAccessorFactorySupport = false; ohair@286: private boolean allNillable; ohair@286: private boolean improvedXsiTypeHandling = true; ohair@286: ohair@286: public JAXBContextBuilder() {}; ohair@286: ohair@286: public JAXBContextBuilder(JAXBContextImpl baseImpl) { ohair@286: this.supressAccessorWarnings = baseImpl.supressAccessorWarnings; ohair@286: this.retainPropertyInfo = baseImpl.retainPropertyInfo; ohair@286: this.defaultNsUri = baseImpl.defaultNsUri; ohair@286: this.annotationReader = baseImpl.annotationReader; ohair@286: this.subclassReplacements = baseImpl.subclassReplacements; ohair@286: this.c14nSupport = baseImpl.c14nSupport; ohair@286: this.classes = baseImpl.classes; ohair@286: this.typeRefs = baseImpl.bridges.keySet(); ohair@286: this.xmlAccessorFactorySupport = baseImpl.xmlAccessorFactorySupport; ohair@286: this.allNillable = baseImpl.allNillable; ohair@286: } ohair@286: ohair@286: public JAXBContextBuilder setRetainPropertyInfo(boolean val) { ohair@286: this.retainPropertyInfo = val; ohair@286: return this; ohair@286: } ohair@286: ohair@286: public JAXBContextBuilder setSupressAccessorWarnings(boolean val) { ohair@286: this.supressAccessorWarnings = val; ohair@286: return this; ohair@286: } ohair@286: ohair@286: public JAXBContextBuilder setC14NSupport(boolean val) { ohair@286: this.c14nSupport = val; ohair@286: return this; ohair@286: } ohair@286: ohair@286: public JAXBContextBuilder setXmlAccessorFactorySupport(boolean val) { ohair@286: this.xmlAccessorFactorySupport = val; ohair@286: return this; ohair@286: } ohair@286: ohair@286: public JAXBContextBuilder setDefaultNsUri(String val) { ohair@286: this.defaultNsUri = val; ohair@286: return this; ohair@286: } ohair@286: ohair@286: public JAXBContextBuilder setAllNillable(boolean val) { ohair@286: this.allNillable = val; ohair@286: return this; ohair@286: } ohair@286: ohair@286: public JAXBContextBuilder setClasses(Class[] val) { ohair@286: this.classes = val; ohair@286: return this; ohair@286: } ohair@286: ohair@286: public JAXBContextBuilder setAnnotationReader(RuntimeAnnotationReader val) { ohair@286: this.annotationReader = val; ohair@286: return this; ohair@286: } ohair@286: ohair@286: public JAXBContextBuilder setSubclassReplacements(Map val) { ohair@286: this.subclassReplacements = val; ohair@286: return this; ohair@286: } ohair@286: ohair@286: public JAXBContextBuilder setTypeRefs(Collection val) { ohair@286: this.typeRefs = val; ohair@286: return this; ohair@286: } ohair@286: ohair@286: public JAXBContextBuilder setImprovedXsiTypeHandling(boolean val) { ohair@286: this.improvedXsiTypeHandling = val; ohair@286: return this; ohair@286: } ohair@286: ohair@286: public JAXBContextImpl build() throws JAXBException { ohair@286: ohair@286: // fool-proof ohair@286: if (this.defaultNsUri == null) { ohair@286: this.defaultNsUri = ""; ohair@286: } ohair@286: ohair@286: if (this.subclassReplacements == null) { ohair@286: this.subclassReplacements = Collections.emptyMap(); ohair@286: } ohair@286: ohair@286: if (this.annotationReader == null) { ohair@286: this.annotationReader = new RuntimeInlineAnnotationReader(); ohair@286: } ohair@286: ohair@286: if (this.typeRefs == null) { ohair@286: this.typeRefs = Collections.emptyList(); ohair@286: } ohair@286: ohair@286: return new JAXBContextImpl(this); ohair@286: } ohair@286: ohair@286: } ohair@286: ohair@286: }