/*
 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.xml.internal.bind.v2.runtime;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.bind.ValidationEvent;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.helpers.ValidationEventImpl;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;

import com.sun.istack.internal.FinalArrayList;
import com.sun.xml.internal.bind.Util;
import com.sun.xml.internal.bind.api.AccessorException;
import com.sun.xml.internal.bind.v2.ClassFactory;
import com.sun.xml.internal.bind.v2.WellKnownNamespace;
import com.sun.xml.internal.bind.v2.model.core.ID;
import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo;
import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo;
import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty;
import com.sun.xml.internal.bind.v2.runtime.property.Property;
import com.sun.xml.internal.bind.v2.runtime.property.PropertyFactory;
import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
import com.sun.xml.internal.bind.v2.runtime.unmarshaller.StructureLoader;
import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiTypeLoader;

import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.LocatorImpl;

JaxBeanInfo implementation for j2s bean.
Author:Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
/** * {@link JaxBeanInfo} implementation for j2s bean. * * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) */
public final class ClassBeanInfoImpl<BeanT> extends JaxBeanInfo<BeanT> implements AttributeAccessor<BeanT> {
Properties of this bean class but not its ancestor classes.
/** * Properties of this bean class but not its ancestor classes. */
public final Property<BeanT>[] properties;
Non-null if this bean has an ID property.
/** * Non-null if this bean has an ID property. */
private Property<? super BeanT> idProperty;
Immutable configured loader for this class.

Set from the link method, but considered final.

/** * Immutable configured loader for this class. * * <p> * Set from the link method, but considered final. */
private Loader loader; private Loader loaderWithTypeSubst;
Set only until the link phase to avoid leaking memory.
/** * Set only until the link phase to avoid leaking memory. */
private RuntimeClassInfo ci; private final Accessor<? super BeanT,Map<QName,String>> inheritedAttWildcard; private final Transducer<BeanT> xducer;
ClassBeanInfoImpl that represents the super class of JaxBeanInfo<BeanT>.jaxbType.
/** * {@link ClassBeanInfoImpl} that represents the super class of {@link #jaxbType}. */
public final ClassBeanInfoImpl<? super BeanT> superClazz; private final Accessor<? super BeanT,Locator> xmlLocatorField; private final Name tagName; private boolean retainPropertyInfo = false;
The AttributePropertys for this type and all its ancestors. If JAXBContextImpl.c14nSupport is true, this is sorted alphabetically.
/** * The {@link AttributeProperty}s for this type and all its ancestors. * If {@link JAXBContextImpl#c14nSupport} is true, this is sorted alphabetically. */
private /*final*/ AttributeProperty<BeanT>[] attributeProperties; /** * {@link Property}s that need to receive {@link Property#serializeURIs(Object, XMLSerializer)} callback. */ private /*final*/ Property<BeanT>[] uriProperties; private final Method factoryMethod; /*package*/ ClassBeanInfoImpl(JAXBContextImpl owner, RuntimeClassInfo ci) { super(owner,ci,ci.getClazz(),ci.getTypeName(),ci.isElement(),false,true); this.ci = ci; this.inheritedAttWildcard = ci.getAttributeWildcard(); this.xducer = ci.getTransducer(); this.factoryMethod = ci.getFactoryMethod(); this.retainPropertyInfo = owner.retainPropertyInfo; // make the factory accessible if(factoryMethod!=null) { int classMod = factoryMethod.getDeclaringClass().getModifiers(); if(!Modifier.isPublic(classMod) || !Modifier.isPublic(factoryMethod.getModifiers())) { // attempt to make it work even if the constructor is not accessible try { factoryMethod.setAccessible(true); } catch(SecurityException e) { // but if we don't have a permission to do so, work gracefully. logger.log(Level.FINE,"Unable to make the method of "+factoryMethod+" accessible",e); throw e; } } } if(ci.getBaseClass()==null) this.superClazz = null; else this.superClazz = owner.getOrCreate(ci.getBaseClass()); if(superClazz!=null && superClazz.xmlLocatorField!=null) xmlLocatorField = superClazz.xmlLocatorField; else xmlLocatorField = ci.getLocatorField(); // create property objects Collection<? extends RuntimePropertyInfo> ps = ci.getProperties(); this.properties = new Property[ps.size()]; int idx=0; boolean elementOnly = true; for( RuntimePropertyInfo info : ps ) { Property p = PropertyFactory.create(owner,info); if(info.id()==ID.ID) idProperty = p; properties[idx++] = p; elementOnly &= info.elementOnlyContent(); checkOverrideProperties(p); } // super class' idProperty might not be computed at this point, // so check that later hasElementOnlyContentModel( elementOnly ); // again update this value later when we know that of the super class if(ci.isElement()) tagName = owner.nameBuilder.createElementName(ci.getElementName()); else tagName = null; setLifecycleFlags(); } private void checkOverrideProperties(Property p) { ClassBeanInfoImpl bi = this; while ((bi = bi.superClazz) != null) { Property[] props = bi.properties; if (props == null) break; for (Property superProperty : props) { if (superProperty != null) { String spName = superProperty.getFieldName(); if ((spName != null) && (spName.equals(p.getFieldName()))) { superProperty.setHiddenByOverride(true); } } } } } @Override protected void link(JAXBContextImpl grammar) { if(uriProperties!=null) return; // avoid linking twice super.link(grammar); if(superClazz!=null) superClazz.link(grammar); getLoader(grammar,true); // make sure to build the loader if we haven't done so. // propagate values from super class if(superClazz!=null) { if(idProperty==null) idProperty = superClazz.idProperty; if(!superClazz.hasElementOnlyContentModel()) hasElementOnlyContentModel(false); } // create a list of attribute/URI handlers List<AttributeProperty> attProps = new FinalArrayList<AttributeProperty>(); List<Property> uriProps = new FinalArrayList<Property>(); for (ClassBeanInfoImpl bi = this; bi != null; bi = bi.superClazz) { for (int i = 0; i < bi.properties.length; i++) { Property p = bi.properties[i]; if(p instanceof AttributeProperty) attProps.add((AttributeProperty) p); if(p.hasSerializeURIAction()) uriProps.add(p); } } if(grammar.c14nSupport) Collections.sort(attProps); if(attProps.isEmpty()) attributeProperties = EMPTY_PROPERTIES; else attributeProperties = attProps.toArray(new AttributeProperty[attProps.size()]); if(uriProps.isEmpty()) uriProperties = EMPTY_PROPERTIES; else uriProperties = uriProps.toArray(new Property[uriProps.size()]); } @Override public void wrapUp() { for (Property p : properties) p.wrapUp(); ci = null; super.wrapUp(); } public String getElementNamespaceURI(BeanT bean) { return tagName.nsUri; } public String getElementLocalName(BeanT bean) { return tagName.localName; } public BeanT createInstance(UnmarshallingContext context) throws IllegalAccessException, InvocationTargetException, InstantiationException, SAXException { BeanT bean = null; if (factoryMethod == null){ bean = ClassFactory.create0(jaxbType); }else { Object o = ClassFactory.create(factoryMethod); if( jaxbType.isInstance(o) ){ bean = (BeanT)o; } else { throw new InstantiationException("The factory method didn't return a correct object"); } } if(xmlLocatorField!=null) // need to copy because Locator is mutable try { xmlLocatorField.set(bean,new LocatorImpl(context.getLocator())); } catch (AccessorException e) { context.handleError(e); } return bean; } public boolean reset(BeanT bean, UnmarshallingContext context) throws SAXException { try { if(superClazz!=null) superClazz.reset(bean,context); for( Property<BeanT> p : properties ) p.reset(bean); return true; } catch (AccessorException e) { context.handleError(e); return false; } } public String getId(BeanT bean, XMLSerializer target) throws SAXException { if(idProperty!=null) { try { return idProperty.getIdValue(bean); } catch (AccessorException e) { target.reportError(null,e); } } return null; } public void serializeRoot(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException { if(tagName==null) { Class beanClass = bean.getClass(); String message; if (beanClass.isAnnotationPresent(XmlRootElement.class)) { message = Messages.UNABLE_TO_MARSHAL_UNBOUND_CLASS.format(beanClass.getName()); } else { message = Messages.UNABLE_TO_MARSHAL_NON_ELEMENT.format(beanClass.getName()); } target.reportError(new ValidationEventImpl(ValidationEvent.ERROR,message,null, null)); } else { target.startElement(tagName,bean); target.childAsSoleContent(bean,null); target.endElement(); if (retainPropertyInfo) { target.currentProperty.remove(); } } } public void serializeBody(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException { if (superClazz != null) { superClazz.serializeBody(bean, target); } try { for (Property<BeanT> p : properties) { if (retainPropertyInfo) { target.currentProperty.set(p); } boolean isThereAnOverridingProperty = p.isHiddenByOverride(); if (!isThereAnOverridingProperty || bean.getClass().equals(jaxbType)) { p.serializeBody(bean, target, null); } else if (isThereAnOverridingProperty) { // 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 Class beanClass = bean.getClass(); if (Utils.REFLECTION_NAVIGATOR.getDeclaredField(beanClass, p.getFieldName()) == null) { p.serializeBody(bean, target, null); } } } } catch (AccessorException e) { target.reportError(null, e); } } public void serializeAttributes(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException { for( AttributeProperty<BeanT> p : attributeProperties ) try { if (retainPropertyInfo) { final Property parentProperty = target.getCurrentProperty(); target.currentProperty.set(p); p.serializeAttributes(bean,target); target.currentProperty.set(parentProperty); } else { p.serializeAttributes(bean,target); } if (p.attName.equals(WellKnownNamespace.XML_SCHEMA_INSTANCE, "nil")) { isNilIncluded = true; } } catch (AccessorException e) { target.reportError(null,e); } try { if(inheritedAttWildcard!=null) { Map<QName,String> map = inheritedAttWildcard.get(bean); target.attWildcardAsAttributes(map,null); } } catch (AccessorException e) { target.reportError(null,e); } } public void serializeURIs(BeanT bean, XMLSerializer target) throws SAXException { try { if (retainPropertyInfo) { final Property parentProperty = target.getCurrentProperty(); for( Property<BeanT> p : uriProperties ) { target.currentProperty.set(p); p.serializeURIs(bean,target); } target.currentProperty.set(parentProperty); } else { for( Property<BeanT> p : uriProperties ) { p.serializeURIs(bean,target); } } if(inheritedAttWildcard!=null) { Map<QName,String> map = inheritedAttWildcard.get(bean); target.attWildcardAsURIs(map,null); } } catch (AccessorException e) { target.reportError(null,e); } } public Loader getLoader(JAXBContextImpl context, boolean typeSubstitutionCapable) { if(loader==null) { // these variables have to be set before they are initialized, // because the initialization may build other loaders and they may refer to this. StructureLoader sl = new StructureLoader(this); loader = sl; if(ci.hasSubClasses()) loaderWithTypeSubst = new XsiTypeLoader(this); else // optimization. we know there can be no @xsi:type loaderWithTypeSubst = loader; sl.init(context,this,ci.getAttributeWildcard()); } if(typeSubstitutionCapable) return loaderWithTypeSubst; else return loader; } public Transducer<BeanT> getTransducer() { return xducer; } private static final AttributeProperty[] EMPTY_PROPERTIES = new AttributeProperty[0]; private static final Logger logger = Util.getClassLogger(); }