src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/JAXBContextImpl.java

Tue, 09 Apr 2013 14:51:13 +0100

author
alanb
date
Tue, 09 Apr 2013 14:51:13 +0100
changeset 368
0989ad8c0860
parent 286
f50545b5e2f1
child 384
8f2986ff0235
permissions
-rw-r--r--

8010393: Update JAX-WS RI to 2.2.9-b12941
Reviewed-by: alanb, erikj
Contributed-by: miroslav.kos@oracle.com, martin.grebac@oracle.com

     1 /*
     2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.xml.internal.bind.v2.runtime;
    28 import java.io.IOException;
    29 import java.lang.ref.WeakReference;
    30 import java.lang.reflect.Field;
    31 import java.lang.reflect.Method;
    32 import java.lang.reflect.Type;
    33 import java.util.Arrays;
    34 import java.util.Collection;
    35 import java.util.Collections;
    36 import java.util.Comparator;
    37 import java.util.HashMap;
    38 import java.util.HashSet;
    39 import java.util.LinkedHashMap;
    40 import java.util.List;
    41 import java.util.Map;
    42 import java.util.Map.Entry;
    43 import java.util.Set;
    44 import java.util.TreeSet;
    45 import javax.xml.bind.Binder;
    46 import javax.xml.bind.JAXBContext;
    47 import javax.xml.bind.JAXBElement;
    48 import javax.xml.bind.JAXBException;
    49 import javax.xml.bind.JAXBIntrospector;
    50 import javax.xml.bind.Marshaller;
    51 import javax.xml.bind.SchemaOutputResolver;
    52 import javax.xml.bind.Unmarshaller;
    53 import javax.xml.bind.Validator;
    54 import javax.xml.bind.annotation.XmlAttachmentRef;
    55 import javax.xml.bind.annotation.XmlList;
    56 import javax.xml.bind.annotation.XmlNs;
    57 import javax.xml.bind.annotation.XmlSchema;
    58 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
    59 import javax.xml.namespace.QName;
    60 import javax.xml.parsers.DocumentBuilder;
    61 import javax.xml.parsers.DocumentBuilderFactory;
    62 import javax.xml.parsers.FactoryConfigurationError;
    63 import javax.xml.parsers.ParserConfigurationException;
    64 import javax.xml.transform.Result;
    65 import javax.xml.transform.Transformer;
    66 import javax.xml.transform.TransformerConfigurationException;
    67 import javax.xml.transform.TransformerFactory;
    68 import javax.xml.transform.sax.SAXResult;
    69 import javax.xml.transform.sax.SAXTransformerFactory;
    70 import javax.xml.transform.sax.TransformerHandler;
    72 import com.sun.istack.internal.NotNull;
    73 import com.sun.istack.internal.Pool;
    74 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
    75 import com.sun.xml.internal.bind.api.AccessorException;
    76 import com.sun.xml.internal.bind.api.Bridge;
    77 import com.sun.xml.internal.bind.api.BridgeContext;
    78 import com.sun.xml.internal.bind.api.CompositeStructure;
    79 import com.sun.xml.internal.bind.api.ErrorListener;
    80 import com.sun.xml.internal.bind.api.JAXBRIContext;
    81 import com.sun.xml.internal.bind.api.RawAccessor;
    82 import com.sun.xml.internal.bind.api.TypeReference;
    83 import com.sun.xml.internal.bind.unmarshaller.DOMScanner;
    84 import com.sun.xml.internal.bind.util.Which;
    85 import com.sun.xml.internal.bind.v2.model.annotation.RuntimeAnnotationReader;
    86 import com.sun.xml.internal.bind.v2.model.annotation.RuntimeInlineAnnotationReader;
    87 import com.sun.xml.internal.bind.v2.model.core.Adapter;
    88 import com.sun.xml.internal.bind.v2.model.core.NonElement;
    89 import com.sun.xml.internal.bind.v2.model.core.Ref;
    90 import com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl;
    91 import com.sun.xml.internal.bind.v2.model.impl.RuntimeModelBuilder;
    92 import com.sun.xml.internal.bind.v2.model.nav.Navigator;
    93 import com.sun.xml.internal.bind.v2.model.nav.ReflectionNavigator;
    94 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeArrayInfo;
    95 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeBuiltinLeafInfo;
    96 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo;
    97 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElementInfo;
    98 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeEnumLeafInfo;
    99 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeLeafInfo;
   100 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfo;
   101 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfoSet;
   102 import com.sun.xml.internal.bind.v2.runtime.output.Encoded;
   103 import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty;
   104 import com.sun.xml.internal.bind.v2.runtime.property.Property;
   105 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
   106 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
   107 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.TagName;
   108 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl;
   109 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
   110 import com.sun.xml.internal.bind.v2.schemagen.XmlSchemaGenerator;
   111 import com.sun.xml.internal.bind.v2.util.EditDistance;
   112 import com.sun.xml.internal.bind.v2.util.QNameMap;
   113 import com.sun.xml.internal.bind.v2.util.XmlFactory;
   114 import com.sun.xml.internal.txw2.output.ResultFactory;
   116 import org.w3c.dom.Document;
   117 import org.w3c.dom.Element;
   118 import org.w3c.dom.Node;
   119 import org.xml.sax.SAXException;
   120 import org.xml.sax.SAXParseException;
   121 import org.xml.sax.helpers.DefaultHandler;
   123 /**
   124  * This class provides the implementation of JAXBContext.
   125  *
   126  */
   127 public final class JAXBContextImpl extends JAXBRIContext {
   129     /**
   130      * All the bridge classes.
   131      */
   132     private final Map<TypeReference,Bridge> bridges = new LinkedHashMap<TypeReference,Bridge>();
   134     /**
   135      * Shared instance of {@link TransformerFactory}.
   136      * Lock before use, because a {@link TransformerFactory} is not thread-safe
   137      * whereas {@link JAXBContextImpl} is.
   138      * Lazily created.
   139      */
   140     private volatile static SAXTransformerFactory tf;
   142     /**
   143      * Shared instance of {@link DocumentBuilder}.
   144      * Lock before use. Lazily created.
   145      */
   146     private static DocumentBuilder db;
   148     private final QNameMap<JaxBeanInfo> rootMap = new QNameMap<JaxBeanInfo>();
   149     private final HashMap<QName,JaxBeanInfo> typeMap = new HashMap<QName,JaxBeanInfo>();
   151     /**
   152      * Map from JAXB-bound {@link Class} to its {@link JaxBeanInfo}.
   153      */
   154     private final Map<Class,JaxBeanInfo> beanInfoMap = new LinkedHashMap<Class,JaxBeanInfo>();
   156     /**
   157      * All created {@link JaxBeanInfo}s.
   158      * Updated from each {@link JaxBeanInfo}s constructors to avoid infinite recursion
   159      * for a cyclic reference.
   160      *
   161      * <p>
   162      * This map is only used while the {@link JAXBContextImpl} is built and set to null
   163      * to avoid keeping references too long.
   164      */
   165     protected Map<RuntimeTypeInfo,JaxBeanInfo> beanInfos = new LinkedHashMap<RuntimeTypeInfo, JaxBeanInfo>();
   167     private final Map<Class/*scope*/,Map<QName,ElementBeanInfoImpl>> elements = new LinkedHashMap<Class, Map<QName, ElementBeanInfoImpl>>();
   169     /**
   170      * Pool of {@link Marshaller}s.
   171      */
   172     public final Pool<Marshaller> marshallerPool = new Pool.Impl<Marshaller>() {
   173         protected @NotNull Marshaller create() {
   174             return createMarshaller();
   175         }
   176     };
   178     public final Pool<Unmarshaller> unmarshallerPool = new Pool.Impl<Unmarshaller>() {
   179         protected @NotNull Unmarshaller create() {
   180             return createUnmarshaller();
   181         }
   182     };
   184     /**
   185      * Used to assign indices to known names in this grammar.
   186      * Reset to null once the build phase is completed.
   187      */
   188     public NameBuilder nameBuilder = new NameBuilder();
   190     /**
   191      * Keeps the list of known names.
   192      * This field is set once the build pahse is completed.
   193      */
   194     public final NameList nameList;
   196     /**
   197      * Input to the JAXBContext.newInstance, so that we can recreate
   198      * {@link RuntimeTypeInfoSet} whenever we need.
   199      */
   200     private final String defaultNsUri;
   201     private final Class[] classes;
   203     /**
   204      * true to reorder attributes lexicographically in preparation of the c14n support.
   205      */
   206     protected final boolean c14nSupport;
   208     /**
   209      * Flag that user has provided a custom AccessorFactory for JAXB to use
   210      */
   211     public final boolean xmlAccessorFactorySupport;
   213     /**
   214      * @see JAXBRIContext#TREAT_EVERYTHING_NILLABLE
   215      */
   216     public final boolean allNillable;
   218     /**
   219      * Store properties, so that they can be recovered in the run (is here because of JSON encoding of Jersey).
   220      */
   221     public final boolean retainPropertyInfo;
   223     /**
   224      * Suppress reflection accessor warnings.
   225      */
   226     public final boolean supressAccessorWarnings;
   228     /**
   229      * Improved xsi type handling.
   230      */
   231     public final boolean improvedXsiTypeHandling;
   233     /**
   234      * Disable security processing.
   235      */
   236     public final boolean disableSecurityProcessing;
   238     private WeakReference<RuntimeTypeInfoSet> typeInfoSetCache;
   240     private @NotNull RuntimeAnnotationReader annotationReader;
   242     private /*almost final*/ boolean hasSwaRef;
   243     private final @NotNull Map<Class,Class> subclassReplacements;
   245     /**
   246      * If true, we aim for faster {@link JAXBContext} instantiation performance,
   247      * instead of going after efficient sustained unmarshalling/marshalling performance.
   248      *
   249      * @since 2.0.4
   250      */
   251     public final boolean fastBoot;
   253     private Set<XmlNs> xmlNsSet = null;
   255     /**
   256      * Returns declared XmlNs annotations (from package-level annotation XmlSchema
   257      *
   258      * @return set of all present XmlNs annotations
   259      */
   260     public Set<XmlNs> getXmlNsSet() {
   261         return xmlNsSet;
   262     }
   264     private JAXBContextImpl(JAXBContextBuilder builder) throws JAXBException {
   266         this.defaultNsUri = builder.defaultNsUri;
   267         this.retainPropertyInfo = builder.retainPropertyInfo;
   268         this.annotationReader = builder.annotationReader;
   269         this.subclassReplacements = builder.subclassReplacements;
   270         this.c14nSupport = builder.c14nSupport;
   271         this.classes = builder.classes;
   272         this.xmlAccessorFactorySupport = builder.xmlAccessorFactorySupport;
   273         this.allNillable = builder.allNillable;
   274         this.supressAccessorWarnings = builder.supressAccessorWarnings;
   275         this.improvedXsiTypeHandling = builder.improvedXsiTypeHandling;
   276         this.disableSecurityProcessing = builder.disableSecurityProcessing;
   278         Collection<TypeReference> typeRefs = builder.typeRefs;
   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;
   288         RuntimeTypeInfoSet typeSet = getTypeInfoSet();
   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>());
   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         }
   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         }
   309         for (RuntimeArrayInfo a : typeSet.arrays().values()) {
   310             JaxBeanInfo<?> ai = getOrCreate(a);
   311             for (QName qn : ai.getTypeNames())
   312                 typeMap.put( qn, ai );
   313         }
   315         for( Entry<Class, ? extends RuntimeClassInfo> e : typeSet.beans().entrySet() ) {
   316             ClassBeanInfoImpl<?> bi = getOrCreate(e.getValue());
   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             }
   327             if(bi.isElement())
   328                 rootMap.put( e.getValue().getElementName(), bi );
   330             for (QName qn : bi.getTypeNames())
   331                 typeMap.put( qn, bi );
   332         }
   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);
   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         }
   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));
   355         getOrCreate(typeSet.getAnyTypeInfo());
   357         // then link them all!
   358         for (JaxBeanInfo bi : beanInfos.values())
   359             bi.link(this);
   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()) );
   365         // build bridges
   366         ReflectionNavigator nav = typeSet.getNavigator();
   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);
   373             // eventually compute the in-memory type
   374             Class erasedType = nav.erasure(tr.type);
   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             }
   384             if(a!=null) {
   385                 erasedType = nav.erasure(a.defaultType);
   386             }
   388             Name name = nameBuilder.createElementName(tr.tagName);
   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);
   396             if(a!=null)
   397                 bridge = new BridgeAdapter(bridge,a.adapterType);
   399             bridges.put(tr,bridge);
   400         }
   402         this.nameList = nameBuilder.conclude();
   404         for (JaxBeanInfo bi : beanInfos.values())
   405             bi.wrapUp();
   407         // no use for them now
   408         nameBuilder = null;
   409         beanInfos = null;
   410     }
   412     /**
   413      * True if this JAXBContext has {@link XmlAttachmentRef}.
   414      */
   415     public boolean hasSwaRef() {
   416         return hasSwaRef;
   417     }
   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     }
   428     /**
   429      * Creates a {@link RuntimeTypeInfoSet}.
   430      */
   431     public RuntimeTypeInfoSet getTypeInfoSet() throws IllegalAnnotationsException {
   433         // check cache
   434         if(typeInfoSetCache!=null) {
   435             RuntimeTypeInfoSet r = typeInfoSetCache.get();
   436             if(r!=null)
   437                 return r;
   438         }
   440         final RuntimeModelBuilder builder = new RuntimeModelBuilder(this,annotationReader,subclassReplacements,defaultNsUri);
   442         IllegalAnnotationsException.Builder errorHandler = new IllegalAnnotationsException.Builder();
   443         builder.setErrorHandler(errorHandler);
   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         }
   453         this.hasSwaRef |= builder.hasSwaRef;
   454         RuntimeTypeInfoSet r = builder.link();
   456         errorHandler.check();
   457         assert r!=null : "if no error was reported, the link must be a success";
   459         typeInfoSetCache = new WeakReference<RuntimeTypeInfoSet>(r);
   461         return r;
   462     }
   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     }
   480     private ElementBeanInfoImpl getOrCreate( RuntimeElementInfo rei ) {
   481         JaxBeanInfo bi = beanInfos.get(rei);
   482         if(bi!=null)    return (ElementBeanInfoImpl)bi;
   484         // all elements share the same type, so we can't register them to beanInfoMap
   485         return new ElementBeanInfoImpl(this, rei);
   486     }
   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     }
   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     }
   504     protected JaxBeanInfo getOrCreate( RuntimeArrayInfo ai ) {
   505         JaxBeanInfo abi = beanInfos.get(ai);
   506         if(abi!=null)   return abi;
   508         abi = new ArrayBeanInfoImpl(this,ai);
   510         beanInfoMap.put(ai.getType(),abi);
   511         return abi;
   512     }
   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         }
   536         throw new IllegalArgumentException();
   537     }
   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 <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>.
   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     }
   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     }
   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 <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>.
   592      */
   593     public final <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz) {
   594         return (JaxBeanInfo<T>)beanInfoMap.get(clazz);
   595     }
   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     }
   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;
   625         return beanInfo.getLoader(this,true);
   626     }
   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     }
   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         }
   656         String nearest = EditDistance.findNearest(name.toString(), all);
   658         if(EditDistance.editDistance(nearest,name.toString())>10)
   659             return null;    // too far apart.
   661         return nearest;
   662     }
   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     }
   676     /**
   677      * Cache of UTF-8 encoded local names to improve the performance for the marshalling.
   678      */
   679     private Encoded[] utf8nameTable;
   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     }
   694     public int getNumberOfLocalNames() {
   695         return nameList.localNames.length;
   696     }
   698     public int getNumberOfElementNames() {
   699         return nameList.numberOfElementNames;
   700     }
   702     public int getNumberOfAttributeNames() {
   703         return nameList.numberOfAttributeNames;
   704     }
   706     /**
   707      * Creates a new identity transformer.
   708      */
   709     static Transformer createTransformer(boolean disableSecureProcessing) {
   710         try {
   711             if (tf==null) {
   712                 synchronized(JAXBContextImpl.class) {
   713                     if (tf==null) {
   714                         tf = (SAXTransformerFactory)XmlFactory.createTransformerFactory(disableSecureProcessing);
   715                     }
   716                 }
   717             }
   718             return tf.newTransformer();
   719         } catch (TransformerConfigurationException e) {
   720             throw new Error(e); // impossible
   721         }
   722     }
   724     /**
   725      * Creates a new identity transformer.
   726      */
   727     public static TransformerHandler createTransformerHandler(boolean disableSecureProcessing) {
   728         try {
   729             if (tf==null) {
   730                 synchronized(JAXBContextImpl.class) {
   731                     if (tf==null) {
   732                         tf = (SAXTransformerFactory)XmlFactory.createTransformerFactory(disableSecureProcessing);
   733                     }
   734                 }
   735             }
   736             return tf.newTransformerHandler();
   737         } catch (TransformerConfigurationException e) {
   738             throw new Error(e); // impossible
   739         }
   740     }
   742     /**
   743      * Creates a new DOM document.
   744      */
   745     static Document createDom(boolean disableSecurityProcessing) {
   746         synchronized(JAXBContextImpl.class) {
   747             if(db==null) {
   748                 try {
   749                     DocumentBuilderFactory dbf = XmlFactory.createDocumentBuilderFactory(disableSecurityProcessing);
   750                     db = dbf.newDocumentBuilder();
   751                 } catch (ParserConfigurationException e) {
   752                     // impossible
   753                     throw new FactoryConfigurationError(e);
   754                 }
   755             }
   756             return db.newDocument();
   757         }
   758     }
   760     public MarshallerImpl createMarshaller() {
   761         return new MarshallerImpl(this,null);
   762     }
   764     public UnmarshallerImpl createUnmarshaller() {
   765         return new UnmarshallerImpl(this,null);
   766     }
   768     public Validator createValidator() {
   769         throw new UnsupportedOperationException(Messages.NOT_IMPLEMENTED_IN_2_0.format());
   770     }
   772     @Override
   773     public JAXBIntrospector createJAXBIntrospector() {
   774         return new JAXBIntrospector() {
   775             public boolean isElement(Object object) {
   776                 return getElementName(object)!=null;
   777             }
   779             public QName getElementName(Object jaxbElement) {
   780                 try {
   781                     return JAXBContextImpl.this.getElementName(jaxbElement);
   782                 } catch (JAXBException e) {
   783                     return null;
   784                 }
   785             }
   786         };
   787     }
   789     private NonElement<Type,Class> getXmlType(RuntimeTypeInfoSet tis, TypeReference tr) {
   790         if(tr==null)
   791             throw new IllegalArgumentException();
   793         XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class);
   794         XmlList xl = tr.get(XmlList.class);
   796         Ref<Type,Class> ref = new Ref<Type,Class>(annotationReader, tis.getNavigator(), tr.type, xjta, xl );
   798         return tis.getTypeInfo(ref);
   799     }
   801     @Override
   802     public void generateEpisode(Result output) {
   803         if(output==null)
   804             throw new IllegalArgumentException();
   805         createSchemaGenerator().writeEpisodeFile(ResultFactory.createSerializer(output));
   806     }
   808     @Override
   809     @SuppressWarnings("ThrowableInitCause")
   810     public void generateSchema(SchemaOutputResolver outputResolver) throws IOException {
   811         if(outputResolver==null)
   812             throw new IOException(Messages.NULL_OUTPUT_RESOLVER.format());
   814         final SAXParseException[] e = new SAXParseException[1];
   815         final SAXParseException[] w = new SAXParseException[1];
   817         createSchemaGenerator().write(outputResolver, new ErrorListener() {
   818             public void error(SAXParseException exception) {
   819                 e[0] = exception;
   820             }
   822             public void fatalError(SAXParseException exception) {
   823                 e[0] = exception;
   824             }
   826             public void warning(SAXParseException exception) {
   827                 w[0] = exception;
   828             }
   830             public void info(SAXParseException exception) {}
   831         });
   833         if (e[0]!=null) {
   834             IOException x = new IOException(Messages.FAILED_TO_GENERATE_SCHEMA.format());
   835             x.initCause(e[0]);
   836             throw x;
   837         }
   838         if (w[0]!=null) {
   839             IOException x = new IOException(Messages.ERROR_PROCESSING_SCHEMA.format());
   840             x.initCause(w[0]);
   841             throw x;
   842         }
   843     }
   845     private XmlSchemaGenerator<Type,Class,Field,Method> createSchemaGenerator() {
   846         RuntimeTypeInfoSet tis;
   847         try {
   848             tis = getTypeInfoSet();
   849         } catch (IllegalAnnotationsException e) {
   850             // this shouldn't happen because we've already
   851             throw new AssertionError(e);
   852         }
   854         XmlSchemaGenerator<Type,Class,Field,Method> xsdgen =
   855                 new XmlSchemaGenerator<Type,Class,Field,Method>(tis.getNavigator(),tis);
   857         // JAX-RPC uses Bridge objects that collide with
   858         // @XmlRootElement.
   859         // we will avoid collision here
   860         Set<QName> rootTagNames = new HashSet<QName>();
   861         for (RuntimeElementInfo ei : tis.getAllElements()) {
   862             rootTagNames.add(ei.getElementName());
   863         }
   864         for (RuntimeClassInfo ci : tis.beans().values()) {
   865             if(ci.isElement())
   866                 rootTagNames.add(ci.asElement().getElementName());
   867         }
   869         for (TypeReference tr : bridges.keySet()) {
   870             if(rootTagNames.contains(tr.tagName))
   871                 continue;
   873             if(tr.type==void.class || tr.type==Void.class) {
   874                 xsdgen.add(tr.tagName,false,null);
   875             } else
   876             if(tr.type==CompositeStructure.class) {
   877                 // this is a special class we introduced for JAX-WS that we *don't* want in the schema
   878             } else {
   879                 NonElement<Type,Class> typeInfo = getXmlType(tis,tr);
   880                 xsdgen.add(tr.tagName, !Navigator.REFLECTION.isPrimitive(tr.type),typeInfo);
   881             }
   882         }
   883         return xsdgen;
   884     }
   886     public QName getTypeName(TypeReference tr) {
   887         try {
   888             NonElement<Type,Class> xt = getXmlType(getTypeInfoSet(),tr);
   889             if(xt==null)    throw new IllegalArgumentException();
   890             return xt.getTypeName();
   891         } catch (IllegalAnnotationsException e) {
   892             // impossible given that JAXBRIContext has been successfully built in the first place
   893             throw new AssertionError(e);
   894         }
   895     }
   897     /**
   898      * Used for testing.
   899      */
   900     public SchemaOutputResolver createTestResolver() {
   901         return new SchemaOutputResolver() {
   902             public Result createOutput(String namespaceUri, String suggestedFileName) {
   903                 SAXResult r = new SAXResult(new DefaultHandler());
   904                 r.setSystemId(suggestedFileName);
   905                 return r;
   906             }
   907         };
   908     }
   910     @Override
   911     public <T> Binder<T> createBinder(Class<T> domType) {
   912         if(domType==Node.class)
   913             return (Binder<T>)createBinder();
   914         else
   915             return super.createBinder(domType);
   916     }
   918     @Override
   919     public Binder<Node> createBinder() {
   920         return new BinderImpl<Node>(this,new DOMScanner());
   921     }
   923     public QName getElementName(Object o) throws JAXBException {
   924         JaxBeanInfo bi = getBeanInfo(o,true);
   925         if(!bi.isElement())
   926             return null;
   927         return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o));
   928     }
   930     public QName getElementName(Class o) throws JAXBException {
   931         JaxBeanInfo bi = getBeanInfo(o,true);
   932         if(!bi.isElement())
   933             return null;
   934         return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o));
   935     }
   937     public Bridge createBridge(TypeReference ref) {
   938         return bridges.get(ref);
   939     }
   941     public @NotNull BridgeContext createBridgeContext() {
   942         return new BridgeContextImpl(this);
   943     }
   945     public RawAccessor getElementPropertyAccessor(Class wrapperBean, String nsUri, String localName) throws JAXBException {
   946         JaxBeanInfo bi = getBeanInfo(wrapperBean,true);
   947         if(!(bi instanceof ClassBeanInfoImpl))
   948             throw new JAXBException(wrapperBean+" is not a bean");
   950         for( ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi; cb!=null; cb=cb.superClazz) {
   951             for (Property p : cb.properties) {
   952                 final Accessor acc = p.getElementPropertyAccessor(nsUri,localName);
   953                 if(acc!=null)
   954                     return new RawAccessor() {
   955                         // Accessor.set/get are designed for unmarshaller/marshaller, and hence
   956                         // they go through an adapter behind the scene.
   957                         // this isn't desirable for JAX-WS, which essentially uses this method
   958                         // just as a reflection library. So use the "unadapted" version to
   959                         // achieve the desired semantics
   960                         public Object get(Object bean) throws AccessorException {
   961                             return acc.getUnadapted(bean);
   962                         }
   964                         public void set(Object bean, Object value) throws AccessorException {
   965                             acc.setUnadapted(bean,value);
   966                         }
   967                     };
   968             }
   969         }
   970         throw new JAXBException(new QName(nsUri,localName)+" is not a valid property on "+wrapperBean);
   971     }
   973     public List<String> getKnownNamespaceURIs() {
   974         return Arrays.asList(nameList.namespaceURIs);
   975     }
   977     public String getBuildId() {
   978         Package pkg = getClass().getPackage();
   979         if(pkg==null)   return null;
   980         return pkg.getImplementationVersion();
   981     }
   983     @Override
   984     public String toString() {
   985         StringBuilder buf = new StringBuilder(Which.which(getClass()) + " Build-Id: " + getBuildId());
   986         buf.append("\nClasses known to this context:\n");
   988         Set<String> names = new TreeSet<String>();  // sort them so that it's easy to read
   990         for (Class key : beanInfoMap.keySet())
   991             names.add(key.getName());
   993         for(String name: names)
   994             buf.append("  ").append(name).append('\n');
   996         return buf.toString();
   997     }
   999     /**
  1000      * Gets the value of the xmime:contentType attribute on the given object, or null
  1001      * if for some reason it couldn't be found, including any error.
  1002      */
  1003     public String getXMIMEContentType( Object o ) {
  1004         JaxBeanInfo bi = getBeanInfo(o);
  1005         if(!(bi instanceof ClassBeanInfoImpl))
  1006             return null;
  1008         ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi;
  1009         for (Property p : cb.properties) {
  1010             if (p instanceof AttributeProperty) {
  1011                 AttributeProperty ap = (AttributeProperty) p;
  1012                 if(ap.attName.equals(WellKnownNamespace.XML_MIME_URI,"contentType"))
  1013                     try {
  1014                         return (String)ap.xacc.print(o);
  1015                     } catch (AccessorException e) {
  1016                         return null;
  1017                     } catch (SAXException e) {
  1018                         return null;
  1019                     } catch (ClassCastException e) {
  1020                         return null;
  1024         return null;
  1027     /**
  1028      * Creates a {@link JAXBContextImpl} that includes the specified additional classes.
  1029      */
  1030     public JAXBContextImpl createAugmented(Class<?> clazz) throws JAXBException {
  1031         Class[] newList = new Class[classes.length+1];
  1032         System.arraycopy(classes,0,newList,0,classes.length);
  1033         newList[classes.length] = clazz;
  1035         JAXBContextBuilder builder = new JAXBContextBuilder(this);
  1036         builder.setClasses(newList);
  1037         return builder.build();
  1040     private static final Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() {
  1041         public int compare(QName lhs, QName rhs) {
  1042             int r = lhs.getLocalPart().compareTo(rhs.getLocalPart());
  1043             if(r!=0)    return r;
  1045             return lhs.getNamespaceURI().compareTo(rhs.getNamespaceURI());
  1047     };
  1049     public static class JAXBContextBuilder {
  1051         private boolean retainPropertyInfo = false;
  1052         private boolean supressAccessorWarnings = false;
  1053         private String defaultNsUri = "";
  1054         private @NotNull RuntimeAnnotationReader annotationReader = new RuntimeInlineAnnotationReader();
  1055         private @NotNull Map<Class,Class> subclassReplacements = Collections.emptyMap();
  1056         private boolean c14nSupport = false;
  1057         private Class[] classes;
  1058         private Collection<TypeReference> typeRefs;
  1059         private boolean xmlAccessorFactorySupport = false;
  1060         private boolean allNillable;
  1061         private boolean improvedXsiTypeHandling = true;
  1062         private boolean disableSecurityProcessing = true;
  1064         public JAXBContextBuilder() {};
  1066         public JAXBContextBuilder(JAXBContextImpl baseImpl) {
  1067             this.supressAccessorWarnings = baseImpl.supressAccessorWarnings;
  1068             this.retainPropertyInfo = baseImpl.retainPropertyInfo;
  1069             this.defaultNsUri = baseImpl.defaultNsUri;
  1070             this.annotationReader = baseImpl.annotationReader;
  1071             this.subclassReplacements = baseImpl.subclassReplacements;
  1072             this.c14nSupport = baseImpl.c14nSupport;
  1073             this.classes = baseImpl.classes;
  1074             this.typeRefs = baseImpl.bridges.keySet();
  1075             this.xmlAccessorFactorySupport = baseImpl.xmlAccessorFactorySupport;
  1076             this.allNillable = baseImpl.allNillable;
  1077             this.disableSecurityProcessing = baseImpl.disableSecurityProcessing;
  1080         public JAXBContextBuilder setRetainPropertyInfo(boolean val) {
  1081             this.retainPropertyInfo = val;
  1082             return this;
  1085         public JAXBContextBuilder setSupressAccessorWarnings(boolean val) {
  1086             this.supressAccessorWarnings = val;
  1087             return this;
  1090         public JAXBContextBuilder setC14NSupport(boolean val) {
  1091             this.c14nSupport = val;
  1092             return this;
  1095         public JAXBContextBuilder setXmlAccessorFactorySupport(boolean val) {
  1096             this.xmlAccessorFactorySupport = val;
  1097             return this;
  1100         public JAXBContextBuilder setDefaultNsUri(String val) {
  1101             this.defaultNsUri = val;
  1102             return this;
  1105         public JAXBContextBuilder setAllNillable(boolean val) {
  1106             this.allNillable = val;
  1107             return this;
  1110         public JAXBContextBuilder setClasses(Class[] val) {
  1111             this.classes = val;
  1112             return this;
  1115         public JAXBContextBuilder setAnnotationReader(RuntimeAnnotationReader val) {
  1116             this.annotationReader = val;
  1117             return this;
  1120         public JAXBContextBuilder setSubclassReplacements(Map<Class,Class> val) {
  1121             this.subclassReplacements = val;
  1122             return this;
  1125         public JAXBContextBuilder setTypeRefs(Collection<TypeReference> val) {
  1126             this.typeRefs = val;
  1127             return this;
  1130         public JAXBContextBuilder setImprovedXsiTypeHandling(boolean val) {
  1131             this.improvedXsiTypeHandling = val;
  1132             return this;
  1135         public JAXBContextBuilder setDisableSecurityProcessing(boolean val) {
  1136             this.disableSecurityProcessing = val;
  1137             return this;
  1140         public JAXBContextImpl build() throws JAXBException {
  1142             // fool-proof
  1143             if (this.defaultNsUri == null) {
  1144                 this.defaultNsUri = "";
  1147             if (this.subclassReplacements == null) {
  1148                 this.subclassReplacements = Collections.emptyMap();
  1151             if (this.annotationReader == null) {
  1152                 this.annotationReader = new RuntimeInlineAnnotationReader();
  1155             if (this.typeRefs == null) {
  1156                 this.typeRefs = Collections.<TypeReference>emptyList();
  1159             return new JAXBContextImpl(this);

mercurial