ohair@286: /* alanb@368: * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.bind.v2.runtime; ohair@286: ohair@286: import java.io.IOException; ohair@286: import java.lang.reflect.InvocationTargetException; ohair@286: import java.lang.reflect.Method; ohair@286: import java.lang.reflect.Modifier; ohair@286: import java.util.Collection; ohair@286: import java.util.Collections; ohair@286: import java.util.List; ohair@286: import java.util.Map; ohair@286: import java.util.logging.Level; ohair@286: import java.util.logging.Logger; ohair@286: ohair@286: import javax.xml.bind.ValidationEvent; ohair@286: import javax.xml.bind.annotation.XmlRootElement; ohair@286: import javax.xml.bind.helpers.ValidationEventImpl; ohair@286: import javax.xml.namespace.QName; ohair@286: import javax.xml.stream.XMLStreamException; ohair@286: ohair@286: import com.sun.istack.internal.FinalArrayList; ohair@286: import com.sun.xml.internal.bind.Util; ohair@286: import com.sun.xml.internal.bind.api.AccessorException; ohair@286: import com.sun.xml.internal.bind.v2.ClassFactory; ohair@286: import com.sun.xml.internal.bind.v2.WellKnownNamespace; ohair@286: import com.sun.xml.internal.bind.v2.model.core.ID; ohair@286: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo; ohair@286: import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo; ohair@286: import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty; ohair@286: import com.sun.xml.internal.bind.v2.runtime.property.Property; ohair@286: import com.sun.xml.internal.bind.v2.runtime.property.PropertyFactory; ohair@286: import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor; ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader; ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.StructureLoader; ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext; ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiTypeLoader; ohair@286: ohair@286: import org.xml.sax.Locator; ohair@286: import org.xml.sax.SAXException; ohair@286: import org.xml.sax.helpers.LocatorImpl; ohair@286: ohair@286: /** ohair@286: * {@link JaxBeanInfo} implementation for j2s bean. ohair@286: * ohair@286: * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) ohair@286: */ ohair@286: public final class ClassBeanInfoImpl extends JaxBeanInfo implements AttributeAccessor { ohair@286: ohair@286: /** ohair@286: * Properties of this bean class but not its ancestor classes. ohair@286: */ ohair@286: public final Property[] properties; ohair@286: ohair@286: /** ohair@286: * Non-null if this bean has an ID property. ohair@286: */ ohair@286: private Property idProperty; ohair@286: ohair@286: /** ohair@286: * Immutable configured loader for this class. ohair@286: * ohair@286: *

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