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