1 /*
   2  * Copyright (c) 1997, 2013, 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.lang.reflect.Modifier;
  32 import java.util.Collection;
  33 import java.util.Collections;
  34 import java.util.List;
  35 import java.util.Map;
  36 import java.util.logging.Level;
  37 import java.util.logging.Logger;
  38 
  39 import javax.xml.bind.ValidationEvent;
  40 import javax.xml.bind.annotation.XmlRootElement;
  41 import javax.xml.bind.helpers.ValidationEventImpl;
  42 import javax.xml.namespace.QName;
  43 import javax.xml.stream.XMLStreamException;
  44 
  45 import com.sun.istack.internal.FinalArrayList;
  46 import com.sun.xml.internal.bind.Util;
  47 import com.sun.xml.internal.bind.api.AccessorException;
  48 import com.sun.xml.internal.bind.v2.ClassFactory;
  49 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
  50 import com.sun.xml.internal.bind.v2.model.core.ID;
  51 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo;
  52 import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo;
  53 import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty;
  54 import com.sun.xml.internal.bind.v2.runtime.property.Property;
  55 import com.sun.xml.internal.bind.v2.runtime.property.PropertyFactory;
  56 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
  57 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
  58 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.StructureLoader;
  59 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
  60 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiTypeLoader;
  61 
  62 import org.xml.sax.Locator;
  63 import org.xml.sax.SAXException;
  64 import org.xml.sax.helpers.LocatorImpl;
  65 
  66 /**
  67  * {@link JaxBeanInfo} implementation for j2s bean.
  68  *
  69  * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
  70  */
  71 public final class ClassBeanInfoImpl<BeanT> extends JaxBeanInfo<BeanT> implements AttributeAccessor<BeanT> {
  72 
  73     /**
  74      * Properties of this bean class but not its ancestor classes.
  75      */
  76     public final Property<BeanT>[] properties;
  77 
  78     /**
  79      * Non-null if this bean has an ID property.
  80      */
  81     private Property<? super BeanT> idProperty;
  82 
  83     /**
  84      * Immutable configured loader for this class.
  85      *
  86      * <p>
  87      * Set from the link method, but considered final.
  88      */
  89     private Loader loader;
  90     private Loader loaderWithTypeSubst;
  91 
  92     /**
  93      * Set only until the link phase to avoid leaking memory.
  94      */
  95     private RuntimeClassInfo ci;
  96 
  97     private final Accessor<? super BeanT,Map<QName,String>> inheritedAttWildcard;
  98     private final Transducer<BeanT> xducer;
  99 
 100     /**
 101      * {@link ClassBeanInfoImpl} that represents the super class of {@link #jaxbType}.
 102      */
 103     public final ClassBeanInfoImpl<? super BeanT> superClazz;
 104 
 105     private final Accessor<? super BeanT,Locator> xmlLocatorField;
 106 
 107     private final Name tagName;
 108 
 109     private boolean retainPropertyInfo = false;
 110 
 111     /**
 112      * The {@link AttributeProperty}s for this type and all its ancestors.
 113      * If {@link JAXBContextImpl#c14nSupport} is true, this is sorted alphabetically.
 114      */
 115     private /*final*/ AttributeProperty<BeanT>[] attributeProperties;
 116 
 117     /**
 118      * {@link Property}s that need to receive {@link Property#serializeURIs(Object, XMLSerializer)} callback.
 119      */
 120     private /*final*/ Property<BeanT>[] uriProperties;
 121 
 122     private final Method factoryMethod;
 123 
 124     /*package*/ ClassBeanInfoImpl(JAXBContextImpl owner, RuntimeClassInfo ci) {
 125         super(owner,ci,ci.getClazz(),ci.getTypeName(),ci.isElement(),false,true);
 126 
 127         this.ci = ci;
 128         this.inheritedAttWildcard = ci.getAttributeWildcard();
 129         this.xducer = ci.getTransducer();
 130         this.factoryMethod = ci.getFactoryMethod();
 131         this.retainPropertyInfo = owner.retainPropertyInfo;
 132 
 133         // make the factory accessible
 134         if(factoryMethod!=null) {
 135             int classMod = factoryMethod.getDeclaringClass().getModifiers();
 136 
 137             if(!Modifier.isPublic(classMod) || !Modifier.isPublic(factoryMethod.getModifiers())) {
 138                 // attempt to make it work even if the constructor is not accessible
 139                 try {
 140                     factoryMethod.setAccessible(true);
 141                 } catch(SecurityException e) {
 142                     // but if we don't have a permission to do so, work gracefully.
 143                     logger.log(Level.FINE,"Unable to make the method of "+factoryMethod+" accessible",e);
 144                     throw e;
 145                 }
 146             }
 147         }
 148 
 149 
 150         if(ci.getBaseClass()==null)
 151             this.superClazz = null;
 152         else
 153             this.superClazz = owner.getOrCreate(ci.getBaseClass());
 154 
 155         if(superClazz!=null && superClazz.xmlLocatorField!=null)
 156             xmlLocatorField = superClazz.xmlLocatorField;
 157         else
 158             xmlLocatorField = ci.getLocatorField();
 159 
 160         // create property objects
 161         Collection<? extends RuntimePropertyInfo> ps = ci.getProperties();
 162         this.properties = new Property[ps.size()];
 163         int idx=0;
 164         boolean elementOnly = true;
 165         for( RuntimePropertyInfo info : ps ) {
 166             Property p = PropertyFactory.create(owner,info);
 167             if(info.id()==ID.ID)
 168                 idProperty = p;
 169             properties[idx++] = p;
 170             elementOnly &= info.elementOnlyContent();
 171             checkOverrideProperties(p);
 172         }
 173         // super class' idProperty might not be computed at this point,
 174         // so check that later
 175 
 176         hasElementOnlyContentModel( elementOnly );
 177         // again update this value later when we know that of the super class
 178 
 179         if(ci.isElement())
 180             tagName = owner.nameBuilder.createElementName(ci.getElementName());
 181         else
 182             tagName = null;
 183 
 184         setLifecycleFlags();
 185     }
 186 
 187     private void checkOverrideProperties(Property p) {
 188         ClassBeanInfoImpl bi = this;
 189         while ((bi = bi.superClazz) != null) {
 190             Property[] props = bi.properties;
 191             if (props == null) break;
 192             for (Property superProperty : props) {
 193                 if (superProperty != null) {
 194                     String spName = superProperty.getFieldName();
 195                     if ((spName != null) && (spName.equals(p.getFieldName()))) {
 196                         superProperty.setHiddenByOverride(true);
 197                     }
 198                 }
 199             }
 200         }
 201     }
 202 
 203     @Override
 204     protected void link(JAXBContextImpl grammar) {
 205         if(uriProperties!=null)
 206             return; // avoid linking twice
 207 
 208         super.link(grammar);
 209 
 210         if(superClazz!=null)
 211             superClazz.link(grammar);
 212 
 213         getLoader(grammar,true);    // make sure to build the loader if we haven't done so.
 214 
 215         // propagate values from super class
 216         if(superClazz!=null) {
 217             if(idProperty==null)
 218                 idProperty = superClazz.idProperty;
 219 
 220             if(!superClazz.hasElementOnlyContentModel())
 221                 hasElementOnlyContentModel(false);
 222         }
 223 
 224         // create a list of attribute/URI handlers
 225         List<AttributeProperty> attProps = new FinalArrayList<AttributeProperty>();
 226         List<Property> uriProps = new FinalArrayList<Property>();
 227         for (ClassBeanInfoImpl bi = this; bi != null; bi = bi.superClazz) {
 228             for (int i = 0; i < bi.properties.length; i++) {
 229                 Property p = bi.properties[i];
 230                 if(p instanceof AttributeProperty)
 231                     attProps.add((AttributeProperty) p);
 232                 if(p.hasSerializeURIAction())
 233                     uriProps.add(p);
 234             }
 235         }
 236         if(grammar.c14nSupport)
 237             Collections.sort(attProps);
 238 
 239         if(attProps.isEmpty())
 240             attributeProperties = EMPTY_PROPERTIES;
 241         else
 242             attributeProperties = attProps.toArray(new AttributeProperty[attProps.size()]);
 243 
 244         if(uriProps.isEmpty())
 245             uriProperties = EMPTY_PROPERTIES;
 246         else
 247             uriProperties = uriProps.toArray(new Property[uriProps.size()]);
 248     }
 249 
 250     @Override
 251     public void wrapUp() {
 252         for (Property p : properties)
 253             p.wrapUp();
 254         ci = null;
 255         super.wrapUp();
 256     }
 257 
 258     public String getElementNamespaceURI(BeanT bean) {
 259         return tagName.nsUri;
 260     }
 261 
 262     public String getElementLocalName(BeanT bean) {
 263         return tagName.localName;
 264     }
 265 
 266     public BeanT createInstance(UnmarshallingContext context) throws IllegalAccessException, InvocationTargetException, InstantiationException, SAXException {
 267 
 268         BeanT bean = null;
 269         if (factoryMethod == null){
 270            bean = ClassFactory.create0(jaxbType);
 271         }else {
 272             Object o = ClassFactory.create(factoryMethod);
 273             if( jaxbType.isInstance(o) ){
 274                 bean = (BeanT)o;
 275             } else {
 276                 throw new InstantiationException("The factory method didn't return a correct object");
 277             }
 278         }
 279 
 280         if(xmlLocatorField!=null)
 281             // need to copy because Locator is mutable
 282             try {
 283                 xmlLocatorField.set(bean,new LocatorImpl(context.getLocator()));
 284             } catch (AccessorException e) {
 285                 context.handleError(e);
 286             }
 287         return bean;
 288     }
 289 
 290     public boolean reset(BeanT bean, UnmarshallingContext context) throws SAXException {
 291         try {
 292             if(superClazz!=null)
 293                 superClazz.reset(bean,context);
 294             for( Property<BeanT> p : properties )
 295                 p.reset(bean);
 296             return true;
 297         } catch (AccessorException e) {
 298             context.handleError(e);
 299             return false;
 300         }
 301     }
 302 
 303     public String getId(BeanT bean, XMLSerializer target) throws SAXException {
 304         if(idProperty!=null) {
 305             try {
 306                 return idProperty.getIdValue(bean);
 307             } catch (AccessorException e) {
 308                 target.reportError(null,e);
 309             }
 310         }
 311         return null;
 312     }
 313 
 314     public void serializeRoot(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException {
 315         if(tagName==null) {
 316             Class beanClass = bean.getClass();
 317             String message;
 318             if (beanClass.isAnnotationPresent(XmlRootElement.class)) {
 319                 message = Messages.UNABLE_TO_MARSHAL_UNBOUND_CLASS.format(beanClass.getName());
 320             } else {
 321                 message = Messages.UNABLE_TO_MARSHAL_NON_ELEMENT.format(beanClass.getName());
 322             }
 323             target.reportError(new ValidationEventImpl(ValidationEvent.ERROR,message,null, null));
 324         } else {
 325             target.startElement(tagName,bean);
 326             target.childAsSoleContent(bean,null);
 327             target.endElement();
 328             if (retainPropertyInfo) {
 329                 target.currentProperty.remove();
 330             }
 331         }
 332     }
 333 
 334     public void serializeBody(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException {
 335         if (superClazz != null) {
 336             superClazz.serializeBody(bean, target);
 337         }
 338         try {
 339             for (Property<BeanT> p : properties) {
 340                 if (retainPropertyInfo) {
 341                     target.currentProperty.set(p);
 342                 }
 343                 boolean isThereAnOverridingProperty = p.isHiddenByOverride();
 344                 if (!isThereAnOverridingProperty || bean.getClass().equals(jaxbType)) {
 345                     p.serializeBody(bean, target, null);
 346                 } else if (isThereAnOverridingProperty) {
 347                     // need to double check the override - it should be safe to do after the model has been created because it's targeted to override properties only
 348                     Class beanClass = bean.getClass();
 349                     if (Utils.REFLECTION_NAVIGATOR.getDeclaredField(beanClass, p.getFieldName()) == null) {
 350                         p.serializeBody(bean, target, null);
 351                     }
 352                 }
 353             }
 354         } catch (AccessorException e) {
 355             target.reportError(null, e);
 356         }
 357     }
 358 
 359     public void serializeAttributes(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException {
 360         for( AttributeProperty<BeanT> p : attributeProperties )
 361             try {
 362                 if (retainPropertyInfo) {
 363                 final Property parentProperty = target.getCurrentProperty();
 364                 target.currentProperty.set(p);
 365                 p.serializeAttributes(bean,target);
 366                 target.currentProperty.set(parentProperty);
 367                 } else {
 368                     p.serializeAttributes(bean,target);
 369                 }
 370                 if (p.attName.equals(WellKnownNamespace.XML_SCHEMA_INSTANCE, "nil")) {
 371                     isNilIncluded = true;
 372                 }
 373             } catch (AccessorException e) {
 374                 target.reportError(null,e);
 375             }
 376 
 377         try {
 378             if(inheritedAttWildcard!=null) {
 379                 Map<QName,String> map = inheritedAttWildcard.get(bean);
 380                 target.attWildcardAsAttributes(map,null);
 381             }
 382         } catch (AccessorException e) {
 383             target.reportError(null,e);
 384         }
 385     }
 386 
 387     public void serializeURIs(BeanT bean, XMLSerializer target) throws SAXException {
 388         try {
 389             if (retainPropertyInfo) {
 390             final Property parentProperty = target.getCurrentProperty();
 391             for( Property<BeanT> p : uriProperties ) {
 392                 target.currentProperty.set(p);
 393                 p.serializeURIs(bean,target);
 394             }
 395             target.currentProperty.set(parentProperty);
 396             } else {
 397                 for( Property<BeanT> p : uriProperties ) {
 398                     p.serializeURIs(bean,target);
 399                 }
 400             }
 401             if(inheritedAttWildcard!=null) {
 402                 Map<QName,String> map = inheritedAttWildcard.get(bean);
 403                 target.attWildcardAsURIs(map,null);
 404             }
 405         } catch (AccessorException e) {
 406             target.reportError(null,e);
 407         }
 408     }
 409 
 410     public Loader getLoader(JAXBContextImpl context, boolean typeSubstitutionCapable) {
 411         if(loader==null) {
 412             // these variables have to be set before they are initialized,
 413             // because the initialization may build other loaders and they may refer to this.
 414             StructureLoader sl = new StructureLoader(this);
 415             loader = sl;
 416             if(ci.hasSubClasses())
 417                 loaderWithTypeSubst = new XsiTypeLoader(this);
 418             else
 419                 // optimization. we know there can be no @xsi:type
 420                 loaderWithTypeSubst = loader;
 421 
 422 
 423             sl.init(context,this,ci.getAttributeWildcard());
 424         }
 425         if(typeSubstitutionCapable)
 426             return loaderWithTypeSubst;
 427         else
 428             return loader;
 429     }
 430 
 431     public Transducer<BeanT> getTransducer() {
 432         return xducer;
 433     }
 434 
 435     private static final AttributeProperty[] EMPTY_PROPERTIES = new AttributeProperty[0];
 436 
 437     private static final Logger logger = Util.getClassLogger();
 438 
 439 }