/* * Copyright (c) 1997, 2014, 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; /** * {@link JaxBeanInfo} implementation for j2s bean. * * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) */ public final class ClassBeanInfoImpl extends JaxBeanInfo implements AttributeAccessor { /** * Properties of this bean class but not its ancestor classes. */ public final Property[] properties; /** * Non-null if this bean has an ID property. */ private Property idProperty; /** * Immutable configured loader for this class. * *

* Set from the link method, but considered final. */ private Loader loader; private Loader loaderWithTypeSubst; /** * Set only until the link phase to avoid leaking memory. */ private RuntimeClassInfo ci; private final Accessor> inheritedAttWildcard; private final Transducer xducer; /** * {@link ClassBeanInfoImpl} that represents the super class of {@link #jaxbType}. */ public final ClassBeanInfoImpl superClazz; private final Accessor xmlLocatorField; private final Name tagName; private boolean retainPropertyInfo = false; /** * The {@link AttributeProperty}s for this type and all its ancestors. * If {@link JAXBContextImpl#c14nSupport} is true, this is sorted alphabetically. */ private /*final*/ AttributeProperty[] attributeProperties; /** * {@link Property}s that need to receive {@link Property#serializeURIs(Object, XMLSerializer)} callback. */ private /*final*/ Property[] 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 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 attProps = new FinalArrayList(); List uriProps = new FinalArrayList(); 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 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 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 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 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 p : uriProperties ) { target.currentProperty.set(p); p.serializeURIs(bean,target); } target.currentProperty.set(parentProperty); } else { for( Property p : uriProperties ) { p.serializeURIs(bean,target); } } if(inheritedAttWildcard!=null) { Map 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 getTransducer() { return xducer; } private static final AttributeProperty[] EMPTY_PROPERTIES = new AttributeProperty[0]; private static final Logger logger = Util.getClassLogger(); }