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

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