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

Fri, 22 Nov 2013 21:11:19 +0100

author
mkos
date
Fri, 22 Nov 2013 21:11:19 +0100
changeset 450
b0c2840e2513
parent 384
8f2986ff0235
child 515
6cd506508147
permissions
-rw-r--r--

8010935: Better XML handling
8027378: Two closed/javax/xml/8005432 fails with jdk7u51b04
8028382: Two javax/xml/8005433 tests still fail after the fix JDK-8028147
Summary: base fix + fixes for test regressions; fix also reviewed by Maxim Soloviev, Alexander Fomin
Reviewed-by: mchung, mgrebac, mullan

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

mercurial