001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.v2.runtime;
027:
028: import java.io.IOException;
029: import java.lang.ref.WeakReference;
030: import java.lang.reflect.Field;
031: import java.lang.reflect.Method;
032: import java.lang.reflect.Type;
033: import java.util.Arrays;
034: import java.util.Collection;
035: import java.util.Comparator;
036: import java.util.HashMap;
037: import java.util.HashSet;
038: import java.util.LinkedHashMap;
039: import java.util.List;
040: import java.util.Map;
041: import java.util.Set;
042: import java.util.TreeSet;
043:
044: import javax.xml.bind.Binder;
045: import javax.xml.bind.DatatypeConverter;
046: import javax.xml.bind.JAXBElement;
047: import javax.xml.bind.JAXBException;
048: import javax.xml.bind.JAXBIntrospector;
049: import javax.xml.bind.Marshaller;
050: import javax.xml.bind.SchemaOutputResolver;
051: import javax.xml.bind.Unmarshaller;
052: import javax.xml.bind.Validator;
053: import javax.xml.bind.annotation.XmlAttachmentRef;
054: import javax.xml.bind.annotation.XmlList;
055: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
056: import javax.xml.namespace.QName;
057: import javax.xml.parsers.DocumentBuilder;
058: import javax.xml.parsers.DocumentBuilderFactory;
059: import javax.xml.parsers.FactoryConfigurationError;
060: import javax.xml.parsers.ParserConfigurationException;
061: import javax.xml.transform.Result;
062: import javax.xml.transform.Transformer;
063: import javax.xml.transform.TransformerConfigurationException;
064: import javax.xml.transform.TransformerFactory;
065: import javax.xml.transform.sax.SAXResult;
066: import javax.xml.transform.sax.SAXTransformerFactory;
067: import javax.xml.transform.sax.TransformerHandler;
068:
069: import com.sun.istack.internal.NotNull;
070: import com.sun.istack.internal.Pool;
071: import com.sun.xml.internal.bind.DatatypeConverterImpl;
072: import com.sun.xml.internal.bind.api.AccessorException;
073: import com.sun.xml.internal.bind.api.Bridge;
074: import com.sun.xml.internal.bind.api.BridgeContext;
075: import com.sun.xml.internal.bind.api.CompositeStructure;
076: import com.sun.xml.internal.bind.api.JAXBRIContext;
077: import com.sun.xml.internal.bind.api.RawAccessor;
078: import com.sun.xml.internal.bind.api.TypeReference;
079: import com.sun.xml.internal.bind.unmarshaller.DOMScanner;
080: import com.sun.xml.internal.bind.util.Which;
081: import com.sun.xml.internal.bind.v2.WellKnownNamespace;
082: import com.sun.xml.internal.bind.v2.model.annotation.RuntimeInlineAnnotationReader;
083: import com.sun.xml.internal.bind.v2.model.core.Adapter;
084: import com.sun.xml.internal.bind.v2.model.core.NonElement;
085: import com.sun.xml.internal.bind.v2.model.core.Ref;
086: import com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl;
087: import com.sun.xml.internal.bind.v2.model.impl.RuntimeModelBuilder;
088: import com.sun.xml.internal.bind.v2.model.nav.Navigator;
089: import com.sun.xml.internal.bind.v2.model.nav.ReflectionNavigator;
090: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeArrayInfo;
091: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeBuiltinLeafInfo;
092: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo;
093: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElementInfo;
094: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeEnumLeafInfo;
095: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeLeafInfo;
096: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfo;
097: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfoSet;
098: import com.sun.xml.internal.bind.v2.runtime.output.Encoded;
099: import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty;
100: import com.sun.xml.internal.bind.v2.runtime.property.Property;
101: import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
102: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
103: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.TagName;
104: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl;
105: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
106: import com.sun.xml.internal.bind.v2.schemagen.XmlSchemaGenerator;
107: import com.sun.xml.internal.bind.v2.util.EditDistance;
108: import com.sun.xml.internal.bind.v2.util.QNameMap;
109:
110: import org.w3c.dom.Document;
111: import org.w3c.dom.Element;
112: import org.w3c.dom.Node;
113: import org.xml.sax.SAXException;
114: import org.xml.sax.helpers.DefaultHandler;
115:
116: /**
117: * This class provides the implementation of JAXBContext.
118: *
119: * @version $Revision: 1.74 $
120: */
121: public final class JAXBContextImpl extends JAXBRIContext {
122:
123: /**
124: * All the bridge classes.
125: */
126: private final Map<TypeReference, Bridge> bridges = new LinkedHashMap<TypeReference, Bridge>();
127:
128: /**
129: * Shared instance of {@link TransformerFactory}.
130: * Lock before use, because a {@link TransformerFactory} is not thread-safe
131: * whereas {@link JAXBContextImpl} is.
132: * Lazily created.
133: */
134: private static SAXTransformerFactory tf;
135:
136: /**
137: * Shared instance of {@link DocumentBuilder}.
138: * Lock before use. Lazily created.
139: */
140: private static DocumentBuilder db;
141:
142: private final QNameMap<JaxBeanInfo> rootMap = new QNameMap<JaxBeanInfo>();
143: private final HashMap<QName, JaxBeanInfo> typeMap = new HashMap<QName, JaxBeanInfo>();
144:
145: /**
146: * Map from JAXB-bound {@link Class} to its {@link JaxBeanInfo}.
147: */
148: private final Map<Class, JaxBeanInfo> beanInfoMap = new LinkedHashMap<Class, JaxBeanInfo>();
149:
150: /**
151: * All created {@link JaxBeanInfo}s.
152: * Updated from each {@link JaxBeanInfo}s constructors to avoid infinite recursion
153: * for a cyclic reference.
154: *
155: * <p>
156: * This map is only used while the {@link JAXBContextImpl} is built and set to null
157: * to avoid keeping references too long.
158: */
159: protected Map<RuntimeTypeInfo, JaxBeanInfo> beanInfos = new LinkedHashMap<RuntimeTypeInfo, JaxBeanInfo>();
160:
161: private final Map<Class/*scope*/, Map<QName, ElementBeanInfoImpl>> elements = new LinkedHashMap<Class, Map<QName, ElementBeanInfoImpl>>();
162:
163: /**
164: * Pool of {@link Marshaller}s.
165: */
166: public final Pool<Marshaller> marshallerPool = new Pool.Impl<Marshaller>() {
167: protected @NotNull
168: Marshaller create() {
169: return createMarshaller();
170: }
171: };
172:
173: public final Pool<Unmarshaller> unmarshallerPool = new Pool.Impl<Unmarshaller>() {
174: protected @NotNull
175: Unmarshaller create() {
176: return createUnmarshaller();
177: }
178: };
179:
180: /**
181: * Used to assign indices to known names in this grammar.
182: * Reset to null once the build phase is completed.
183: */
184: public NameBuilder nameBuilder = new NameBuilder();
185:
186: /**
187: * Keeps the list of known names.
188: * This field is set once the build pahse is completed.
189: */
190: public final NameList nameList;
191:
192: /**
193: * Input to the JAXBContext.newInstance, so that we can recreate
194: * {@link RuntimeTypeInfoSet} whenever we need.
195: */
196: private final String defaultNsUri;
197: private final Class[] classes;
198:
199: /**
200: * true to reorder attributes lexicographically in preparation of the c14n support.
201: */
202: protected final boolean c14nSupport;
203:
204: private WeakReference<RuntimeTypeInfoSet> typeInfoSetCache;
205:
206: /**
207: *
208: * @param typeRefs
209: * used to build {@link Bridge}s. Can be empty.
210: * @param c14nSupport
211: * {@link #c14nSupport}.
212: */
213: public JAXBContextImpl(Class[] classes,
214: Collection<TypeReference> typeRefs, String defaultNsUri,
215: boolean c14nSupport) throws JAXBException {
216:
217: // initialize datatype converter with ours
218: DatatypeConverter
219: .setDatatypeConverter(DatatypeConverterImpl.theInstance);
220:
221: if (defaultNsUri == null)
222: defaultNsUri = ""; // fool-proof
223:
224: this .defaultNsUri = defaultNsUri;
225: this .c14nSupport = c14nSupport;
226: this .classes = new Class[classes.length];
227: System.arraycopy(classes, 0, this .classes, 0, classes.length);
228:
229: RuntimeTypeInfoSet typeSet = getTypeInfoSet();
230:
231: // at least prepare the empty table so that we don't have to check for null later
232: elements.put(null,
233: new LinkedHashMap<QName, ElementBeanInfoImpl>());
234:
235: // recognize leaf bean infos
236: for (RuntimeBuiltinLeafInfo leaf : RuntimeBuiltinLeafInfoImpl.builtinBeanInfos) {
237: LeafBeanInfoImpl<?> bi = new LeafBeanInfoImpl(this , leaf);
238: beanInfoMap.put(leaf.getClazz(), bi);
239: for (QName t : bi.getTypeNames())
240: typeMap.put(t, bi);
241: }
242:
243: for (RuntimeEnumLeafInfo e : typeSet.enums().values()) {
244: JaxBeanInfo<?> bi = getOrCreate(e);
245: for (QName qn : bi.getTypeNames())
246: typeMap.put(qn, bi);
247: if (e.isElement())
248: rootMap.put(e.getElementName(), bi);
249: }
250:
251: for (RuntimeArrayInfo a : typeSet.arrays().values()) {
252: JaxBeanInfo<?> ai = getOrCreate(a);
253: for (QName qn : ai.getTypeNames())
254: typeMap.put(qn, ai);
255: }
256:
257: for (RuntimeClassInfo ci : typeSet.beans().values()) {
258: ClassBeanInfoImpl<?> bi = getOrCreate(ci);
259:
260: if (bi.isElement())
261: rootMap.put(ci.getElementName(), bi);
262:
263: for (QName qn : bi.getTypeNames())
264: typeMap.put(qn, bi);
265: }
266:
267: // fill in element mappings
268: for (RuntimeElementInfo n : typeSet.getAllElements()) {
269: ElementBeanInfoImpl bi = getOrCreate(n);
270: if (n.getScope() == null)
271: rootMap.put(n.getElementName(), bi);
272:
273: RuntimeClassInfo scope = n.getScope();
274: Class scopeClazz = scope == null ? null : scope.getClazz();
275: Map<QName, ElementBeanInfoImpl> m = elements
276: .get(scopeClazz);
277: if (m == null) {
278: m = new LinkedHashMap<QName, ElementBeanInfoImpl>();
279: elements.put(scopeClazz, m);
280: }
281: m.put(n.getElementName(), bi);
282: }
283:
284: // this one is so that we can handle plain JAXBElements.
285: beanInfoMap.put(JAXBElement.class,
286: new ElementBeanInfoImpl(this ));
287: // another special BeanInfoImpl just for marshalling
288: beanInfoMap.put(CompositeStructure.class,
289: new CompositeStructureBeanInfo(this ));
290:
291: getOrCreate(typeSet.getAnyTypeInfo());
292:
293: // then link them all!
294: for (JaxBeanInfo bi : beanInfos.values())
295: bi.link(this );
296:
297: // register primitives for boxed types just to make GrammarInfo fool-proof
298: for (Map.Entry<Class, Class> e : RuntimeUtil.primitiveToBox
299: .entrySet())
300: beanInfoMap.put(e.getKey(), beanInfoMap.get(e.getValue()));
301:
302: // build bridges
303: ReflectionNavigator nav = typeSet.getNavigator();
304:
305: for (TypeReference tr : typeRefs) {
306: XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class);
307: Adapter<Type, Class> a = null;
308: XmlList xl = tr.get(XmlList.class);
309:
310: // eventually compute the in-memory type
311: Class erasedType = nav.erasure(tr.type);
312:
313: if (xjta != null) {
314: a = new Adapter<Type, Class>(xjta.value(), nav);
315: }
316: if (tr.get(XmlAttachmentRef.class) != null) {
317: a = new Adapter<Type, Class>(SwaRefAdapter.class, nav);
318: }
319:
320: if (a != null) {
321: erasedType = nav.erasure(a.defaultType);
322: }
323:
324: Name name = nameBuilder.createElementName(tr.tagName);
325:
326: InternalBridge bridge;
327: if (xl == null)
328: bridge = new BridgeImpl(this , name, getBeanInfo(
329: erasedType, true), tr);
330: else
331: bridge = new BridgeImpl(this , name,
332: new ValueListBeanInfoImpl(this , erasedType), tr);
333:
334: if (a != null)
335: bridge = new BridgeAdapter(bridge, a.adapterType);
336:
337: bridges.put(tr, bridge);
338: }
339:
340: this .nameList = nameBuilder.conclude();
341:
342: for (JaxBeanInfo bi : beanInfos.values())
343: bi.wrapUp();
344:
345: // no use for them now
346: nameBuilder = null;
347: beanInfos = null;
348: }
349:
350: /**
351: * Creates a {@link RuntimeTypeInfoSet}.
352: */
353: private RuntimeTypeInfoSet getTypeInfoSet()
354: throws IllegalAnnotationsException {
355:
356: // check cache
357: if (typeInfoSetCache != null) {
358: RuntimeTypeInfoSet r = typeInfoSetCache.get();
359: if (r != null)
360: return r;
361: }
362:
363: final RuntimeModelBuilder builder = new RuntimeModelBuilder(
364: new RuntimeInlineAnnotationReader(), defaultNsUri);
365: IllegalAnnotationsException.Builder errorHandler = new IllegalAnnotationsException.Builder();
366: builder.setErrorHandler(errorHandler);
367:
368: for (Class c : classes) {
369: if (c == CompositeStructure.class)
370: // CompositeStructure doesn't have TypeInfo, so skip it.
371: // We'll add JaxBeanInfo for this later automatically
372: continue;
373: builder.getTypeInfo(new Ref<Type, Class>(c));
374: }
375:
376: RuntimeTypeInfoSet r = builder.link();
377:
378: errorHandler.check();
379: assert r != null : "if no error was reported, the link must be a success";
380:
381: typeInfoSetCache = new WeakReference<RuntimeTypeInfoSet>(r);
382:
383: return r;
384: }
385:
386: public ElementBeanInfoImpl getElement(Class scope, QName name) {
387: Map<QName, ElementBeanInfoImpl> m = elements.get(scope);
388: if (m != null) {
389: ElementBeanInfoImpl bi = m.get(name);
390: if (bi != null)
391: return bi;
392: }
393: m = elements.get(null);
394: return m.get(name);
395: }
396:
397: private ElementBeanInfoImpl getOrCreate(RuntimeElementInfo rei) {
398: JaxBeanInfo bi = beanInfos.get(rei);
399: if (bi != null)
400: return (ElementBeanInfoImpl) bi;
401:
402: // all elements share the same type, so we can't register them to beanInfoMap
403: return new ElementBeanInfoImpl(this , rei);
404: }
405:
406: protected JaxBeanInfo getOrCreate(RuntimeEnumLeafInfo eli) {
407: JaxBeanInfo bi = beanInfos.get(eli);
408: if (bi != null)
409: return bi;
410: bi = new LeafBeanInfoImpl(this , eli);
411: beanInfoMap.put(bi.jaxbType, bi);
412: return bi;
413: }
414:
415: protected ClassBeanInfoImpl getOrCreate(RuntimeClassInfo ci) {
416: ClassBeanInfoImpl bi = (ClassBeanInfoImpl) beanInfos.get(ci);
417: if (bi != null)
418: return bi;
419: bi = new ClassBeanInfoImpl(this , ci);
420: beanInfoMap.put(bi.jaxbType, bi);
421: return bi;
422: }
423:
424: protected JaxBeanInfo getOrCreate(RuntimeArrayInfo ai) {
425: JaxBeanInfo abi = beanInfos.get(ai.getType());
426: if (abi != null)
427: return abi;
428:
429: abi = new ArrayBeanInfoImpl(this , ai);
430:
431: beanInfoMap.put(ai.getType(), abi);
432: return abi;
433: }
434:
435: public JaxBeanInfo getOrCreate(RuntimeTypeInfo e) {
436: if (e instanceof RuntimeElementInfo)
437: return getOrCreate((RuntimeElementInfo) e);
438: if (e instanceof RuntimeClassInfo)
439: return getOrCreate((RuntimeClassInfo) e);
440: if (e instanceof RuntimeLeafInfo) {
441: JaxBeanInfo bi = beanInfos.get(e); // must have been created
442: assert bi != null;
443: return bi;
444: }
445: if (e instanceof RuntimeArrayInfo)
446: return getOrCreate((RuntimeArrayInfo) e);
447: if (e.getType() == Object.class) {
448: // anyType
449: JaxBeanInfo bi = beanInfoMap.get(Object.class);
450: if (bi == null) {
451: bi = new AnyTypeBeanInfo(this , e);
452: beanInfoMap.put(Object.class, bi);
453: }
454: return bi;
455: }
456:
457: throw new IllegalArgumentException();
458: }
459:
460: /**
461: * Gets the {@link JaxBeanInfo} object that can handle
462: * the given JAXB-bound object.
463: *
464: * <p>
465: * This method traverses the base classes of the given object.
466: *
467: * @return null
468: * if <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>.
469: */
470: public final JaxBeanInfo getBeanInfo(Object o) {
471: // don't allow xs:anyType beanInfo to handle all the unbound objects
472: for (Class c = o.getClass(); c != Object.class; c = c
473: .getSuperclass()) {
474: JaxBeanInfo bi = beanInfoMap.get(c);
475: if (bi != null)
476: return bi;
477: }
478: if (o instanceof Element)
479: return beanInfoMap.get(Object.class); // return the BeanInfo for xs:anyType
480: return null;
481: }
482:
483: /**
484: * Gets the {@link JaxBeanInfo} object that can handle
485: * the given JAXB-bound object.
486: *
487: * @param fatal
488: * if true, the failure to look up will throw an exception.
489: * Otherwise it will just return null.
490: */
491: public final JaxBeanInfo getBeanInfo(Object o, boolean fatal)
492: throws JAXBException {
493: JaxBeanInfo bi = getBeanInfo(o);
494: if (bi != null)
495: return bi;
496: if (fatal)
497: throw new JAXBException(
498: o.getClass().getName()
499: + " nor any of its super class is known to this context");
500: return null;
501: }
502:
503: /**
504: * Gets the {@link JaxBeanInfo} object that can handle
505: * the given JAXB-bound class.
506: *
507: * <p>
508: * This method doesn't look for base classes.
509: *
510: * @return null
511: * if <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>.
512: */
513: public final <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz) {
514: return (JaxBeanInfo<T>) beanInfoMap.get(clazz);
515: }
516:
517: /**
518: * Gets the {@link JaxBeanInfo} object that can handle
519: * the given JAXB-bound class.
520: *
521: * @param fatal
522: * if true, the failure to look up will throw an exception.
523: * Otherwise it will just return null.
524: */
525: public final <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz,
526: boolean fatal) throws JAXBException {
527: JaxBeanInfo<T> bi = getBeanInfo(clazz);
528: if (bi != null)
529: return bi;
530: if (fatal)
531: throw new JAXBException(clazz.getName()
532: + " is not known to this context");
533: return null;
534: }
535:
536: /**
537: * Based on the tag name, determine what object to unmarshal,
538: * and then set a new object and its loader to the current unmarshaller state.
539: *
540: * @return
541: * null if the given name pair is not recognized.
542: */
543: public final Loader selectRootLoader(
544: UnmarshallingContext.State state, TagName ea) {
545: JaxBeanInfo beanInfo = rootMap.get(ea.uri, ea.local);
546: if (beanInfo == null)
547: // TODO: this is probably the right place to handle @xsi:type
548: return null;
549:
550: return beanInfo.getLoader(this , true);
551: }
552:
553: /**
554: * Gets the {@link JaxBeanInfo} for the given named XML Schema type.
555: *
556: * @return
557: * null if the type name is not recognized. For schema
558: * languages other than XML Schema, this method always
559: * returns null.
560: */
561: public JaxBeanInfo getGlobalType(QName name) {
562: return typeMap.get(name);
563: }
564:
565: /**
566: * Finds a type name that this context recognizes which is
567: * "closest" to the given type name.
568: *
569: * <p>
570: * This method is used for error recovery.
571: */
572: public String getNearestTypeName(QName name) {
573: String[] all = new String[typeMap.size()];
574: int i = 0;
575: for (QName qn : typeMap.keySet()) {
576: if (qn.getLocalPart().equals(name.getLocalPart()))
577: return qn.toString(); // probably a match, as people often gets confused about namespace.
578: all[i++] = qn.toString();
579: }
580:
581: String nearest = EditDistance.findNearest(name.toString(), all);
582:
583: if (EditDistance.editDistance(nearest, name.toString()) > 10)
584: return null; // too far apart.
585:
586: return nearest;
587: }
588:
589: /**
590: * Returns the set of valid root tag names.
591: * For diagnostic use.
592: */
593: public Set<QName> getValidRootNames() {
594: Set<QName> r = new TreeSet<QName>(QNAME_COMPARATOR);
595: for (QNameMap.Entry e : rootMap.entrySet()) {
596: r.add(e.createQName());
597: }
598: return r;
599: }
600:
601: /**
602: * Cache of UTF-8 encoded local names to improve the performance for the marshalling.
603: */
604: private Encoded[] utf8nameTable;
605:
606: public synchronized Encoded[] getUTF8NameTable() {
607: if (utf8nameTable == null) {
608: Encoded[] x = new Encoded[nameList.localNames.length];
609: for (int i = 0; i < x.length; i++) {
610: Encoded e = new Encoded(nameList.localNames[i]);
611: e.compact();
612: x[i] = e;
613: }
614: utf8nameTable = x;
615: }
616: return utf8nameTable;
617: }
618:
619: public int getNumberOfLocalNames() {
620: return nameList.localNames.length;
621: }
622:
623: public int getNumberOfElementNames() {
624: return nameList.numberOfElementNames;
625: }
626:
627: public int getNumberOfAttributeNames() {
628: return nameList.numberOfAttributeNames;
629: }
630:
631: /**
632: * Creates a new identity transformer.
633: */
634: static Transformer createTransformer() {
635: try {
636: synchronized (JAXBContextImpl.class) {
637: if (tf == null)
638: tf = (SAXTransformerFactory) TransformerFactory
639: .newInstance();
640: return tf.newTransformer();
641: }
642: } catch (TransformerConfigurationException e) {
643: throw new Error(e); // impossible
644: }
645: }
646:
647: /**
648: * Creates a new identity transformer.
649: */
650: public static TransformerHandler createTransformerHandler() {
651: try {
652: synchronized (JAXBContextImpl.class) {
653: if (tf == null)
654: tf = (SAXTransformerFactory) TransformerFactory
655: .newInstance();
656: return tf.newTransformerHandler();
657: }
658: } catch (TransformerConfigurationException e) {
659: throw new Error(e); // impossible
660: }
661: }
662:
663: /**
664: * Creates a new DOM document.
665: */
666: static Document createDom() {
667: synchronized (JAXBContextImpl.class) {
668: if (db == null) {
669: try {
670: DocumentBuilderFactory dbf = DocumentBuilderFactory
671: .newInstance();
672: dbf.setNamespaceAware(true);
673: db = dbf.newDocumentBuilder();
674: } catch (ParserConfigurationException e) {
675: // impossible
676: throw new FactoryConfigurationError(e);
677: }
678: }
679: return db.newDocument();
680: }
681: }
682:
683: public MarshallerImpl createMarshaller() {
684: return new MarshallerImpl(this , null);
685: }
686:
687: public UnmarshallerImpl createUnmarshaller() {
688: return new UnmarshallerImpl(this , null);
689: }
690:
691: public Validator createValidator() {
692: throw new UnsupportedOperationException(
693: Messages.NOT_IMPLEMENTED_IN_2_0.format());
694: }
695:
696: @Override
697: public JAXBIntrospector createJAXBIntrospector() {
698: return new JAXBIntrospector() {
699: public boolean isElement(Object object) {
700: return getElementName(object) != null;
701: }
702:
703: public QName getElementName(Object jaxbElement) {
704: try {
705: return JAXBContextImpl.this
706: .getElementName(jaxbElement);
707: } catch (JAXBException e) {
708: return null;
709: }
710: }
711: };
712: }
713:
714: private NonElement<Type, Class> getXmlType(RuntimeTypeInfoSet tis,
715: TypeReference tr) {
716: if (tr == null)
717: throw new IllegalArgumentException();
718:
719: XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class);
720: XmlList xl = tr.get(XmlList.class);
721:
722: Ref<Type, Class> ref = new Ref<Type, Class>(
723: new RuntimeInlineAnnotationReader(),
724: tis.getNavigator(), tr.type, xjta, xl);
725:
726: return tis.getTypeInfo(ref);
727: }
728:
729: public void generateSchema(SchemaOutputResolver outputResolver)
730: throws IOException {
731: if (outputResolver == null) {
732: throw new IOException(Messages.NULL_OUTPUT_RESOLVER
733: .format());
734: }
735:
736: RuntimeTypeInfoSet tis;
737: try {
738: tis = getTypeInfoSet();
739: } catch (IllegalAnnotationsException e) {
740: // this shouldn't happen because we've already
741: throw new AssertionError(e);
742: }
743:
744: XmlSchemaGenerator<Type, Class, Field, Method> xsdgen = new XmlSchemaGenerator<Type, Class, Field, Method>(
745: tis.getNavigator(), tis);
746:
747: // JAX-RPC uses Bridge objects that collide with
748: // @XmlRootElement.
749: // we will avoid collision here
750: Set<QName> rootTagNames = new HashSet<QName>();
751: for (RuntimeElementInfo ei : tis.getAllElements()) {
752: rootTagNames.add(ei.getElementName());
753: }
754: for (RuntimeClassInfo ci : tis.beans().values()) {
755: if (ci.isElement())
756: rootTagNames.add(ci.asElement().getElementName());
757: }
758:
759: for (TypeReference tr : bridges.keySet()) {
760: if (rootTagNames.contains(tr.tagName))
761: continue;
762:
763: if (tr.type == void.class || tr.type == Void.class) {
764: xsdgen.add(tr.tagName, false, null);
765: } else {
766: NonElement<Type, Class> typeInfo = getXmlType(tis, tr);
767: xsdgen.add(tr.tagName, !Navigator.REFLECTION
768: .isPrimitive(tr.type), typeInfo);
769: }
770: }
771:
772: xsdgen.write(outputResolver);
773: }
774:
775: public QName getTypeName(TypeReference tr) {
776: try {
777: NonElement<Type, Class> xt = getXmlType(getTypeInfoSet(),
778: tr);
779: if (xt == null)
780: throw new IllegalArgumentException();
781: return xt.getTypeName();
782: } catch (IllegalAnnotationsException e) {
783: // impossible given that JAXBRIContext has been successfully built in the first place
784: throw new AssertionError(e);
785: }
786: }
787:
788: /**
789: * Used for testing.
790: */
791: public SchemaOutputResolver createTestResolver() {
792: return new SchemaOutputResolver() {
793: public Result createOutput(String namespaceUri,
794: String suggestedFileName) {
795: SAXResult r = new SAXResult(new DefaultHandler());
796: r.setSystemId(suggestedFileName);
797: return r;
798: }
799: };
800: }
801:
802: @Override
803: public <T> Binder<T> createBinder(Class<T> domType) {
804: if (domType == Node.class)
805: return (Binder<T>) createBinder();
806: else
807: return super .createBinder(domType);
808: }
809:
810: @Override
811: public Binder<Node> createBinder() {
812: return new BinderImpl<Node>(this , new DOMScanner());
813: }
814:
815: public QName getElementName(Object o) throws JAXBException {
816: JaxBeanInfo bi = getBeanInfo(o, true);
817: if (!bi.isElement())
818: return null;
819: return new QName(bi.getElementNamespaceURI(o), bi
820: .getElementLocalName(o));
821: }
822:
823: public Bridge createBridge(TypeReference ref) {
824: return bridges.get(ref);
825: }
826:
827: public @NotNull
828: BridgeContext createBridgeContext() {
829: return new BridgeContextImpl(this );
830: }
831:
832: public RawAccessor getElementPropertyAccessor(Class wrapperBean,
833: String nsUri, String localName) throws JAXBException {
834: JaxBeanInfo bi = getBeanInfo(wrapperBean, true);
835: if (!(bi instanceof ClassBeanInfoImpl))
836: throw new JAXBException(wrapperBean + " is not a bean");
837:
838: for (ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi; cb != null; cb = cb.super Clazz) {
839: for (Property p : cb.properties) {
840: final Accessor acc = p.getElementPropertyAccessor(
841: nsUri, localName);
842: if (acc != null)
843: return new RawAccessor() {
844: // Accessor.set/get are designed for unmarshaller/marshaller, and hence
845: // they go through an adapter behind the scene.
846: // this isn't desirable for JAX-WS, which essentially uses this method
847: // just as a reflection library. So use the "unadapted" version to
848: // achieve the desired semantics
849: public Object get(Object bean)
850: throws AccessorException {
851: return acc.getUnadapted(bean);
852: }
853:
854: public void set(Object bean, Object value)
855: throws AccessorException {
856: acc.setUnadapted(bean, value);
857: }
858: };
859: }
860: }
861: throw new JAXBException(new QName(nsUri, localName)
862: + " is not a valid property on " + wrapperBean);
863: }
864:
865: public List<String> getKnownNamespaceURIs() {
866: return Arrays.asList(nameList.namespaceURIs);
867: }
868:
869: public String getBuildId() {
870: Package pkg = getClass().getPackage();
871: if (pkg == null)
872: return null;
873: return pkg.getImplementationVersion();
874: }
875:
876: public String toString() {
877: StringBuilder buf = new StringBuilder(Which.which(getClass())
878: + " Build-Id: " + getBuildId());
879: buf.append("\nClasses known to this context:\n");
880:
881: Set<String> names = new TreeSet<String>(); // sort them so that it's easy to read
882:
883: for (Class key : beanInfoMap.keySet())
884: names.add(key.getName());
885:
886: for (String name : names)
887: buf.append(" ").append(name).append('\n');
888:
889: return buf.toString();
890: }
891:
892: /**
893: * Gets the value of the xmime:contentType attribute on the given object, or null
894: * if for some reason it couldn't be found, including any error.
895: */
896: public String getXMIMEContentType(Object o) {
897: JaxBeanInfo bi = getBeanInfo(o);
898: if (!(bi instanceof ClassBeanInfoImpl))
899: return null;
900:
901: ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi;
902: for (Property p : cb.properties) {
903: if (p instanceof AttributeProperty) {
904: AttributeProperty ap = (AttributeProperty) p;
905: if (ap.attName.equals(WellKnownNamespace.XML_MIME_URI,
906: "contentType"))
907: try {
908: return (String) ap.xacc.print(o);
909: } catch (AccessorException e) {
910: return null;
911: } catch (SAXException e) {
912: return null;
913: } catch (ClassCastException e) {
914: return null;
915: }
916: }
917: }
918: return null;
919: }
920:
921: private static final Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() {
922: public int compare(QName lhs, QName rhs) {
923: int r = lhs.getLocalPart().compareTo(rhs.getLocalPart());
924: if (r != 0)
925: return r;
926:
927: return lhs.getNamespaceURI().compareTo(
928: rhs.getNamespaceURI());
929: }
930: };;
931: }
|