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 
  26 package com.sun.xml.internal.bind.v2.runtime;
  27 
  28 import java.io.IOException;
  29 import java.lang.reflect.InvocationTargetException;
  30 import java.lang.reflect.Method;
  31 import java.security.AccessController;
  32 import java.security.PrivilegedAction;
  33 import java.util.Arrays;
  34 import java.util.Collection;
  35 import java.util.Collections;
  36 import java.util.logging.Level;
  37 import java.util.logging.Logger;
  38 
  39 import javax.xml.bind.JAXBContext;
  40 import javax.xml.bind.Marshaller;
  41 import javax.xml.bind.Unmarshaller;
  42 import javax.xml.datatype.XMLGregorianCalendar;
  43 import javax.xml.namespace.QName;
  44 import javax.xml.stream.XMLStreamException;
  45 
  46 import com.sun.istack.internal.NotNull;
  47 import com.sun.xml.internal.bind.Util;
  48 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfo;
  49 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
  50 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl;
  51 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
  52 
  53 import 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  */
  79 public 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 }