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.reflect.InvocationTargetException;
030: import java.lang.reflect.Method;
031: import java.util.Arrays;
032: import java.util.Collection;
033: import java.util.Collections;
034: import java.util.logging.Level;
035: import java.util.logging.Logger;
036:
037: import javax.xml.bind.JAXBContext;
038: import javax.xml.bind.Marshaller;
039: import javax.xml.bind.Unmarshaller;
040: import javax.xml.datatype.XMLGregorianCalendar;
041: import javax.xml.namespace.QName;
042: import javax.xml.stream.XMLStreamException;
043:
044: import com.sun.istack.internal.NotNull;
045: import com.sun.xml.internal.bind.Util;
046: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfo;
047: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
048: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl;
049: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
050:
051: import org.xml.sax.SAXException;
052:
053: /**
054: * Encapsulates various JAXB operations on objects bound by JAXB.
055: * Immutable and thread-safe.
056: *
057: * <p>
058: * Each JAXB-bound class has a corresponding {@link JaxBeanInfo} object,
059: * which performs all the JAXB related operations on behalf of
060: * the JAXB-bound object.
061: *
062: * <p>
063: * Given a class, the corresponding {@link JaxBeanInfo} can be located
064: * via {@link JAXBContextImpl#getBeanInfo(Class,boolean)}.
065: *
066: * <p>
067: * Typically, {@link JaxBeanInfo} implementations should be generated
068: * by XJC/JXC. Those impl classes will register themselves to their
069: * master <tt>ObjectFactory</tt> class.
070: *
071: * <p>
072: * The type parameter BeanT is the Java class of the bean that this represents.
073: *
074: * @author
075: * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
076: */
077: public abstract class JaxBeanInfo<BeanT> {
078:
079: /**
080: * For {@link JaxBeanInfo} that has multiple type names.
081: */
082: protected JaxBeanInfo(JAXBContextImpl grammar, RuntimeTypeInfo rti,
083: Class<BeanT> jaxbType, QName[] typeNames,
084: boolean isElement, boolean isImmutable,
085: boolean hasLifecycleEvents) {
086: this (grammar, rti, jaxbType, (Object) typeNames, isElement,
087: isImmutable, hasLifecycleEvents);
088: }
089:
090: /**
091: * For {@link JaxBeanInfo} that has one type name.
092: */
093: protected JaxBeanInfo(JAXBContextImpl grammar, RuntimeTypeInfo rti,
094: Class<BeanT> jaxbType, QName typeName, boolean isElement,
095: boolean isImmutable, boolean hasLifecycleEvents) {
096: this (grammar, rti, jaxbType, (Object) typeName, isElement,
097: isImmutable, hasLifecycleEvents);
098: }
099:
100: /**
101: * For {@link JaxBeanInfo} that has no type names.
102: */
103: protected JaxBeanInfo(JAXBContextImpl grammar, RuntimeTypeInfo rti,
104: Class<BeanT> jaxbType, boolean isElement,
105: boolean isImmutable, boolean hasLifecycleEvents) {
106: this (grammar, rti, jaxbType, (Object) null, isElement,
107: isImmutable, hasLifecycleEvents);
108: }
109:
110: private JaxBeanInfo(JAXBContextImpl grammar, RuntimeTypeInfo rti,
111: Class<BeanT> jaxbType, Object typeName, boolean isElement,
112: boolean isImmutable, boolean hasLifecycleEvents) {
113: grammar.beanInfos.put(rti, this );
114:
115: this .jaxbType = jaxbType;
116: this .typeName = typeName;
117: this .flag = (short) ((isElement ? FLAG_IS_ELEMENT : 0)
118: | (isImmutable ? FLAG_IS_IMMUTABLE : 0) | (hasLifecycleEvents ? FLAG_HAS_LIFECYCLE_EVENTS
119: : 0));
120: }
121:
122: /**
123: * Various boolean flags combined into one field to improve memory footprint.
124: */
125: protected short flag;
126:
127: private static final short FLAG_IS_ELEMENT = 1;
128: private static final short FLAG_IS_IMMUTABLE = 2;
129: private static final short FLAG_HAS_ELEMENT_ONLY_CONTENTMODEL = 4;
130: private static final short FLAG_HAS_BEFORE_UNMARSHAL_METHOD = 8;
131: private static final short FLAG_HAS_AFTER_UNMARSHAL_METHOD = 16;
132: private static final short FLAG_HAS_BEFORE_MARSHAL_METHOD = 32;
133: private static final short FLAG_HAS_AFTER_MARSHAL_METHOD = 64;
134: private static final short FLAG_HAS_LIFECYCLE_EVENTS = 128;
135:
136: /** cache of lifecycle methods */
137: private LifecycleMethods lcm = null;
138:
139: /**
140: * True if {@link #jaxbType} has the lifecycle method.
141: */
142: public final boolean hasBeforeUnmarshalMethod() {
143: return (flag & FLAG_HAS_BEFORE_UNMARSHAL_METHOD) != 0;
144: }
145:
146: /**
147: * True if {@link #jaxbType} has the lifecycle method.
148: */
149: public final boolean hasAfterUnmarshalMethod() {
150: return (flag & FLAG_HAS_AFTER_UNMARSHAL_METHOD) != 0;
151: }
152:
153: /**
154: * True if {@link #jaxbType} has the lifecycle method.
155: */
156: public final boolean hasBeforeMarshalMethod() {
157: return (flag & FLAG_HAS_BEFORE_MARSHAL_METHOD) != 0;
158: }
159:
160: /**
161: * True if {@link #jaxbType} has the lifecycle method.
162: */
163: public final boolean hasAfterMarshalMethod() {
164: return (flag & FLAG_HAS_AFTER_MARSHAL_METHOD) != 0;
165: }
166:
167: /**
168: * Gets the JAXB bound class type that this {@link JaxBeanInfo}
169: * handles.
170: *
171: * <p>
172: * IOW, when a bean info object is requested for T,
173: * sometimes the bean info for one of its base classes might be
174: * returned.
175: */
176: public final Class<BeanT> jaxbType;
177:
178: /**
179: * Returns true if the bean is mapped to/from an XML element.
180: *
181: * <p>
182: * When this method returns true, {@link #getElementNamespaceURI(Object)}
183: * and {@link #getElementLocalName(Object)} returns the element name of
184: * the bean.
185: */
186: public final boolean isElement() {
187: return (flag & FLAG_IS_ELEMENT) != 0;
188: }
189:
190: /**
191: * Returns true if the bean is immutable.
192: *
193: * <p>
194: * If this is true, Binder won't try to ueuse this object, and the unmarshaller
195: * won't create a new instance of it before it starts.
196: */
197: public final boolean isImmutable() {
198: return (flag & FLAG_IS_IMMUTABLE) != 0;
199: }
200:
201: /**
202: * True if this bean has an element-only content model.
203: * <p>
204: * If this flag is true, the unmarshaller can work
205: * faster by ignoring whitespaces more efficiently.
206: */
207: public final boolean hasElementOnlyContentModel() {
208: return (flag & FLAG_HAS_ELEMENT_ONLY_CONTENTMODEL) != 0;
209: }
210:
211: /**
212: * True if this bean has an element-only content model.
213: * <p>
214: * Should be considered immutable, though I can't mark it final
215: * because it cannot be computed in this constructor.
216: */
217: protected final void hasElementOnlyContentModel(boolean value) {
218: if (value)
219: flag |= FLAG_HAS_ELEMENT_ONLY_CONTENTMODEL;
220: else
221: flag &= ~FLAG_HAS_ELEMENT_ONLY_CONTENTMODEL;
222: }
223:
224: /**
225: * This method is used to determine which of the sub-classes should be
226: * interrogated for the existence of lifecycle methods.
227: *
228: * @return true if the un|marshaller should look for lifecycle methods
229: * on this beanInfo, false otherwise.
230: */
231: public boolean lookForLifecycleMethods() {
232: return (flag & FLAG_HAS_LIFECYCLE_EVENTS) != 0;
233: }
234:
235: /**
236: * Returns the namespace URI portion of the element name,
237: * if the bean that this class represents is mapped from/to
238: * an XML element.
239: *
240: * @throws UnsupportedOperationException
241: * if {@link #isElement} is false.
242: */
243: public abstract String getElementNamespaceURI(BeanT o);
244:
245: /**
246: * Returns the local name portion of the element name,
247: * if the bean that this class represents is mapped from/to
248: * an XML element.
249: *
250: * @throws UnsupportedOperationException
251: * if {@link #isElement} is false.
252: */
253: public abstract String getElementLocalName(BeanT o);
254:
255: /**
256: * Type names associated with this {@link JaxBeanInfo}.
257: *
258: * @see #getTypeNames()
259: */
260: private final Object typeName; // either null, QName, or QName[]. save memory since most of them have just one.
261:
262: /**
263: * Returns XML Schema type names if the bean is mapped from
264: * a complex/simple type of XML Schema.
265: *
266: * <p>
267: * This is an ugly necessity to correctly handle
268: * the type substitution semantics of XML Schema.
269: *
270: * <p>
271: * A single Java class maybe mapped to more than one
272: * XML types. All the types listed here are recognized
273: * when we are unmarshalling XML.
274: *
275: * <p>
276: * null if the class is not bound to a named schema type.
277: *
278: * <p>
279: */
280: public Collection<QName> getTypeNames() {
281: if (typeName == null)
282: return Collections.emptyList();
283: if (typeName instanceof QName)
284: return Collections.singletonList((QName) typeName);
285: return Arrays.asList((QName[]) typeName);
286: }
287:
288: /**
289: * Returns the XML type name to be used to marshal the specified instance.
290: *
291: * <P>
292: * Most of the times the type can be determined regardless of the actual
293: * instance, but there's a few exceptions (most notably {@link XMLGregorianCalendar}),
294: * so as a general rule we need an instance to determine it.
295: */
296: public QName getTypeName(@NotNull
297: BeanT instance) {
298: if (typeName == null)
299: return null;
300: if (typeName instanceof QName)
301: return (QName) typeName;
302: return ((QName[]) typeName)[0];
303: }
304:
305: /**
306: * Creates a new instance of the bean.
307: *
308: * <p>
309: * This operation is only supported when {@link #isImmutable} is false.
310: *
311: * @param context
312: * Sometimes the created bean remembers the corresponding source location,
313: */
314: public abstract BeanT createInstance(UnmarshallingContext context)
315: throws IllegalAccessException, InvocationTargetException,
316: InstantiationException, SAXException;
317:
318: /**
319: * Resets the object to the initial state, as if the object
320: * is created fresh.
321: *
322: * <p>
323: * This is used to reuse an existing object for unmarshalling.
324: *
325: * @param context
326: * used for reporting any errors.
327: *
328: * @return
329: * true if the object was successfuly resetted.
330: * False if the object is not resettable, in which case the object will be
331: * discarded and new one will be created.
332: * <p>
333: * If the object is resettable but failed by an error, it should be reported to the context,
334: * then return false. If the object is not resettable to begin with, do not report an error.
335: *
336: * @throws SAXException
337: * as a result of reporting an error, the context may throw a {@link SAXException}.
338: */
339: public abstract boolean reset(BeanT o, UnmarshallingContext context)
340: throws SAXException;
341:
342: /**
343: * Gets the ID value of the given bean, if it has an ID value.
344: * Otherwise return null.
345: */
346: public abstract String getId(BeanT o, XMLSerializer target)
347: throws SAXException;
348:
349: /**
350: * Serializes child elements and texts into the specified target.
351: */
352: public abstract void serializeBody(BeanT o, XMLSerializer target)
353: throws SAXException, IOException, XMLStreamException;
354:
355: /**
356: * Serializes attributes into the specified target.
357: */
358: public abstract void serializeAttributes(BeanT o,
359: XMLSerializer target) throws SAXException, IOException,
360: XMLStreamException;
361:
362: /**
363: * Serializes the bean as the root element.
364: *
365: * <p>
366: * In the java-to-schema binding, an object might marshal in two different
367: * ways depending on whether it is used as the root of the graph or not.
368: * In the former case, an object could marshal as an element, whereas
369: * in the latter case, it marshals as a type.
370: *
371: * <p>
372: * This method is used to marshal the root of the object graph to allow
373: * this semantics to be implemented.
374: *
375: * <p>
376: * It is doubtful to me if it's a good idea for an object to marshal
377: * in two ways depending on the context.
378: *
379: * <p>
380: * For schema-to-java, this is equivalent to {@link #serializeBody(Object, XMLSerializer)}.
381: */
382: public abstract void serializeRoot(BeanT o, XMLSerializer target)
383: throws SAXException, IOException, XMLStreamException;
384:
385: /**
386: * Declares all the namespace URIs this object is using at
387: * its top-level scope into the specified target.
388: */
389: public abstract void serializeURIs(BeanT o, XMLSerializer target)
390: throws SAXException;
391:
392: /**
393: * Gets the {@link Loader} that will unmarshall the given object.
394: *
395: * @param context
396: * The {@link JAXBContextImpl} object that governs this object.
397: * This object is taken as a parameter so that {@link JaxBeanInfo} doesn't have
398: * to store them on its own.
399: *
400: * When this method is invoked from within the unmarshaller, tihs parameter can be
401: * null (because the loader is constructed already.)
402: *
403: * @param typeSubstitutionCapable
404: * If true, the returned {@link Loader} is capable of recognizing @xsi:type (if necessary)
405: * and unmarshals a subtype. This allowes an optimization where this bean info
406: * is guaranteed not to have a type substitution.
407: * If false, the returned {@link Loader} doesn't look for @xsi:type.
408: * @return
409: * must return non-null valid object
410: */
411: public abstract Loader getLoader(JAXBContextImpl context,
412: boolean typeSubstitutionCapable);
413:
414: /**
415: * If the bean's representation in XML is just a text,
416: * this method return a {@link Transducer} that lets you convert
417: * values between the text and the bean.
418: */
419: public abstract Transducer<BeanT> getTransducer();
420:
421: /**
422: * Called after all the {@link JaxBeanInfo}s are created.
423: * @param grammar
424: */
425: protected void link(JAXBContextImpl grammar) {
426: }
427:
428: /**
429: * Called at the end of the {@link JAXBContext} initialization phase
430: * to clean up any unnecessary references.
431: */
432: public void wrapUp() {
433: }
434:
435: private static final Class[] unmarshalEventParams = {
436: Unmarshaller.class, Object.class };
437: private static Class[] marshalEventParams = { Marshaller.class };
438:
439: /**
440: * use reflection to determine which of the 4 object lifecycle methods exist on
441: * the JAXB bound type.
442: */
443: protected final void setLifecycleFlags() {
444: try {
445: Method m;
446:
447: // beforeUnmarshal
448: try {
449: m = jaxbType.getDeclaredMethod("beforeUnmarshal",
450: unmarshalEventParams);
451: cacheLifecycleMethod(m,
452: FLAG_HAS_BEFORE_UNMARSHAL_METHOD);
453: } catch (NoSuchMethodException e) {
454: // no-op, look for the next method
455: }
456:
457: // afterUnmarshal
458: try {
459: m = jaxbType.getDeclaredMethod("afterUnmarshal",
460: unmarshalEventParams);
461: cacheLifecycleMethod(m, FLAG_HAS_AFTER_UNMARSHAL_METHOD);
462: } catch (NoSuchMethodException e) {
463: // no-op, look for the next method
464: }
465:
466: // beforeMarshal
467: try {
468: m = jaxbType.getDeclaredMethod("beforeMarshal",
469: marshalEventParams);
470: cacheLifecycleMethod(m, FLAG_HAS_BEFORE_MARSHAL_METHOD);
471: } catch (NoSuchMethodException e) {
472: // no-op, look for the next method
473: }
474:
475: // afterMarshal
476: try {
477: m = jaxbType.getDeclaredMethod("afterMarshal",
478: marshalEventParams);
479: cacheLifecycleMethod(m, FLAG_HAS_AFTER_MARSHAL_METHOD);
480: } catch (NoSuchMethodException e) {
481: // no-op
482: }
483: } catch (SecurityException e) {
484: // this happens when we don't have enough permission.
485: logger.log(Level.WARNING,
486: Messages.UNABLE_TO_DISCOVER_EVENTHANDLER.format(
487: jaxbType.getName(), e));
488: }
489: }
490:
491: /**
492: * Cache a reference to the specified lifecycle method for the jaxbType
493: * associated with this beanInfo.
494: *
495: * @param m Method reference
496: * @param lifecycleFlag byte representing which of the 4 lifecycle methods
497: * is being cached
498: */
499: private void cacheLifecycleMethod(Method m, short lifecycleFlag) {
500: //LifecycleMethods lcm = getLifecycleMethods();
501: if (lcm == null) {
502: lcm = new LifecycleMethods();
503: //lcmCache.put(jaxbType, lcm);
504: }
505:
506: m.setAccessible(true);
507:
508: flag |= lifecycleFlag;
509:
510: switch (lifecycleFlag) {
511: case FLAG_HAS_BEFORE_UNMARSHAL_METHOD:
512: lcm.setBeforeUnmarshal(m);
513: break;
514: case FLAG_HAS_AFTER_UNMARSHAL_METHOD:
515: lcm.setAfterUnmarshal(m);
516: break;
517: case FLAG_HAS_BEFORE_MARSHAL_METHOD:
518: lcm.setBeforeMarshal(m);
519: break;
520: case FLAG_HAS_AFTER_MARSHAL_METHOD:
521: lcm.setAfterMarshal(m);
522: break;
523: }
524: }
525:
526: /**
527: * Return the LifecycleMethods cache for this ClassBeanInfo's corresponding
528: * jaxbType if it exists, else return null.
529: *
530: */
531: public final LifecycleMethods getLifecycleMethods() {
532: return lcm;
533: }
534:
535: /**
536: * Invokes the beforeUnmarshal method if applicable.
537: */
538: public final void invokeBeforeUnmarshalMethod(UnmarshallerImpl unm,
539: Object child, Object parent) throws SAXException {
540: Method m = getLifecycleMethods().getBeforeUnmarshal();
541: invokeUnmarshallCallback(m, child, unm, parent);
542: }
543:
544: /**
545: * Invokes the afterUnmarshal method if applicable.
546: */
547: public final void invokeAfterUnmarshalMethod(UnmarshallerImpl unm,
548: Object child, Object parent) throws SAXException {
549: Method m = getLifecycleMethods().getAfterUnmarshal();
550: invokeUnmarshallCallback(m, child, unm, parent);
551: }
552:
553: private void invokeUnmarshallCallback(Method m, Object child,
554: UnmarshallerImpl unm, Object parent) throws SAXException {
555: try {
556: m.invoke(child, unm, parent);
557: } catch (IllegalAccessException e) {
558: UnmarshallingContext.getInstance().handleError(e);
559: } catch (InvocationTargetException e) {
560: UnmarshallingContext.getInstance().handleError(e);
561: }
562: }
563:
564: private static final Logger logger = Util.getClassLogger();
565: }
|