aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2011, 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.lang.annotation.Annotation; aoqi@0: import java.util.Collection; aoqi@0: import java.util.Collections; aoqi@0: import java.util.List; aoqi@0: aoqi@0: import javax.activation.MimeType; aoqi@0: import javax.xml.bind.JAXBElement; aoqi@0: import javax.xml.bind.annotation.XmlAttachmentRef; aoqi@0: import javax.xml.bind.annotation.XmlElementDecl; aoqi@0: import javax.xml.bind.annotation.XmlID; aoqi@0: import javax.xml.bind.annotation.XmlIDREF; aoqi@0: import javax.xml.bind.annotation.XmlInlineBinaryData; aoqi@0: import javax.xml.bind.annotation.XmlSchema; aoqi@0: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; aoqi@0: import javax.xml.namespace.QName; aoqi@0: aoqi@0: import com.sun.istack.internal.FinalArrayList; aoqi@0: import com.sun.xml.internal.bind.v2.TODO; aoqi@0: import com.sun.xml.internal.bind.v2.model.annotation.AnnotationSource; aoqi@0: import com.sun.xml.internal.bind.v2.model.annotation.Locatable; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.Adapter; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.ClassInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.ElementInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.ElementPropertyInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.ID; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.NonElement; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.PropertyInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.PropertyKind; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.TypeInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.TypeRef; 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.SwaRefAdapter; aoqi@0: aoqi@0: /** aoqi@0: * {@link ElementInfo} implementation. aoqi@0: * aoqi@0: * @author Kohsuke Kawaguchi aoqi@0: */ aoqi@0: class ElementInfoImpl extends TypeInfoImpl implements ElementInfo { aoqi@0: aoqi@0: private final QName tagName; aoqi@0: aoqi@0: private final NonElement contentType; aoqi@0: aoqi@0: private final T tOfJAXBElementT; aoqi@0: aoqi@0: private final T elementType; aoqi@0: aoqi@0: private final ClassInfo scope; aoqi@0: aoqi@0: /** aoqi@0: * Annotation that controls the binding. aoqi@0: */ aoqi@0: private final XmlElementDecl anno; aoqi@0: aoqi@0: /** aoqi@0: * If this element can substitute another element, the element name. aoqi@0: * @see #link() aoqi@0: */ aoqi@0: private ElementInfoImpl substitutionHead; aoqi@0: aoqi@0: /** aoqi@0: * Lazily constructed list of {@link ElementInfo}s that can substitute this element. aoqi@0: * This could be null. aoqi@0: * @see #link() aoqi@0: */ aoqi@0: private FinalArrayList> substitutionMembers; aoqi@0: aoqi@0: /** aoqi@0: * The factory method from which this mapping was created. aoqi@0: */ aoqi@0: private final M method; aoqi@0: aoqi@0: /** aoqi@0: * If the content type is adapter, return that adapter. aoqi@0: */ aoqi@0: private final Adapter adapter; aoqi@0: aoqi@0: private final boolean isCollection; aoqi@0: aoqi@0: private final ID id; aoqi@0: aoqi@0: private final PropertyImpl property; aoqi@0: private final MimeType expectedMimeType; aoqi@0: private final boolean inlineBinary; aoqi@0: private final QName schemaType; aoqi@0: aoqi@0: /** aoqi@0: * Singleton instance of {@link ElementPropertyInfo} for this element. aoqi@0: */ aoqi@0: protected class PropertyImpl implements aoqi@0: ElementPropertyInfo, aoqi@0: TypeRef, aoqi@0: AnnotationSource { aoqi@0: // aoqi@0: // TypeRef impl aoqi@0: // aoqi@0: public NonElement getTarget() { aoqi@0: return contentType; aoqi@0: } aoqi@0: public QName getTagName() { aoqi@0: return tagName; aoqi@0: } aoqi@0: aoqi@0: public List> getTypes() { aoqi@0: return Collections.singletonList(this); aoqi@0: } aoqi@0: aoqi@0: public List> ref() { aoqi@0: return Collections.singletonList(contentType); aoqi@0: } aoqi@0: aoqi@0: public QName getXmlName() { aoqi@0: return tagName; aoqi@0: } aoqi@0: aoqi@0: public boolean isCollectionRequired() { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: public boolean isCollectionNillable() { aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: public boolean isNillable() { aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: public String getDefaultValue() { aoqi@0: String v = anno.defaultValue(); aoqi@0: if(v.equals("\u0000")) aoqi@0: return null; aoqi@0: else aoqi@0: return v; aoqi@0: } aoqi@0: aoqi@0: public ElementInfoImpl parent() { aoqi@0: return ElementInfoImpl.this; aoqi@0: } aoqi@0: aoqi@0: public String getName() { aoqi@0: return "value"; aoqi@0: } aoqi@0: aoqi@0: public String displayName() { aoqi@0: return "JAXBElement#value"; aoqi@0: } aoqi@0: aoqi@0: public boolean isCollection() { aoqi@0: return isCollection; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * For {@link ElementInfo}s, a collection always means a list of values. aoqi@0: */ aoqi@0: public boolean isValueList() { aoqi@0: return isCollection; aoqi@0: } aoqi@0: aoqi@0: public boolean isRequired() { aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: public PropertyKind kind() { aoqi@0: return PropertyKind.ELEMENT; aoqi@0: } aoqi@0: aoqi@0: public Adapter getAdapter() { aoqi@0: return adapter; aoqi@0: } aoqi@0: aoqi@0: public ID id() { aoqi@0: return id; aoqi@0: } aoqi@0: aoqi@0: public MimeType getExpectedMimeType() { aoqi@0: return expectedMimeType; aoqi@0: } aoqi@0: aoqi@0: public QName getSchemaType() { aoqi@0: return schemaType; aoqi@0: } aoqi@0: aoqi@0: public boolean inlineBinaryData() { aoqi@0: return inlineBinary; aoqi@0: } aoqi@0: aoqi@0: public PropertyInfo getSource() { aoqi@0: return this; aoqi@0: } aoqi@0: aoqi@0: // aoqi@0: // aoqi@0: // AnnotationSource impl aoqi@0: // aoqi@0: // aoqi@0: public A readAnnotation(Class annotationType) { aoqi@0: return reader().getMethodAnnotation(annotationType,method,ElementInfoImpl.this); aoqi@0: } aoqi@0: aoqi@0: public boolean hasAnnotation(Class annotationType) { aoqi@0: return reader().hasMethodAnnotation(annotationType,method); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * @param m aoqi@0: * The factory method on ObjectFactory that comes with {@link XmlElementDecl}. aoqi@0: */ aoqi@0: public ElementInfoImpl(ModelBuilder builder, aoqi@0: RegistryInfoImpl registry, M m ) throws IllegalAnnotationException { aoqi@0: super(builder,registry); aoqi@0: aoqi@0: this.method = m; aoqi@0: anno = reader().getMethodAnnotation( XmlElementDecl.class, m, this ); aoqi@0: assert anno!=null; // the caller should check this aoqi@0: assert anno instanceof Locatable; aoqi@0: aoqi@0: elementType = nav().getReturnType(m); aoqi@0: T baseClass = nav().getBaseClass(elementType,nav().asDecl(JAXBElement.class)); aoqi@0: if(baseClass==null) aoqi@0: throw new IllegalAnnotationException( aoqi@0: Messages.XML_ELEMENT_MAPPING_ON_NON_IXMLELEMENT_METHOD.format(nav().getMethodName(m)), aoqi@0: anno ); aoqi@0: aoqi@0: tagName = parseElementName(anno); aoqi@0: T[] methodParams = nav().getMethodParameters(m); aoqi@0: aoqi@0: // adapter aoqi@0: Adapter a = null; aoqi@0: if(methodParams.length>0) { aoqi@0: XmlJavaTypeAdapter adapter = reader().getMethodAnnotation(XmlJavaTypeAdapter.class,m,this); aoqi@0: if(adapter!=null) aoqi@0: a = new Adapter(adapter,reader(),nav()); aoqi@0: else { aoqi@0: XmlAttachmentRef xsa = reader().getMethodAnnotation(XmlAttachmentRef.class,m,this); aoqi@0: if(xsa!=null) { aoqi@0: TODO.prototype("in Annotation Processing swaRefAdapter isn't avaialble, so this returns null"); aoqi@0: a = new Adapter(owner.nav.asDecl(SwaRefAdapter.class),owner.nav); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: this.adapter = a; aoqi@0: aoqi@0: // T of JAXBElement aoqi@0: tOfJAXBElementT = aoqi@0: methodParams.length>0 ? methodParams[0] // this is more reliable, as it works even for ObjectFactory that sometimes have to return public types aoqi@0: : nav().getTypeArgument(baseClass,0); // fall back to infer from the return type if no parameter. aoqi@0: aoqi@0: if(adapter==null) { aoqi@0: T list = nav().getBaseClass(tOfJAXBElementT,nav().asDecl(List.class)); aoqi@0: if(list==null) { aoqi@0: isCollection = false; aoqi@0: contentType = builder.getTypeInfo(tOfJAXBElementT,this); // suck this type into the current set. aoqi@0: } else { aoqi@0: isCollection = true; aoqi@0: contentType = builder.getTypeInfo(nav().getTypeArgument(list,0),this); aoqi@0: } aoqi@0: } else { aoqi@0: // but if adapted, use the adapted type aoqi@0: contentType = builder.getTypeInfo(this.adapter.defaultType,this); aoqi@0: isCollection = false; aoqi@0: } aoqi@0: aoqi@0: // scope aoqi@0: T s = reader().getClassValue(anno,"scope"); aoqi@0: if(nav().isSameType(s, nav().ref(XmlElementDecl.GLOBAL.class))) aoqi@0: scope = null; aoqi@0: else { aoqi@0: // TODO: what happens if there's an error? aoqi@0: NonElement scp = builder.getClassInfo(nav().asDecl(s),this); aoqi@0: if(!(scp instanceof ClassInfo)) { aoqi@0: throw new IllegalAnnotationException( aoqi@0: Messages.SCOPE_IS_NOT_COMPLEXTYPE.format(nav().getTypeName(s)), aoqi@0: anno ); aoqi@0: } aoqi@0: scope = (ClassInfo)scp; aoqi@0: } aoqi@0: aoqi@0: id = calcId(); aoqi@0: aoqi@0: property = createPropertyImpl(); aoqi@0: aoqi@0: this.expectedMimeType = Util.calcExpectedMediaType(property,builder); aoqi@0: this.inlineBinary = reader().hasMethodAnnotation(XmlInlineBinaryData.class,method); aoqi@0: this.schemaType = Util.calcSchemaType(reader(),property,registry.registryClass, aoqi@0: getContentInMemoryType(),this); aoqi@0: } aoqi@0: aoqi@0: final QName parseElementName(XmlElementDecl e) { aoqi@0: String local = e.name(); aoqi@0: String nsUri = e.namespace(); aoqi@0: if(nsUri.equals("##default")) { aoqi@0: // if defaulted ... aoqi@0: XmlSchema xs = reader().getPackageAnnotation(XmlSchema.class, aoqi@0: nav().getDeclaringClassForMethod(method),this); aoqi@0: if(xs!=null) aoqi@0: nsUri = xs.namespace(); aoqi@0: else { aoqi@0: nsUri = builder.defaultNsUri; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return new QName(nsUri.intern(),local.intern()); aoqi@0: } aoqi@0: aoqi@0: protected PropertyImpl createPropertyImpl() { aoqi@0: return new PropertyImpl(); aoqi@0: } aoqi@0: aoqi@0: public ElementPropertyInfo getProperty() { aoqi@0: return property; aoqi@0: } aoqi@0: aoqi@0: public NonElement getContentType() { aoqi@0: return contentType; aoqi@0: } aoqi@0: aoqi@0: public T getContentInMemoryType() { aoqi@0: if(adapter==null) { aoqi@0: return tOfJAXBElementT; aoqi@0: } else { aoqi@0: return adapter.customType; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public QName getElementName() { aoqi@0: return tagName; aoqi@0: } aoqi@0: aoqi@0: public T getType() { aoqi@0: return elementType; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Leaf-type cannot be referenced from IDREF. aoqi@0: * aoqi@0: * @deprecated aoqi@0: * why are you calling a method whose return value is always known? aoqi@0: */ aoqi@0: public final boolean canBeReferencedByIDREF() { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: private ID calcId() { aoqi@0: // TODO: share code with PropertyInfoImpl aoqi@0: if(reader().hasMethodAnnotation(XmlID.class,method)) { aoqi@0: return ID.ID; aoqi@0: } else aoqi@0: if(reader().hasMethodAnnotation(XmlIDREF.class,method)) { aoqi@0: return ID.IDREF; aoqi@0: } else { aoqi@0: return ID.NONE; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public ClassInfo getScope() { aoqi@0: return scope; aoqi@0: } aoqi@0: aoqi@0: public ElementInfo getSubstitutionHead() { aoqi@0: return substitutionHead; aoqi@0: } aoqi@0: aoqi@0: public Collection> getSubstitutionMembers() { aoqi@0: if(substitutionMembers==null) aoqi@0: return Collections.emptyList(); aoqi@0: else aoqi@0: return substitutionMembers; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Called after all the {@link TypeInfo}s are collected into the {@link #owner}. aoqi@0: */ aoqi@0: /*package*/ void link() { aoqi@0: // substitution head aoqi@0: if(anno.substitutionHeadName().length()!=0) { aoqi@0: QName name = new QName( aoqi@0: anno.substitutionHeadNamespace(), anno.substitutionHeadName() ); aoqi@0: substitutionHead = owner.getElementInfo(null,name); aoqi@0: if(substitutionHead==null) { aoqi@0: builder.reportError( aoqi@0: new IllegalAnnotationException(Messages.NON_EXISTENT_ELEMENT_MAPPING.format( aoqi@0: name.getNamespaceURI(),name.getLocalPart()), anno)); aoqi@0: // recover by ignoring this substitution declaration aoqi@0: } else aoqi@0: substitutionHead.addSubstitutionMember(this); aoqi@0: } else aoqi@0: substitutionHead = null; aoqi@0: super.link(); aoqi@0: } aoqi@0: aoqi@0: private void addSubstitutionMember(ElementInfoImpl child) { aoqi@0: if(substitutionMembers==null) aoqi@0: substitutionMembers = new FinalArrayList>(); aoqi@0: substitutionMembers.add(child); aoqi@0: } aoqi@0: aoqi@0: public Location getLocation() { aoqi@0: return nav().getMethodLocation(method); aoqi@0: } aoqi@0: }