0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common Development
0008: * and Distribution License("CDDL") (collectively, the "License"). You
0009: * may not use this file except in compliance with the License. You can obtain
0010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
0011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
0012: * language governing permissions and limitations under the License.
0013: *
0014: * When distributing the software, include this License Header Notice in each
0015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
0016: * Sun designates this particular file as subject to the "Classpath" exception
0017: * as provided by Sun in the GPL Version 2 section of the License file that
0018: * accompanied this code. If applicable, add the following below the License
0019: * Header, with the fields enclosed by brackets [] replaced by your own
0020: * identifying information: "Portions Copyrighted [year]
0021: * [name of copyright owner]"
0022: *
0023: * Contributor(s):
0024: *
0025: * If you wish your version of this file to be governed by only the CDDL or
0026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
0027: * elects to include this software in this distribution under the [CDDL or GPL
0028: * Version 2] license." If you don't indicate a single choice of license, a
0029: * recipient has the option to distribute your version of this file under
0030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
0031: * its licensees as provided above. However, if you add GPL Version 2 code
0032: * and therefore, elected the GPL Version 2 license, then the option applies
0033: * only if the new code is made subject to such option by the copyright
0034: * holder.
0035: */
0036: package com.sun.xml.bind.v2.runtime;
0037:
0038: import java.io.IOException;
0039: import java.lang.ref.WeakReference;
0040: import java.lang.reflect.Field;
0041: import java.lang.reflect.Method;
0042: import java.lang.reflect.Type;
0043: import java.util.Arrays;
0044: import java.util.Collection;
0045: import java.util.Collections;
0046: import java.util.Comparator;
0047: import java.util.HashMap;
0048: import java.util.HashSet;
0049: import java.util.LinkedHashMap;
0050: import java.util.List;
0051: import java.util.Map;
0052: import java.util.Set;
0053: import java.util.TreeSet;
0054:
0055: import javax.xml.bind.Binder;
0056: import javax.xml.bind.DatatypeConverter;
0057: import javax.xml.bind.JAXBContext;
0058: import javax.xml.bind.JAXBElement;
0059: import javax.xml.bind.JAXBException;
0060: import javax.xml.bind.JAXBIntrospector;
0061: import javax.xml.bind.Marshaller;
0062: import javax.xml.bind.SchemaOutputResolver;
0063: import javax.xml.bind.Unmarshaller;
0064: import javax.xml.bind.Validator;
0065: import javax.xml.bind.annotation.XmlAttachmentRef;
0066: import javax.xml.bind.annotation.XmlList;
0067: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
0068: import javax.xml.namespace.QName;
0069: import javax.xml.parsers.DocumentBuilder;
0070: import javax.xml.parsers.DocumentBuilderFactory;
0071: import javax.xml.parsers.FactoryConfigurationError;
0072: import javax.xml.parsers.ParserConfigurationException;
0073: import javax.xml.transform.Result;
0074: import javax.xml.transform.Transformer;
0075: import javax.xml.transform.TransformerConfigurationException;
0076: import javax.xml.transform.TransformerFactory;
0077: import javax.xml.transform.sax.SAXResult;
0078: import javax.xml.transform.sax.SAXTransformerFactory;
0079: import javax.xml.transform.sax.TransformerHandler;
0080:
0081: import com.sun.istack.NotNull;
0082: import com.sun.istack.Nullable;
0083: import com.sun.istack.Pool;
0084: import com.sun.xml.bind.DatatypeConverterImpl;
0085: import com.sun.xml.bind.api.AccessorException;
0086: import com.sun.xml.bind.api.Bridge;
0087: import com.sun.xml.bind.api.BridgeContext;
0088: import com.sun.xml.bind.api.CompositeStructure;
0089: import com.sun.xml.bind.api.ErrorListener;
0090: import com.sun.xml.bind.api.JAXBRIContext;
0091: import com.sun.xml.bind.api.RawAccessor;
0092: import com.sun.xml.bind.api.TypeReference;
0093: import com.sun.xml.bind.unmarshaller.DOMScanner;
0094: import com.sun.xml.bind.util.Which;
0095: import com.sun.xml.bind.v2.WellKnownNamespace;
0096: import com.sun.xml.bind.v2.model.annotation.RuntimeAnnotationReader;
0097: import com.sun.xml.bind.v2.model.annotation.RuntimeInlineAnnotationReader;
0098: import com.sun.xml.bind.v2.model.core.Adapter;
0099: import com.sun.xml.bind.v2.model.core.NonElement;
0100: import com.sun.xml.bind.v2.model.core.Ref;
0101: import com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl;
0102: import com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder;
0103: import com.sun.xml.bind.v2.model.nav.Navigator;
0104: import com.sun.xml.bind.v2.model.nav.ReflectionNavigator;
0105: import com.sun.xml.bind.v2.model.runtime.RuntimeArrayInfo;
0106: import com.sun.xml.bind.v2.model.runtime.RuntimeBuiltinLeafInfo;
0107: import com.sun.xml.bind.v2.model.runtime.RuntimeClassInfo;
0108: import com.sun.xml.bind.v2.model.runtime.RuntimeElementInfo;
0109: import com.sun.xml.bind.v2.model.runtime.RuntimeEnumLeafInfo;
0110: import com.sun.xml.bind.v2.model.runtime.RuntimeLeafInfo;
0111: import com.sun.xml.bind.v2.model.runtime.RuntimeTypeInfo;
0112: import com.sun.xml.bind.v2.model.runtime.RuntimeTypeInfoSet;
0113: import com.sun.xml.bind.v2.runtime.output.Encoded;
0114: import com.sun.xml.bind.v2.runtime.property.AttributeProperty;
0115: import com.sun.xml.bind.v2.runtime.property.Property;
0116: import com.sun.xml.bind.v2.runtime.reflect.Accessor;
0117: import com.sun.xml.bind.v2.runtime.unmarshaller.Loader;
0118: import com.sun.xml.bind.v2.runtime.unmarshaller.TagName;
0119: import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl;
0120: import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
0121: import com.sun.xml.bind.v2.schemagen.XmlSchemaGenerator;
0122: import com.sun.xml.bind.v2.util.EditDistance;
0123: import com.sun.xml.bind.v2.util.QNameMap;
0124: import com.sun.xml.txw2.output.ResultFactory;
0125:
0126: import org.w3c.dom.Document;
0127: import org.w3c.dom.Element;
0128: import org.w3c.dom.Node;
0129: import org.xml.sax.SAXException;
0130: import org.xml.sax.SAXParseException;
0131: import org.xml.sax.helpers.DefaultHandler;
0132:
0133: /**
0134: * This class provides the implementation of JAXBContext.
0135: *
0136: * @version $Revision: 1.75.2.19 $
0137: */
0138: public final class JAXBContextImpl extends JAXBRIContext {
0139:
0140: /**
0141: * All the bridge classes.
0142: */
0143: private final Map<TypeReference, Bridge> bridges = new LinkedHashMap<TypeReference, Bridge>();
0144:
0145: /**
0146: * Shared instance of {@link TransformerFactory}.
0147: * Lock before use, because a {@link TransformerFactory} is not thread-safe
0148: * whereas {@link JAXBContextImpl} is.
0149: * Lazily created.
0150: */
0151: private static SAXTransformerFactory tf;
0152:
0153: /**
0154: * Shared instance of {@link DocumentBuilder}.
0155: * Lock before use. Lazily created.
0156: */
0157: private static DocumentBuilder db;
0158:
0159: private final QNameMap<JaxBeanInfo> rootMap = new QNameMap<JaxBeanInfo>();
0160: private final HashMap<QName, JaxBeanInfo> typeMap = new HashMap<QName, JaxBeanInfo>();
0161:
0162: /**
0163: * Map from JAXB-bound {@link Class} to its {@link JaxBeanInfo}.
0164: */
0165: private final Map<Class, JaxBeanInfo> beanInfoMap = new LinkedHashMap<Class, JaxBeanInfo>();
0166:
0167: /**
0168: * All created {@link JaxBeanInfo}s.
0169: * Updated from each {@link JaxBeanInfo}s constructors to avoid infinite recursion
0170: * for a cyclic reference.
0171: *
0172: * <p>
0173: * This map is only used while the {@link JAXBContextImpl} is built and set to null
0174: * to avoid keeping references too long.
0175: */
0176: protected Map<RuntimeTypeInfo, JaxBeanInfo> beanInfos = new LinkedHashMap<RuntimeTypeInfo, JaxBeanInfo>();
0177:
0178: private final Map<Class/*scope*/, Map<QName, ElementBeanInfoImpl>> elements = new LinkedHashMap<Class, Map<QName, ElementBeanInfoImpl>>();
0179:
0180: /**
0181: * Pool of {@link Marshaller}s.
0182: */
0183: public final Pool<Marshaller> marshallerPool = new Pool.Impl<Marshaller>() {
0184: protected @NotNull
0185: Marshaller create() {
0186: return createMarshaller();
0187: }
0188: };
0189:
0190: public final Pool<Unmarshaller> unmarshallerPool = new Pool.Impl<Unmarshaller>() {
0191: protected @NotNull
0192: Unmarshaller create() {
0193: return createUnmarshaller();
0194: }
0195: };
0196:
0197: /**
0198: * Used to assign indices to known names in this grammar.
0199: * Reset to null once the build phase is completed.
0200: */
0201: public NameBuilder nameBuilder = new NameBuilder();
0202:
0203: /**
0204: * Keeps the list of known names.
0205: * This field is set once the build pahse is completed.
0206: */
0207: public final NameList nameList;
0208:
0209: /**
0210: * Input to the JAXBContext.newInstance, so that we can recreate
0211: * {@link RuntimeTypeInfoSet} whenever we need.
0212: */
0213: private final String defaultNsUri;
0214: private final Class[] classes;
0215:
0216: /**
0217: * true to reorder attributes lexicographically in preparation of the c14n support.
0218: */
0219: protected final boolean c14nSupport;
0220:
0221: /**
0222: * Flag that user has provided a custom AccessorFactory for JAXB to use
0223: */
0224: public final boolean xmlAccessorFactorySupport;
0225:
0226: /**
0227: * @see JAXBRIContext#TREAT_EVERYTHING_NILLABLE
0228: */
0229: public final boolean allNillable;
0230:
0231: private WeakReference<RuntimeTypeInfoSet> typeInfoSetCache;
0232:
0233: private @NotNull
0234: RuntimeAnnotationReader annotaitonReader;
0235:
0236: private/*almost final*/boolean hasSwaRef;
0237: private final @NotNull
0238: Map<Class, Class> subclassReplacements;
0239:
0240: /**
0241: * If true, we aim for faster {@link JAXBContext} instanciation performance,
0242: * instead of going after efficient sustained unmarshalling/marshalling performance.
0243: *
0244: * @since 2.0.4
0245: */
0246: public final boolean fastBoot;
0247:
0248: /**
0249: *
0250: * @param typeRefs
0251: * used to build {@link Bridge}s. Can be empty.
0252: * @param c14nSupport
0253: * {@link #c14nSupport}.
0254: * @param xmlAccessorFactorySupport
0255: * Use custom com.sun.xml.bind.v2.runtime.reflect.Accessor implementation.
0256: */
0257: public JAXBContextImpl(Class[] classes,
0258: Collection<TypeReference> typeRefs,
0259: Map<Class, Class> subclassReplacements,
0260: String defaultNsUri, boolean c14nSupport, @Nullable
0261: RuntimeAnnotationReader ar,
0262: boolean xmlAccessorFactorySupport, boolean allNillable)
0263: throws JAXBException {
0264: // initialize datatype converter with ours
0265: DatatypeConverter
0266: .setDatatypeConverter(DatatypeConverterImpl.theInstance);
0267:
0268: if (defaultNsUri == null)
0269: defaultNsUri = ""; // fool-proof
0270:
0271: if (ar == null)
0272: ar = new RuntimeInlineAnnotationReader();
0273:
0274: if (subclassReplacements == null)
0275: subclassReplacements = Collections.emptyMap();
0276: if (typeRefs == null)
0277: typeRefs = Collections.emptyList();
0278:
0279: this .annotaitonReader = ar;
0280: this .subclassReplacements = subclassReplacements;
0281:
0282: boolean fastBoot;
0283: try {
0284: fastBoot = Boolean.getBoolean(JAXBContextImpl.class
0285: .getName()
0286: + ".fastBoot");
0287: } catch (SecurityException e) {
0288: fastBoot = false;
0289: }
0290: this .fastBoot = fastBoot;
0291:
0292: this .defaultNsUri = defaultNsUri;
0293: this .c14nSupport = c14nSupport;
0294: this .xmlAccessorFactorySupport = xmlAccessorFactorySupport;
0295: this .allNillable = allNillable;
0296: this .classes = new Class[classes.length];
0297: System.arraycopy(classes, 0, this .classes, 0, classes.length);
0298:
0299: RuntimeTypeInfoSet typeSet = getTypeInfoSet();
0300:
0301: // at least prepare the empty table so that we don't have to check for null later
0302: elements.put(null,
0303: new LinkedHashMap<QName, ElementBeanInfoImpl>());
0304:
0305: // recognize leaf bean infos
0306: for (RuntimeBuiltinLeafInfo leaf : RuntimeBuiltinLeafInfoImpl.builtinBeanInfos) {
0307: LeafBeanInfoImpl<?> bi = new LeafBeanInfoImpl(this , leaf);
0308: beanInfoMap.put(leaf.getClazz(), bi);
0309: for (QName t : bi.getTypeNames())
0310: typeMap.put(t, bi);
0311: }
0312:
0313: for (RuntimeEnumLeafInfo e : typeSet.enums().values()) {
0314: JaxBeanInfo<?> bi = getOrCreate(e);
0315: for (QName qn : bi.getTypeNames())
0316: typeMap.put(qn, bi);
0317: if (e.isElement())
0318: rootMap.put(e.getElementName(), bi);
0319: }
0320:
0321: for (RuntimeArrayInfo a : typeSet.arrays().values()) {
0322: JaxBeanInfo<?> ai = getOrCreate(a);
0323: for (QName qn : ai.getTypeNames())
0324: typeMap.put(qn, ai);
0325: }
0326:
0327: for (RuntimeClassInfo ci : typeSet.beans().values()) {
0328: ClassBeanInfoImpl<?> bi = getOrCreate(ci);
0329:
0330: if (bi.isElement())
0331: rootMap.put(ci.getElementName(), bi);
0332:
0333: for (QName qn : bi.getTypeNames())
0334: typeMap.put(qn, bi);
0335: }
0336:
0337: // fill in element mappings
0338: for (RuntimeElementInfo n : typeSet.getAllElements()) {
0339: ElementBeanInfoImpl bi = getOrCreate(n);
0340: if (n.getScope() == null)
0341: rootMap.put(n.getElementName(), bi);
0342:
0343: RuntimeClassInfo scope = n.getScope();
0344: Class scopeClazz = scope == null ? null : scope.getClazz();
0345: Map<QName, ElementBeanInfoImpl> m = elements
0346: .get(scopeClazz);
0347: if (m == null) {
0348: m = new LinkedHashMap<QName, ElementBeanInfoImpl>();
0349: elements.put(scopeClazz, m);
0350: }
0351: m.put(n.getElementName(), bi);
0352: }
0353:
0354: // this one is so that we can handle plain JAXBElements.
0355: beanInfoMap.put(JAXBElement.class,
0356: new ElementBeanInfoImpl(this ));
0357: // another special BeanInfoImpl just for marshalling
0358: beanInfoMap.put(CompositeStructure.class,
0359: new CompositeStructureBeanInfo(this ));
0360:
0361: getOrCreate(typeSet.getAnyTypeInfo());
0362:
0363: // then link them all!
0364: for (JaxBeanInfo bi : beanInfos.values())
0365: bi.link(this );
0366:
0367: // register primitives for boxed types just to make GrammarInfo fool-proof
0368: for (Map.Entry<Class, Class> e : RuntimeUtil.primitiveToBox
0369: .entrySet())
0370: beanInfoMap.put(e.getKey(), beanInfoMap.get(e.getValue()));
0371:
0372: // build bridges
0373: ReflectionNavigator nav = typeSet.getNavigator();
0374:
0375: for (TypeReference tr : typeRefs) {
0376: XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class);
0377: Adapter<Type, Class> a = null;
0378: XmlList xl = tr.get(XmlList.class);
0379:
0380: // eventually compute the in-memory type
0381: Class erasedType = nav.erasure(tr.type);
0382:
0383: if (xjta != null) {
0384: a = new Adapter<Type, Class>(xjta.value(), nav);
0385: }
0386: if (tr.get(XmlAttachmentRef.class) != null) {
0387: a = new Adapter<Type, Class>(SwaRefAdapter.class, nav);
0388: hasSwaRef = true;
0389: }
0390:
0391: if (a != null) {
0392: erasedType = nav.erasure(a.defaultType);
0393: }
0394:
0395: Name name = nameBuilder.createElementName(tr.tagName);
0396:
0397: InternalBridge bridge;
0398: if (xl == null)
0399: bridge = new BridgeImpl(this , name, getBeanInfo(
0400: erasedType, true), tr);
0401: else
0402: bridge = new BridgeImpl(this , name,
0403: new ValueListBeanInfoImpl(this , erasedType), tr);
0404:
0405: if (a != null)
0406: bridge = new BridgeAdapter(bridge, a.adapterType);
0407:
0408: bridges.put(tr, bridge);
0409: }
0410:
0411: this .nameList = nameBuilder.conclude();
0412:
0413: for (JaxBeanInfo bi : beanInfos.values())
0414: bi.wrapUp();
0415:
0416: // no use for them now
0417: nameBuilder = null;
0418: beanInfos = null;
0419: }
0420:
0421: /**
0422: * True if this JAXBContext has {@link XmlAttachmentRef}.
0423: */
0424: public boolean hasSwaRef() {
0425: return hasSwaRef;
0426: }
0427:
0428: /**
0429: * Creates a {@link RuntimeTypeInfoSet}.
0430: */
0431: private RuntimeTypeInfoSet getTypeInfoSet()
0432: throws IllegalAnnotationsException {
0433:
0434: // check cache
0435: if (typeInfoSetCache != null) {
0436: RuntimeTypeInfoSet r = typeInfoSetCache.get();
0437: if (r != null)
0438: return r;
0439: }
0440:
0441: final RuntimeModelBuilder builder = new RuntimeModelBuilder(
0442: this , annotaitonReader, subclassReplacements,
0443: defaultNsUri);
0444:
0445: IllegalAnnotationsException.Builder errorHandler = new IllegalAnnotationsException.Builder();
0446: builder.setErrorHandler(errorHandler);
0447:
0448: for (Class c : classes) {
0449: if (c == CompositeStructure.class)
0450: // CompositeStructure doesn't have TypeInfo, so skip it.
0451: // We'll add JaxBeanInfo for this later automatically
0452: continue;
0453: builder.getTypeInfo(new Ref<Type, Class>(c));
0454: }
0455:
0456: this .hasSwaRef |= builder.hasSwaRef;
0457: RuntimeTypeInfoSet r = builder.link();
0458:
0459: errorHandler.check();
0460: assert r != null : "if no error was reported, the link must be a success";
0461:
0462: typeInfoSetCache = new WeakReference<RuntimeTypeInfoSet>(r);
0463:
0464: return r;
0465: }
0466:
0467: public ElementBeanInfoImpl getElement(Class scope, QName name) {
0468: Map<QName, ElementBeanInfoImpl> m = elements.get(scope);
0469: if (m != null) {
0470: ElementBeanInfoImpl bi = m.get(name);
0471: if (bi != null)
0472: return bi;
0473: }
0474: m = elements.get(null);
0475: return m.get(name);
0476: }
0477:
0478: private ElementBeanInfoImpl getOrCreate(RuntimeElementInfo rei) {
0479: JaxBeanInfo bi = beanInfos.get(rei);
0480: if (bi != null)
0481: return (ElementBeanInfoImpl) bi;
0482:
0483: // all elements share the same type, so we can't register them to beanInfoMap
0484: return new ElementBeanInfoImpl(this , rei);
0485: }
0486:
0487: protected JaxBeanInfo getOrCreate(RuntimeEnumLeafInfo eli) {
0488: JaxBeanInfo bi = beanInfos.get(eli);
0489: if (bi != null)
0490: return bi;
0491: bi = new LeafBeanInfoImpl(this , eli);
0492: beanInfoMap.put(bi.jaxbType, bi);
0493: return bi;
0494: }
0495:
0496: protected ClassBeanInfoImpl getOrCreate(RuntimeClassInfo ci) {
0497: ClassBeanInfoImpl bi = (ClassBeanInfoImpl) beanInfos.get(ci);
0498: if (bi != null)
0499: return bi;
0500: bi = new ClassBeanInfoImpl(this , ci);
0501: beanInfoMap.put(bi.jaxbType, bi);
0502: return bi;
0503: }
0504:
0505: protected JaxBeanInfo getOrCreate(RuntimeArrayInfo ai) {
0506: JaxBeanInfo abi = beanInfos.get(ai.getType());
0507: if (abi != null)
0508: return abi;
0509:
0510: abi = new ArrayBeanInfoImpl(this , ai);
0511:
0512: beanInfoMap.put(ai.getType(), abi);
0513: return abi;
0514: }
0515:
0516: public JaxBeanInfo getOrCreate(RuntimeTypeInfo e) {
0517: if (e instanceof RuntimeElementInfo)
0518: return getOrCreate((RuntimeElementInfo) e);
0519: if (e instanceof RuntimeClassInfo)
0520: return getOrCreate((RuntimeClassInfo) e);
0521: if (e instanceof RuntimeLeafInfo) {
0522: JaxBeanInfo bi = beanInfos.get(e); // must have been created
0523: assert bi != null;
0524: return bi;
0525: }
0526: if (e instanceof RuntimeArrayInfo)
0527: return getOrCreate((RuntimeArrayInfo) e);
0528: if (e.getType() == Object.class) {
0529: // anyType
0530: JaxBeanInfo bi = beanInfoMap.get(Object.class);
0531: if (bi == null) {
0532: bi = new AnyTypeBeanInfo(this , e);
0533: beanInfoMap.put(Object.class, bi);
0534: }
0535: return bi;
0536: }
0537:
0538: throw new IllegalArgumentException();
0539: }
0540:
0541: /**
0542: * Gets the {@link JaxBeanInfo} object that can handle
0543: * the given JAXB-bound object.
0544: *
0545: * <p>
0546: * This method traverses the base classes of the given object.
0547: *
0548: * @return null
0549: * if <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>.
0550: */
0551: public final JaxBeanInfo getBeanInfo(Object o) {
0552: // don't allow xs:anyType beanInfo to handle all the unbound objects
0553: for (Class c = o.getClass(); c != Object.class; c = c
0554: .getSuperclass()) {
0555: JaxBeanInfo bi = beanInfoMap.get(c);
0556: if (bi != null)
0557: return bi;
0558: }
0559: if (o instanceof Element)
0560: return beanInfoMap.get(Object.class); // return the BeanInfo for xs:anyType
0561: return null;
0562: }
0563:
0564: /**
0565: * Gets the {@link JaxBeanInfo} object that can handle
0566: * the given JAXB-bound object.
0567: *
0568: * @param fatal
0569: * if true, the failure to look up will throw an exception.
0570: * Otherwise it will just return null.
0571: */
0572: public final JaxBeanInfo getBeanInfo(Object o, boolean fatal)
0573: throws JAXBException {
0574: JaxBeanInfo bi = getBeanInfo(o);
0575: if (bi != null)
0576: return bi;
0577: if (fatal) {
0578: if (o instanceof Document)
0579: throw new JAXBException(
0580: Messages.ELEMENT_NEEDED_BUT_FOUND_DOCUMENT
0581: .format(o.getClass()));
0582: throw new JAXBException(Messages.UNKNOWN_CLASS.format(o
0583: .getClass()));
0584: }
0585: return null;
0586: }
0587:
0588: /**
0589: * Gets the {@link JaxBeanInfo} object that can handle
0590: * the given JAXB-bound class.
0591: *
0592: * <p>
0593: * This method doesn't look for base classes.
0594: *
0595: * @return null
0596: * if <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>.
0597: */
0598: public final <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz) {
0599: return (JaxBeanInfo<T>) beanInfoMap.get(clazz);
0600: }
0601:
0602: /**
0603: * Gets the {@link JaxBeanInfo} object that can handle
0604: * the given JAXB-bound class.
0605: *
0606: * @param fatal
0607: * if true, the failure to look up will throw an exception.
0608: * Otherwise it will just return null.
0609: */
0610: public final <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz,
0611: boolean fatal) throws JAXBException {
0612: JaxBeanInfo<T> bi = getBeanInfo(clazz);
0613: if (bi != null)
0614: return bi;
0615: if (fatal)
0616: throw new JAXBException(clazz.getName()
0617: + " is not known to this context");
0618: return null;
0619: }
0620:
0621: /**
0622: * Based on the tag name, determine what object to unmarshal,
0623: * and then set a new object and its loader to the current unmarshaller state.
0624: *
0625: * @return
0626: * null if the given name pair is not recognized.
0627: */
0628: public final Loader selectRootLoader(
0629: UnmarshallingContext.State state, TagName tag) {
0630: JaxBeanInfo beanInfo = rootMap.get(tag.uri, tag.local);
0631: if (beanInfo == null)
0632: return null;
0633:
0634: return beanInfo.getLoader(this , true);
0635: }
0636:
0637: /**
0638: * Gets the {@link JaxBeanInfo} for the given named XML Schema type.
0639: *
0640: * @return
0641: * null if the type name is not recognized. For schema
0642: * languages other than XML Schema, this method always
0643: * returns null.
0644: */
0645: public JaxBeanInfo getGlobalType(QName name) {
0646: return typeMap.get(name);
0647: }
0648:
0649: /**
0650: * Finds a type name that this context recognizes which is
0651: * "closest" to the given type name.
0652: *
0653: * <p>
0654: * This method is used for error recovery.
0655: */
0656: public String getNearestTypeName(QName name) {
0657: String[] all = new String[typeMap.size()];
0658: int i = 0;
0659: for (QName qn : typeMap.keySet()) {
0660: if (qn.getLocalPart().equals(name.getLocalPart()))
0661: return qn.toString(); // probably a match, as people often gets confused about namespace.
0662: all[i++] = qn.toString();
0663: }
0664:
0665: String nearest = EditDistance.findNearest(name.toString(), all);
0666:
0667: if (EditDistance.editDistance(nearest, name.toString()) > 10)
0668: return null; // too far apart.
0669:
0670: return nearest;
0671: }
0672:
0673: /**
0674: * Returns the set of valid root tag names.
0675: * For diagnostic use.
0676: */
0677: public Set<QName> getValidRootNames() {
0678: Set<QName> r = new TreeSet<QName>(QNAME_COMPARATOR);
0679: for (QNameMap.Entry e : rootMap.entrySet()) {
0680: r.add(e.createQName());
0681: }
0682: return r;
0683: }
0684:
0685: /**
0686: * Cache of UTF-8 encoded local names to improve the performance for the marshalling.
0687: */
0688: private Encoded[] utf8nameTable;
0689:
0690: public synchronized Encoded[] getUTF8NameTable() {
0691: if (utf8nameTable == null) {
0692: Encoded[] x = new Encoded[nameList.localNames.length];
0693: for (int i = 0; i < x.length; i++) {
0694: Encoded e = new Encoded(nameList.localNames[i]);
0695: e.compact();
0696: x[i] = e;
0697: }
0698: utf8nameTable = x;
0699: }
0700: return utf8nameTable;
0701: }
0702:
0703: public int getNumberOfLocalNames() {
0704: return nameList.localNames.length;
0705: }
0706:
0707: public int getNumberOfElementNames() {
0708: return nameList.numberOfElementNames;
0709: }
0710:
0711: public int getNumberOfAttributeNames() {
0712: return nameList.numberOfAttributeNames;
0713: }
0714:
0715: /**
0716: * Creates a new identity transformer.
0717: */
0718: static Transformer createTransformer() {
0719: try {
0720: synchronized (JAXBContextImpl.class) {
0721: if (tf == null)
0722: tf = (SAXTransformerFactory) TransformerFactory
0723: .newInstance();
0724: return tf.newTransformer();
0725: }
0726: } catch (TransformerConfigurationException e) {
0727: throw new Error(e); // impossible
0728: }
0729: }
0730:
0731: /**
0732: * Creates a new identity transformer.
0733: */
0734: public static TransformerHandler createTransformerHandler() {
0735: try {
0736: synchronized (JAXBContextImpl.class) {
0737: if (tf == null)
0738: tf = (SAXTransformerFactory) TransformerFactory
0739: .newInstance();
0740: return tf.newTransformerHandler();
0741: }
0742: } catch (TransformerConfigurationException e) {
0743: throw new Error(e); // impossible
0744: }
0745: }
0746:
0747: /**
0748: * Creates a new DOM document.
0749: */
0750: static Document createDom() {
0751: synchronized (JAXBContextImpl.class) {
0752: if (db == null) {
0753: try {
0754: DocumentBuilderFactory dbf = DocumentBuilderFactory
0755: .newInstance();
0756: dbf.setNamespaceAware(true);
0757: db = dbf.newDocumentBuilder();
0758: } catch (ParserConfigurationException e) {
0759: // impossible
0760: throw new FactoryConfigurationError(e);
0761: }
0762: }
0763: return db.newDocument();
0764: }
0765: }
0766:
0767: public MarshallerImpl createMarshaller() {
0768: return new MarshallerImpl(this , null);
0769: }
0770:
0771: public UnmarshallerImpl createUnmarshaller() {
0772: return new UnmarshallerImpl(this , null);
0773: }
0774:
0775: public Validator createValidator() {
0776: throw new UnsupportedOperationException(
0777: Messages.NOT_IMPLEMENTED_IN_2_0.format());
0778: }
0779:
0780: @Override
0781: public JAXBIntrospector createJAXBIntrospector() {
0782: return new JAXBIntrospector() {
0783: public boolean isElement(Object object) {
0784: return getElementName(object) != null;
0785: }
0786:
0787: public QName getElementName(Object jaxbElement) {
0788: try {
0789: return JAXBContextImpl.this
0790: .getElementName(jaxbElement);
0791: } catch (JAXBException e) {
0792: return null;
0793: }
0794: }
0795: };
0796: }
0797:
0798: private NonElement<Type, Class> getXmlType(RuntimeTypeInfoSet tis,
0799: TypeReference tr) {
0800: if (tr == null)
0801: throw new IllegalArgumentException();
0802:
0803: XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class);
0804: XmlList xl = tr.get(XmlList.class);
0805:
0806: Ref<Type, Class> ref = new Ref<Type, Class>(annotaitonReader,
0807: tis.getNavigator(), tr.type, xjta, xl);
0808:
0809: return tis.getTypeInfo(ref);
0810: }
0811:
0812: @Override
0813: public void generateEpisode(Result output) {
0814: if (output == null)
0815: throw new IllegalArgumentException();
0816: createSchemaGenerator().writeEpisodeFile(
0817: ResultFactory.createSerializer(output));
0818: }
0819:
0820: @Override
0821: public void generateSchema(SchemaOutputResolver outputResolver)
0822: throws IOException {
0823: if (outputResolver == null)
0824: throw new IOException(Messages.NULL_OUTPUT_RESOLVER
0825: .format());
0826:
0827: final SAXParseException[] e = new SAXParseException[1];
0828:
0829: createSchemaGenerator().write(outputResolver,
0830: new ErrorListener() {
0831: public void error(SAXParseException exception) {
0832: e[0] = exception;
0833: }
0834:
0835: public void fatalError(SAXParseException exception) {
0836: e[0] = exception;
0837: }
0838:
0839: public void warning(SAXParseException exception) {
0840: }
0841:
0842: public void info(SAXParseException exception) {
0843: }
0844: });
0845:
0846: if (e[0] != null) {
0847: IOException x = new IOException(
0848: Messages.FAILED_TO_GENERATE_SCHEMA.format());
0849: x.initCause(e[0]);
0850: throw x;
0851: }
0852: }
0853:
0854: private XmlSchemaGenerator<Type, Class, Field, Method> createSchemaGenerator() {
0855: RuntimeTypeInfoSet tis;
0856: try {
0857: tis = getTypeInfoSet();
0858: } catch (IllegalAnnotationsException e) {
0859: // this shouldn't happen because we've already
0860: throw new AssertionError(e);
0861: }
0862:
0863: XmlSchemaGenerator<Type, Class, Field, Method> xsdgen = new XmlSchemaGenerator<Type, Class, Field, Method>(
0864: tis.getNavigator(), tis);
0865:
0866: // JAX-RPC uses Bridge objects that collide with
0867: // @XmlRootElement.
0868: // we will avoid collision here
0869: Set<QName> rootTagNames = new HashSet<QName>();
0870: for (RuntimeElementInfo ei : tis.getAllElements()) {
0871: rootTagNames.add(ei.getElementName());
0872: }
0873: for (RuntimeClassInfo ci : tis.beans().values()) {
0874: if (ci.isElement())
0875: rootTagNames.add(ci.asElement().getElementName());
0876: }
0877:
0878: for (TypeReference tr : bridges.keySet()) {
0879: if (rootTagNames.contains(tr.tagName))
0880: continue;
0881:
0882: if (tr.type == void.class || tr.type == Void.class) {
0883: xsdgen.add(tr.tagName, false, null);
0884: } else if (tr.type == CompositeStructure.class) {
0885: // this is a special class we introduced for JAX-WS that we *don't* want in the schema
0886: } else {
0887: NonElement<Type, Class> typeInfo = getXmlType(tis, tr);
0888: xsdgen.add(tr.tagName, !Navigator.REFLECTION
0889: .isPrimitive(tr.type), typeInfo);
0890: }
0891: }
0892: return xsdgen;
0893: }
0894:
0895: public QName getTypeName(TypeReference tr) {
0896: try {
0897: NonElement<Type, Class> xt = getXmlType(getTypeInfoSet(),
0898: tr);
0899: if (xt == null)
0900: throw new IllegalArgumentException();
0901: return xt.getTypeName();
0902: } catch (IllegalAnnotationsException e) {
0903: // impossible given that JAXBRIContext has been successfully built in the first place
0904: throw new AssertionError(e);
0905: }
0906: }
0907:
0908: /**
0909: * Used for testing.
0910: */
0911: public SchemaOutputResolver createTestResolver() {
0912: return new SchemaOutputResolver() {
0913: public Result createOutput(String namespaceUri,
0914: String suggestedFileName) {
0915: SAXResult r = new SAXResult(new DefaultHandler());
0916: r.setSystemId(suggestedFileName);
0917: return r;
0918: }
0919: };
0920: }
0921:
0922: @Override
0923: public <T> Binder<T> createBinder(Class<T> domType) {
0924: if (domType == Node.class)
0925: return (Binder<T>) createBinder();
0926: else
0927: return super .createBinder(domType);
0928: }
0929:
0930: @Override
0931: public Binder<Node> createBinder() {
0932: return new BinderImpl<Node>(this , new DOMScanner());
0933: }
0934:
0935: public QName getElementName(Object o) throws JAXBException {
0936: JaxBeanInfo bi = getBeanInfo(o, true);
0937: if (!bi.isElement())
0938: return null;
0939: return new QName(bi.getElementNamespaceURI(o), bi
0940: .getElementLocalName(o));
0941: }
0942:
0943: public Bridge createBridge(TypeReference ref) {
0944: return bridges.get(ref);
0945: }
0946:
0947: public @NotNull
0948: BridgeContext createBridgeContext() {
0949: return new BridgeContextImpl(this );
0950: }
0951:
0952: public RawAccessor getElementPropertyAccessor(Class wrapperBean,
0953: String nsUri, String localName) throws JAXBException {
0954: JaxBeanInfo bi = getBeanInfo(wrapperBean, true);
0955: if (!(bi instanceof ClassBeanInfoImpl))
0956: throw new JAXBException(wrapperBean + " is not a bean");
0957:
0958: for (ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi; cb != null; cb = cb.super Clazz) {
0959: for (Property p : cb.properties) {
0960: final Accessor acc = p.getElementPropertyAccessor(
0961: nsUri, localName);
0962: if (acc != null)
0963: return new RawAccessor() {
0964: // Accessor.set/get are designed for unmarshaller/marshaller, and hence
0965: // they go through an adapter behind the scene.
0966: // this isn't desirable for JAX-WS, which essentially uses this method
0967: // just as a reflection library. So use the "unadapted" version to
0968: // achieve the desired semantics
0969: public Object get(Object bean)
0970: throws AccessorException {
0971: return acc.getUnadapted(bean);
0972: }
0973:
0974: public void set(Object bean, Object value)
0975: throws AccessorException {
0976: acc.setUnadapted(bean, value);
0977: }
0978: };
0979: }
0980: }
0981: throw new JAXBException(new QName(nsUri, localName)
0982: + " is not a valid property on " + wrapperBean);
0983: }
0984:
0985: public List<String> getKnownNamespaceURIs() {
0986: return Arrays.asList(nameList.namespaceURIs);
0987: }
0988:
0989: public String getBuildId() {
0990: Package pkg = getClass().getPackage();
0991: if (pkg == null)
0992: return null;
0993: return pkg.getImplementationVersion();
0994: }
0995:
0996: public String toString() {
0997: StringBuilder buf = new StringBuilder(Which.which(getClass())
0998: + " Build-Id: " + getBuildId());
0999: buf.append("\nClasses known to this context:\n");
1000:
1001: Set<String> names = new TreeSet<String>(); // sort them so that it's easy to read
1002:
1003: for (Class key : beanInfoMap.keySet())
1004: names.add(key.getName());
1005:
1006: for (String name : names)
1007: buf.append(" ").append(name).append('\n');
1008:
1009: return buf.toString();
1010: }
1011:
1012: /**
1013: * Gets the value of the xmime:contentType attribute on the given object, or null
1014: * if for some reason it couldn't be found, including any error.
1015: */
1016: public String getXMIMEContentType(Object o) {
1017: JaxBeanInfo bi = getBeanInfo(o);
1018: if (!(bi instanceof ClassBeanInfoImpl))
1019: return null;
1020:
1021: ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi;
1022: for (Property p : cb.properties) {
1023: if (p instanceof AttributeProperty) {
1024: AttributeProperty ap = (AttributeProperty) p;
1025: if (ap.attName.equals(WellKnownNamespace.XML_MIME_URI,
1026: "contentType"))
1027: try {
1028: return (String) ap.xacc.print(o);
1029: } catch (AccessorException e) {
1030: return null;
1031: } catch (SAXException e) {
1032: return null;
1033: } catch (ClassCastException e) {
1034: return null;
1035: }
1036: }
1037: }
1038: return null;
1039: }
1040:
1041: /**
1042: * Creates a {@link JAXBContextImpl} that includes the specified additional classes.
1043: */
1044: public JAXBContextImpl createAugmented(Class<?> clazz)
1045: throws JAXBException {
1046: Class[] newList = new Class[classes.length + 1];
1047: System.arraycopy(classes, 0, newList, 0, classes.length);
1048: newList[classes.length] = clazz;
1049:
1050: return new JAXBContextImpl(newList, bridges.keySet(),
1051: subclassReplacements, defaultNsUri, c14nSupport,
1052: annotaitonReader, xmlAccessorFactorySupport,
1053: allNillable);
1054: }
1055:
1056: private static final Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() {
1057: public int compare(QName lhs, QName rhs) {
1058: int r = lhs.getLocalPart().compareTo(rhs.getLocalPart());
1059: if (r != 0)
1060: return r;
1061:
1062: return lhs.getNamespaceURI().compareTo(
1063: rhs.getNamespaceURI());
1064: }
1065: };
1066: }
|