aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2010, 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.model.impl; aoqi@0: aoqi@0: import java.io.IOException; aoqi@0: import java.lang.annotation.Annotation; aoqi@0: import java.lang.reflect.Field; aoqi@0: import java.lang.reflect.Method; aoqi@0: import java.lang.reflect.Modifier; aoqi@0: import java.lang.reflect.Type; aoqi@0: import java.util.List; aoqi@0: import java.util.Map; aoqi@0: aoqi@0: import javax.xml.bind.JAXBException; aoqi@0: import javax.xml.namespace.QName; aoqi@0: import javax.xml.stream.XMLStreamException; aoqi@0: aoqi@0: import com.sun.istack.internal.NotNull; aoqi@0: import com.sun.xml.internal.bind.AccessorFactory; aoqi@0: import com.sun.xml.internal.bind.AccessorFactoryImpl; aoqi@0: import com.sun.xml.internal.bind.InternalAccessorFactory; aoqi@0: import com.sun.xml.internal.bind.XmlAccessorFactory; aoqi@0: import com.sun.xml.internal.bind.annotation.XmlLocation; 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.model.annotation.Locatable; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.PropertyKind; aoqi@0: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElement; aoqi@0: import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeValuePropertyInfo; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.Location; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.Name; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.Transducer; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext; aoqi@0: aoqi@0: import org.xml.sax.Locator; aoqi@0: import org.xml.sax.SAXException; aoqi@0: aoqi@0: /** aoqi@0: * @author Kohsuke Kawaguchi (kk@kohsuke.org) aoqi@0: */ aoqi@0: class RuntimeClassInfoImpl extends ClassInfoImpl aoqi@0: implements RuntimeClassInfo, RuntimeElement { aoqi@0: aoqi@0: /** aoqi@0: * If this class has a property annotated with {@link XmlLocation}, aoqi@0: * this field will get the accessor for it. aoqi@0: * aoqi@0: * TODO: support method based XmlLocation aoqi@0: */ aoqi@0: private Accessor xmlLocationAccessor; aoqi@0: aoqi@0: private AccessorFactory accessorFactory; aoqi@0: aoqi@0: private boolean supressAccessorWarnings = false; aoqi@0: aoqi@0: public RuntimeClassInfoImpl(RuntimeModelBuilder modelBuilder, Locatable upstream, Class clazz) { aoqi@0: super(modelBuilder, upstream, clazz); aoqi@0: accessorFactory = createAccessorFactory(clazz); aoqi@0: } aoqi@0: aoqi@0: protected AccessorFactory createAccessorFactory(Class clazz) { aoqi@0: XmlAccessorFactory factoryAnn; aoqi@0: AccessorFactory accFactory = null; aoqi@0: aoqi@0: // user providing class to be used. aoqi@0: JAXBContextImpl context = ((RuntimeModelBuilder) builder).context; aoqi@0: if (context!=null) { aoqi@0: this.supressAccessorWarnings = context.supressAccessorWarnings; aoqi@0: if (context.xmlAccessorFactorySupport) { aoqi@0: factoryAnn = findXmlAccessorFactoryAnnotation(clazz); aoqi@0: if (factoryAnn != null) { aoqi@0: try { aoqi@0: accFactory = factoryAnn.value().newInstance(); aoqi@0: } catch (InstantiationException e) { aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.ACCESSORFACTORY_INSTANTIATION_EXCEPTION.format( aoqi@0: factoryAnn.getClass().getName(), nav().getClassName(clazz)), this)); aoqi@0: } catch (IllegalAccessException e) { aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.ACCESSORFACTORY_ACCESS_EXCEPTION.format( aoqi@0: factoryAnn.getClass().getName(), nav().getClassName(clazz)),this)); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: aoqi@0: // Fall back to local AccessorFactory when no aoqi@0: // user not providing one or as error recovery. aoqi@0: if (accFactory == null){ aoqi@0: accFactory = AccessorFactoryImpl.getInstance(); aoqi@0: } aoqi@0: return accFactory; aoqi@0: } aoqi@0: aoqi@0: protected XmlAccessorFactory findXmlAccessorFactoryAnnotation(Class clazz) { aoqi@0: XmlAccessorFactory factoryAnn = reader().getClassAnnotation(XmlAccessorFactory.class,clazz,this); aoqi@0: if (factoryAnn == null) { aoqi@0: factoryAnn = reader().getPackageAnnotation(XmlAccessorFactory.class,clazz,this); aoqi@0: } aoqi@0: return factoryAnn; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: public Method getFactoryMethod(){ aoqi@0: return super.getFactoryMethod(); aoqi@0: } aoqi@0: aoqi@0: public final RuntimeClassInfoImpl getBaseClass() { aoqi@0: return (RuntimeClassInfoImpl)super.getBaseClass(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected ReferencePropertyInfoImpl createReferenceProperty(PropertySeed seed) { aoqi@0: return new RuntimeReferencePropertyInfoImpl(this,seed); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected AttributePropertyInfoImpl createAttributeProperty(PropertySeed seed) { aoqi@0: return new RuntimeAttributePropertyInfoImpl(this,seed); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected ValuePropertyInfoImpl createValueProperty(PropertySeed seed) { aoqi@0: return new RuntimeValuePropertyInfoImpl(this,seed); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected ElementPropertyInfoImpl createElementProperty(PropertySeed seed) { aoqi@0: return new RuntimeElementPropertyInfoImpl(this,seed); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected MapPropertyInfoImpl createMapProperty(PropertySeed seed) { aoqi@0: return new RuntimeMapPropertyInfoImpl(this,seed); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: @Override aoqi@0: public List getProperties() { aoqi@0: return (List)super.getProperties(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public RuntimePropertyInfo getProperty(String name) { aoqi@0: return (RuntimePropertyInfo)super.getProperty(name); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: public void link() { aoqi@0: getTransducer(); // populate the transducer aoqi@0: super.link(); aoqi@0: } aoqi@0: aoqi@0: private Accessor> attributeWildcardAccessor; aoqi@0: aoqi@0: public Accessor> getAttributeWildcard() { aoqi@0: for( RuntimeClassInfoImpl c=this; c!=null; c=c.getBaseClass() ) { aoqi@0: if(c.attributeWildcard!=null) { aoqi@0: if(c.attributeWildcardAccessor==null) aoqi@0: c.attributeWildcardAccessor = c.createAttributeWildcardAccessor(); aoqi@0: return (Accessor>)c.attributeWildcardAccessor; aoqi@0: } aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: private boolean computedTransducer = false; aoqi@0: private Transducer xducer = null; aoqi@0: aoqi@0: public Transducer getTransducer() { aoqi@0: if(!computedTransducer) { aoqi@0: computedTransducer = true; aoqi@0: xducer = calcTransducer(); aoqi@0: } aoqi@0: return xducer; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Creates a transducer if this class is bound to a text in XML. aoqi@0: */ aoqi@0: private Transducer calcTransducer() { aoqi@0: RuntimeValuePropertyInfo valuep=null; aoqi@0: if(hasAttributeWildcard()) aoqi@0: return null; // has attribute wildcard. Can't be handled as a leaf aoqi@0: for (RuntimeClassInfoImpl ci = this; ci != null; ci = ci.getBaseClass()) { aoqi@0: for( RuntimePropertyInfo pi : ci.getProperties() ) aoqi@0: if(pi.kind()==PropertyKind.VALUE) { aoqi@0: valuep = (RuntimeValuePropertyInfo)pi; aoqi@0: } else { aoqi@0: // this bean has something other than a value aoqi@0: return null; aoqi@0: } aoqi@0: } aoqi@0: if(valuep==null) aoqi@0: return null; aoqi@0: if( !valuep.getTarget().isSimpleType() ) aoqi@0: return null; // if there's an error, recover from it by returning null. aoqi@0: aoqi@0: return new TransducerImpl(getClazz(),TransducedAccessor.get( aoqi@0: ((RuntimeModelBuilder)builder).context,valuep)); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Creates aoqi@0: */ aoqi@0: private Accessor> createAttributeWildcardAccessor() { aoqi@0: assert attributeWildcard!=null; aoqi@0: return ((RuntimePropertySeed)attributeWildcard).getAccessor(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected RuntimePropertySeed createFieldSeed(Field field) { aoqi@0: final boolean readOnly = Modifier.isStatic(field.getModifiers()); aoqi@0: Accessor acc; aoqi@0: try { aoqi@0: if (supressAccessorWarnings) { aoqi@0: acc = ((InternalAccessorFactory)accessorFactory).createFieldAccessor(clazz, field, readOnly, supressAccessorWarnings); aoqi@0: } else { aoqi@0: acc = accessorFactory.createFieldAccessor(clazz, field, readOnly); aoqi@0: } aoqi@0: } catch(JAXBException e) { aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.CUSTOM_ACCESSORFACTORY_FIELD_ERROR.format( aoqi@0: nav().getClassName(clazz), e.toString()), this )); aoqi@0: acc = Accessor.getErrorInstance(); // error recovery aoqi@0: } aoqi@0: return new RuntimePropertySeed(super.createFieldSeed(field), acc ); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public RuntimePropertySeed createAccessorSeed(Method getter, Method setter) { aoqi@0: Accessor acc; aoqi@0: try { aoqi@0: acc = accessorFactory.createPropertyAccessor(clazz, getter, setter); aoqi@0: } catch(JAXBException e) { aoqi@0: builder.reportError(new IllegalAnnotationException( aoqi@0: Messages.CUSTOM_ACCESSORFACTORY_PROPERTY_ERROR.format( aoqi@0: nav().getClassName(clazz), e.toString()), this )); aoqi@0: acc = Accessor.getErrorInstance(); // error recovery aoqi@0: } aoqi@0: return new RuntimePropertySeed( super.createAccessorSeed(getter,setter), aoqi@0: acc ); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected void checkFieldXmlLocation(Field f) { aoqi@0: if(reader().hasFieldAnnotation(XmlLocation.class,f)) aoqi@0: // TODO: check for XmlLocation signature aoqi@0: // TODO: check a collision with the super class aoqi@0: xmlLocationAccessor = new Accessor.FieldReflection(f); aoqi@0: } aoqi@0: aoqi@0: public Accessor getLocatorField() { aoqi@0: return xmlLocationAccessor; aoqi@0: } aoqi@0: aoqi@0: static final class RuntimePropertySeed implements PropertySeed { aoqi@0: /** aoqi@0: * @see #getAccessor() aoqi@0: */ aoqi@0: private final Accessor acc; aoqi@0: aoqi@0: private final PropertySeed core; aoqi@0: aoqi@0: public RuntimePropertySeed(PropertySeed core, Accessor acc) { aoqi@0: this.core = core; aoqi@0: this.acc = acc; aoqi@0: } aoqi@0: aoqi@0: public String getName() { aoqi@0: return core.getName(); aoqi@0: } aoqi@0: aoqi@0: public A readAnnotation(Class annotationType) { aoqi@0: return core.readAnnotation(annotationType); aoqi@0: } aoqi@0: aoqi@0: public boolean hasAnnotation(Class annotationType) { aoqi@0: return core.hasAnnotation(annotationType); aoqi@0: } aoqi@0: aoqi@0: public Type getRawType() { aoqi@0: return core.getRawType(); aoqi@0: } aoqi@0: aoqi@0: public Location getLocation() { aoqi@0: return core.getLocation(); aoqi@0: } aoqi@0: aoqi@0: public Locatable getUpstream() { aoqi@0: return core.getUpstream(); aoqi@0: } aoqi@0: aoqi@0: public Accessor getAccessor() { aoqi@0: return acc; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * {@link Transducer} implementation used when this class maps to PCDATA in XML. aoqi@0: * aoqi@0: * TODO: revisit the exception handling aoqi@0: */ aoqi@0: private static final class TransducerImpl implements Transducer { aoqi@0: private final TransducedAccessor xacc; aoqi@0: private final Class ownerClass; aoqi@0: aoqi@0: public TransducerImpl(Class ownerClass,TransducedAccessor xacc) { aoqi@0: this.xacc = xacc; aoqi@0: this.ownerClass = ownerClass; aoqi@0: } aoqi@0: aoqi@0: public boolean useNamespace() { aoqi@0: return xacc.useNamespace(); aoqi@0: } aoqi@0: aoqi@0: public boolean isDefault() { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: public void declareNamespace(BeanT bean, XMLSerializer w) throws AccessorException { aoqi@0: try { aoqi@0: xacc.declareNamespace(bean,w); aoqi@0: } catch (SAXException e) { aoqi@0: throw new AccessorException(e); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public @NotNull CharSequence print(BeanT o) throws AccessorException { aoqi@0: try { aoqi@0: CharSequence value = xacc.print(o); aoqi@0: if(value==null) aoqi@0: throw new AccessorException(Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE.format(o)); aoqi@0: return value; aoqi@0: } catch (SAXException e) { aoqi@0: throw new AccessorException(e); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public BeanT parse(CharSequence lexical) throws AccessorException, SAXException { aoqi@0: UnmarshallingContext ctxt = UnmarshallingContext.getInstance(); aoqi@0: BeanT inst; aoqi@0: if(ctxt!=null) aoqi@0: inst = (BeanT)ctxt.createInstance(ownerClass); aoqi@0: else aoqi@0: // when this runs for parsing enum constants, aoqi@0: // there's no UnmarshallingContext. aoqi@0: inst = ClassFactory.create(ownerClass); aoqi@0: aoqi@0: xacc.parse(inst,lexical); aoqi@0: return inst; aoqi@0: } aoqi@0: aoqi@0: public void writeText(XMLSerializer w, BeanT o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException { aoqi@0: if(!xacc.hasValue(o)) aoqi@0: throw new AccessorException(Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE.format(o)); aoqi@0: xacc.writeText(w,o,fieldName); aoqi@0: } aoqi@0: aoqi@0: public void writeLeafElement(XMLSerializer w, Name tagName, BeanT o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException { aoqi@0: if(!xacc.hasValue(o)) aoqi@0: throw new AccessorException(Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE.format(o)); aoqi@0: xacc.writeLeafElement(w,tagName,o,fieldName); aoqi@0: } aoqi@0: aoqi@0: public QName getTypeName(BeanT instance) { aoqi@0: return null; aoqi@0: } aoqi@0: } aoqi@0: }