/*
 * Copyright (c) 1997, 2012, 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.model.impl;

import java.util.Collection;
import java.lang.annotation.Annotation;

import javax.activation.MimeType;
import javax.xml.bind.annotation.XmlAttachmentRef;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.XmlInlineBinaryData;
import javax.xml.bind.annotation.XmlMimeType;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.namespace.QName;

import com.sun.xml.internal.bind.v2.TODO;
import com.sun.xml.internal.bind.v2.model.annotation.AnnotationReader;
import com.sun.xml.internal.bind.v2.model.annotation.Locatable;
import com.sun.xml.internal.bind.v2.model.core.Adapter;
import com.sun.xml.internal.bind.v2.model.core.ID;
import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
import com.sun.xml.internal.bind.v2.model.nav.Navigator;
import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
import com.sun.xml.internal.bind.v2.runtime.Location;
import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter;

Default partial implementation for PropertyInfo.
Author:Kohsuke Kawaguchi
/** * Default partial implementation for {@link PropertyInfo}. * * @author Kohsuke Kawaguchi */
abstract class PropertyInfoImpl<T,C,F,M> implements PropertyInfo<T,C>, Locatable, Comparable<PropertyInfoImpl> /*by their names*/ {
Object that reads annotations.
/** * Object that reads annotations. */
protected final PropertySeed<T,C,F,M> seed; private final boolean isCollection; private final ID id; private final MimeType expectedMimeType; private final boolean inlineBinary; private final QName schemaType; protected final ClassInfoImpl<T,C,F,M> parent; private final Adapter<T,C> adapter; protected PropertyInfoImpl(ClassInfoImpl<T,C,F,M> parent, PropertySeed<T,C,F,M> spi) { this.seed = spi; this.parent = parent; if(parent==null) /* Various people reported a bug where this parameter is somehow null. In an attempt to catch the error better, let's do an explicit check here. http://forums.java.net/jive/thread.jspa?threadID=18479 http://forums.java.net/jive/thread.jspa?messageID=165946 */ throw new AssertionError(); MimeType mt = Util.calcExpectedMediaType(seed,parent.builder); if(mt!=null && !kind().canHaveXmlMimeType) { parent.builder.reportError(new IllegalAnnotationException( Messages.ILLEGAL_ANNOTATION.format(XmlMimeType.class.getName()), seed.readAnnotation(XmlMimeType.class) )); mt = null; } this.expectedMimeType = mt; this.inlineBinary = seed.hasAnnotation(XmlInlineBinaryData.class); T t = seed.getRawType(); // check if there's an adapter applicable to the whole property XmlJavaTypeAdapter xjta = getApplicableAdapter(t); if(xjta!=null) { isCollection = false; adapter = new Adapter<T,C>(xjta,reader(),nav()); } else { // check if the adapter is applicable to the individual item in the property this.isCollection = nav().isSubClassOf(t, nav().ref(Collection.class)) || nav().isArrayButNotByteArray(t); xjta = getApplicableAdapter(getIndividualType()); if(xjta==null) { // ugly ugly hack, but we implement swaRef as adapter XmlAttachmentRef xsa = seed.readAnnotation(XmlAttachmentRef.class); if(xsa!=null) { parent.builder.hasSwaRef = true; adapter = new Adapter<T,C>(nav().asDecl(SwaRefAdapter.class),nav()); } else { adapter = null; // if this field has adapter annotation but not applicable, // that must be an error of the user xjta = seed.readAnnotation(XmlJavaTypeAdapter.class); if(xjta!=null) { T ad = reader().getClassValue(xjta,"value"); parent.builder.reportError(new IllegalAnnotationException( Messages.UNMATCHABLE_ADAPTER.format( nav().getTypeName(ad), nav().getTypeName(t)), xjta )); } } } else { adapter = new Adapter<T,C>(xjta,reader(),nav()); } } this.id = calcId(); this.schemaType = Util.calcSchemaType(reader(),seed,parent.clazz, getIndividualType(),this); } public ClassInfoImpl<T,C,F,M> parent() { return parent; } protected final Navigator<T,C,F,M> nav() { return parent.nav(); } protected final AnnotationReader<T,C,F,M> reader() { return parent.reader(); } public T getRawType() { return seed.getRawType(); } public T getIndividualType() { if(adapter!=null) return adapter.defaultType; T raw = getRawType(); if(!isCollection()) { return raw; } else { if(nav().isArrayButNotByteArray(raw)) return nav().getComponentType(raw); T bt = nav().getBaseClass(raw, nav().asDecl(Collection.class) ); if(nav().isParameterizedType(bt)) return nav().getTypeArgument(bt,0); else return nav().ref(Object.class); } } public final String getName() { return seed.getName(); }
Checks if the given adapter is applicable to the declared property type.
/** * Checks if the given adapter is applicable to the declared property type. */
private boolean isApplicable(XmlJavaTypeAdapter jta, T declaredType ) { if(jta==null) return false; T type = reader().getClassValue(jta,"type"); if(nav().isSameType(declaredType, type)) return true; // for types explicitly marked in XmlJavaTypeAdapter.type() T ad = reader().getClassValue(jta,"value"); T ba = nav().getBaseClass(ad, nav().asDecl(XmlAdapter.class)); if(!nav().isParameterizedType(ba)) return true; // can't check type applicability. assume Object, which means applicable to any. T inMemType = nav().getTypeArgument(ba, 1); return nav().isSubClassOf(declaredType,inMemType); } private XmlJavaTypeAdapter getApplicableAdapter(T type) { XmlJavaTypeAdapter jta = seed.readAnnotation(XmlJavaTypeAdapter.class); if(jta!=null && isApplicable(jta,type)) return jta; // check the applicable adapters on the package XmlJavaTypeAdapters jtas = reader().getPackageAnnotation(XmlJavaTypeAdapters.class, parent.clazz, seed ); if(jtas!=null) { for (XmlJavaTypeAdapter xjta : jtas.value()) { if(isApplicable(xjta,type)) return xjta; } } jta = reader().getPackageAnnotation(XmlJavaTypeAdapter.class, parent.clazz, seed ); if(isApplicable(jta,type)) return jta; // then on the target class C refType = nav().asDecl(type); if(refType!=null) { jta = reader().getClassAnnotation(XmlJavaTypeAdapter.class, refType, seed ); if(jta!=null && isApplicable(jta,type)) // the one on the type always apply. return jta; } return null; }
This is the default implementation of the getAdapter method defined on many of the PropertyInfo-derived classes.
/** * This is the default implementation of the getAdapter method * defined on many of the {@link PropertyInfo}-derived classes. */
public Adapter<T,C> getAdapter() { return adapter; } public final String displayName() { return nav().getClassName(parent.getClazz())+'#'+getName(); } public final ID id() { return id; } private ID calcId() { if(seed.hasAnnotation(XmlID.class)) { // check the type if(!nav().isSameType(getIndividualType(), nav().ref(String.class))) parent.builder.reportError(new IllegalAnnotationException( Messages.ID_MUST_BE_STRING.format(getName()), seed ) ); return ID.ID; } else if(seed.hasAnnotation(XmlIDREF.class)) { return ID.IDREF; } else { return ID.NONE; } } public final MimeType getExpectedMimeType() { return expectedMimeType; } public final boolean inlineBinaryData() { return inlineBinary; } public final QName getSchemaType() { return schemaType; } public final boolean isCollection() { return isCollection; }
Called after all the TypeInfos are collected into the governing TypeInfoSet. Derived class can do additional actions to complete the model.
/** * Called after all the {@link TypeInfo}s are collected into the governing {@link TypeInfoSet}. * * Derived class can do additional actions to complete the model. */
protected void link() { if(id==ID.IDREF) { // make sure that the refereced type has ID for (TypeInfo<T,C> ti : ref()) { if(!ti.canBeReferencedByIDREF()) parent.builder.reportError(new IllegalAnnotationException( Messages.INVALID_IDREF.format( parent.builder.nav.getTypeName(ti.getType())), this )); } } }
A PropertyInfoImpl is always referenced by its enclosing class, so return that as the upstream.
/** * A {@link PropertyInfoImpl} is always referenced by its enclosing class, * so return that as the upstream. */
public Locatable getUpstream() { return parent; } public Location getLocation() { return seed.getLocation(); } // // // convenience methods for derived classes // //
Computes the tag name from a XmlElement by taking the defaulting into account.
/** * Computes the tag name from a {@link XmlElement} by taking the defaulting into account. */
protected final QName calcXmlName(XmlElement e) { if(e!=null) return calcXmlName(e.namespace(),e.name()); else return calcXmlName("##default","##default"); }
Computes the tag name from a XmlElementWrapper by taking the defaulting into account.
/** * Computes the tag name from a {@link XmlElementWrapper} by taking the defaulting into account. */
protected final QName calcXmlName(XmlElementWrapper e) { if(e!=null) return calcXmlName(e.namespace(),e.name()); else return calcXmlName("##default","##default"); } private QName calcXmlName(String uri,String local) { // compute the default TODO.checkSpec(); if(local.length()==0 || local.equals("##default")) local = seed.getName(); if(uri.equals("##default")) { XmlSchema xs = reader().getPackageAnnotation( XmlSchema.class, parent.getClazz(), this ); // JAX-RPC doesn't want the default namespace URI swapping to take effect to // local "unqualified" elements. UGLY. if(xs!=null) { switch(xs.elementFormDefault()) { case QUALIFIED: QName typeName = parent.getTypeName(); if(typeName!=null) uri = typeName.getNamespaceURI(); else uri = xs.namespace(); if(uri.length()==0) uri = parent.builder.defaultNsUri; break; case UNQUALIFIED: case UNSET: uri = ""; } } else { uri = ""; } } return new QName(uri.intern(),local.intern()); } public int compareTo(PropertyInfoImpl that) { return this.getName().compareTo(that.getName()); } public final <A extends Annotation> A readAnnotation(Class<A> annotationType) { return seed.readAnnotation(annotationType); } public final boolean hasAnnotation(Class<? extends Annotation> annotationType) { return seed.hasAnnotation(annotationType); } }