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