001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.bind.v2.model.impl;
038:
039: import java.util.Collection;
040: import java.lang.annotation.Annotation;
041:
042: import javax.activation.MimeType;
043: import javax.xml.bind.annotation.XmlAttachmentRef;
044: import javax.xml.bind.annotation.XmlElement;
045: import javax.xml.bind.annotation.XmlElementWrapper;
046: import javax.xml.bind.annotation.XmlID;
047: import javax.xml.bind.annotation.XmlIDREF;
048: import javax.xml.bind.annotation.XmlInlineBinaryData;
049: import javax.xml.bind.annotation.XmlMimeType;
050: import javax.xml.bind.annotation.XmlSchema;
051: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
052: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
053: import javax.xml.bind.annotation.adapters.XmlAdapter;
054: import javax.xml.namespace.QName;
055:
056: import com.sun.xml.bind.v2.TODO;
057: import com.sun.xml.bind.v2.model.annotation.AnnotationReader;
058: import com.sun.xml.bind.v2.model.annotation.Locatable;
059: import com.sun.xml.bind.v2.model.core.Adapter;
060: import com.sun.xml.bind.v2.model.core.ID;
061: import com.sun.xml.bind.v2.model.core.PropertyInfo;
062: import com.sun.xml.bind.v2.model.core.TypeInfo;
063: import com.sun.xml.bind.v2.model.core.TypeInfoSet;
064: import com.sun.xml.bind.v2.model.nav.Navigator;
065: import com.sun.xml.bind.v2.runtime.IllegalAnnotationException;
066: import com.sun.xml.bind.v2.runtime.Location;
067: import com.sun.xml.bind.v2.runtime.SwaRefAdapter;
068:
069: /**
070: * Default partial implementation for {@link PropertyInfo}.
071: *
072: * @author Kohsuke Kawaguchi
073: */
074: abstract class PropertyInfoImpl<T, C, F, M> implements
075: PropertyInfo<T, C>, Locatable, Comparable<PropertyInfoImpl> /*by their names*/{
076:
077: /**
078: * Object that reads annotations.
079: */
080: protected final PropertySeed<T, C, F, M> seed;
081:
082: private final boolean isCollection;
083:
084: private final ID id;
085:
086: private final MimeType expectedMimeType;
087: private final boolean inlineBinary;
088: private final QName schemaType;
089:
090: protected final ClassInfoImpl<T, C, F, M> parent;
091:
092: private final Adapter<T, C> adapter;
093:
094: protected PropertyInfoImpl(ClassInfoImpl<T, C, F, M> parent,
095: PropertySeed<T, C, F, M> spi) {
096: this .seed = spi;
097: this .parent = parent;
098:
099: if (parent == null)
100: /*
101: Various people reported a bug where this parameter is somehow null.
102: In an attempt to catch the error better, let's do an explicit check here.
103:
104: http://forums.java.net/jive/thread.jspa?threadID=18479
105: http://forums.java.net/jive/thread.jspa?messageID=165946
106: */
107: throw new AssertionError();
108:
109: MimeType mt = Util.calcExpectedMediaType(seed, parent.builder);
110: if (mt != null && !kind().canHaveXmlMimeType) {
111: parent.builder.reportError(new IllegalAnnotationException(
112: Messages.ILLEGAL_ANNOTATION
113: .format(XmlMimeType.class.getName()), seed
114: .readAnnotation(XmlMimeType.class)));
115: mt = null;
116: }
117: this .expectedMimeType = mt;
118: this .inlineBinary = seed
119: .hasAnnotation(XmlInlineBinaryData.class);
120:
121: T t = seed.getRawType();
122:
123: // check if there's an adapter applicable to the whole property
124: XmlJavaTypeAdapter xjta = getApplicableAdapter(t);
125: if (xjta != null) {
126: isCollection = false;
127: adapter = new Adapter<T, C>(xjta, reader(), nav());
128: } else {
129: // check if the adapter is applicable to the individual item in the property
130:
131: this .isCollection = nav().isSubClassOf(t,
132: nav().ref(Collection.class))
133: || nav().isArrayButNotByteArray(t);
134:
135: xjta = getApplicableAdapter(getIndividualType());
136: if (xjta == null) {
137: // ugly ugly hack, but we implement swaRef as adapter
138: XmlAttachmentRef xsa = seed
139: .readAnnotation(XmlAttachmentRef.class);
140: if (xsa != null) {
141: parent.builder.hasSwaRef = true;
142: adapter = new Adapter<T, C>(nav().asDecl(
143: SwaRefAdapter.class), nav());
144: } else {
145: adapter = null;
146:
147: // if this field has adapter annotation but not applicable,
148: // that must be an error of the user
149: xjta = seed
150: .readAnnotation(XmlJavaTypeAdapter.class);
151: if (xjta != null) {
152: T adapter = reader().getClassValue(xjta,
153: "value");
154: parent.builder
155: .reportError(new IllegalAnnotationException(
156: Messages.UNMATCHABLE_ADAPTER
157: .format(
158: nav()
159: .getTypeName(
160: adapter),
161: nav()
162: .getTypeName(
163: t)),
164: xjta));
165: }
166: }
167: } else {
168: adapter = new Adapter<T, C>(xjta, reader(), nav());
169: }
170: }
171:
172: this .id = calcId();
173: this .schemaType = Util.calcSchemaType(reader(), seed,
174: parent.clazz, getIndividualType(), this );
175: }
176:
177: public ClassInfoImpl<T, C, F, M> parent() {
178: return parent;
179: }
180:
181: protected final Navigator<T, C, F, M> nav() {
182: return parent.nav();
183: }
184:
185: protected final AnnotationReader<T, C, F, M> reader() {
186: return parent.reader();
187: }
188:
189: public T getRawType() {
190: return seed.getRawType();
191: }
192:
193: public T getIndividualType() {
194: if (adapter != null)
195: return adapter.defaultType;
196: T raw = getRawType();
197: if (!isCollection()) {
198: return raw;
199: } else {
200: if (nav().isArrayButNotByteArray(raw))
201: return nav().getComponentType(raw);
202:
203: T bt = nav().getBaseClass(raw,
204: nav().asDecl(Collection.class));
205: if (nav().isParameterizedType(bt))
206: return nav().getTypeArgument(bt, 0);
207: else
208: return nav().ref(Object.class);
209: }
210: }
211:
212: public final String getName() {
213: return seed.getName();
214: }
215:
216: /**
217: * Checks if the given adapter is applicable to the declared property type.
218: */
219: private boolean isApplicable(XmlJavaTypeAdapter jta, T declaredType) {
220: if (jta == null)
221: return false;
222:
223: T type = reader().getClassValue(jta, "type");
224: if (declaredType.equals(type))
225: return true; // for types explicitly marked in XmlJavaTypeAdapter.type()
226:
227: T adapter = reader().getClassValue(jta, "value");
228: T ba = nav().getBaseClass(adapter,
229: nav().asDecl(XmlAdapter.class));
230: if (!nav().isParameterizedType(ba))
231: return true; // can't check type applicability. assume Object, which means applicable to any.
232: T inMemType = nav().getTypeArgument(ba, 1);
233:
234: return nav().isSubClassOf(declaredType, inMemType);
235: }
236:
237: private XmlJavaTypeAdapter getApplicableAdapter(T type) {
238: XmlJavaTypeAdapter jta = seed
239: .readAnnotation(XmlJavaTypeAdapter.class);
240: if (jta != null && isApplicable(jta, type))
241: return jta;
242:
243: // check the applicable adapters on the package
244: XmlJavaTypeAdapters jtas = reader().getPackageAnnotation(
245: XmlJavaTypeAdapters.class, parent.clazz, seed);
246: if (jtas != null) {
247: for (XmlJavaTypeAdapter xjta : jtas.value()) {
248: if (isApplicable(xjta, type))
249: return xjta;
250: }
251: }
252: jta = reader().getPackageAnnotation(XmlJavaTypeAdapter.class,
253: parent.clazz, seed);
254: if (isApplicable(jta, type))
255: return jta;
256:
257: // then on the target class
258: C refType = nav().asDecl(type);
259: if (refType != null) {
260: jta = reader().getClassAnnotation(XmlJavaTypeAdapter.class,
261: refType, seed);
262: if (jta != null && isApplicable(jta, type)) // the one on the type always apply.
263: return jta;
264: }
265:
266: return null;
267: }
268:
269: /**
270: * This is the default implementation of the getAdapter method
271: * defined on many of the {@link PropertyInfo}-derived classes.
272: */
273: public Adapter<T, C> getAdapter() {
274: return adapter;
275: }
276:
277: public final String displayName() {
278: return nav().getClassName(parent.getClazz()) + '#' + getName();
279: }
280:
281: public final ID id() {
282: return id;
283: }
284:
285: private ID calcId() {
286: if (seed.hasAnnotation(XmlID.class)) {
287: // check the type
288: if (!getIndividualType().equals(nav().ref(String.class)))
289: parent.builder
290: .reportError(new IllegalAnnotationException(
291: Messages.ID_MUST_BE_STRING
292: .format(getName()), seed));
293: return ID.ID;
294: } else if (seed.hasAnnotation(XmlIDREF.class)) {
295: return ID.IDREF;
296: } else {
297: return ID.NONE;
298: }
299: }
300:
301: public final MimeType getExpectedMimeType() {
302: return expectedMimeType;
303: }
304:
305: public final boolean inlineBinaryData() {
306: return inlineBinary;
307: }
308:
309: public final QName getSchemaType() {
310: return schemaType;
311: }
312:
313: public final boolean isCollection() {
314: return isCollection;
315: }
316:
317: /**
318: * Called after all the {@link TypeInfo}s are collected into the governing {@link TypeInfoSet}.
319: *
320: * Derived class can do additional actions to complete the model.
321: */
322: protected void link() {
323: if (id == ID.IDREF) {
324: // make sure that the refereced type has ID
325: for (TypeInfo<T, C> ti : ref()) {
326: if (!ti.canBeReferencedByIDREF())
327: parent.builder
328: .reportError(new IllegalAnnotationException(
329: Messages.INVALID_IDREF
330: .format(parent.builder.nav
331: .getTypeName(ti
332: .getType())),
333: this ));
334: }
335: }
336: }
337:
338: /**
339: * A {@link PropertyInfoImpl} is always referenced by its enclosing class,
340: * so return that as the upstream.
341: */
342: public Locatable getUpstream() {
343: return parent;
344: }
345:
346: public Location getLocation() {
347: return seed.getLocation();
348: }
349:
350: //
351: //
352: // convenience methods for derived classes
353: //
354: //
355:
356: /**
357: * Computes the tag name from a {@link XmlElement} by taking the defaulting into account.
358: */
359: protected final QName calcXmlName(XmlElement e) {
360: if (e != null)
361: return calcXmlName(e.namespace(), e.name());
362: else
363: return calcXmlName("##default", "##default");
364: }
365:
366: /**
367: * Computes the tag name from a {@link XmlElementWrapper} by taking the defaulting into account.
368: */
369: protected final QName calcXmlName(XmlElementWrapper e) {
370: if (e != null)
371: return calcXmlName(e.namespace(), e.name());
372: else
373: return calcXmlName("##default", "##default");
374: }
375:
376: private QName calcXmlName(String uri, String local) {
377: // compute the default
378: TODO.checkSpec();
379: if (local.length() == 0 || local.equals("##default"))
380: local = seed.getName();
381: if (uri.equals("##default")) {
382: XmlSchema xs = reader().getPackageAnnotation(
383: XmlSchema.class, parent.getClazz(), this );
384: // JAX-RPC doesn't want the default namespace URI swapping to take effect to
385: // local "unqualified" elements. UGLY.
386: if (xs != null) {
387: switch (xs.elementFormDefault()) {
388: case QUALIFIED:
389: QName typeName = parent.getTypeName();
390: if (typeName != null)
391: uri = typeName.getNamespaceURI();
392: else
393: uri = xs.namespace();
394: if (uri.length() == 0)
395: uri = parent.builder.defaultNsUri;
396: break;
397: case UNQUALIFIED:
398: case UNSET:
399: uri = "";
400: }
401: } else {
402: uri = "";
403: }
404: }
405: return new QName(uri.intern(), local.intern());
406: }
407:
408: public int compareTo(PropertyInfoImpl that) {
409: return this .getName().compareTo(that.getName());
410: }
411:
412: public final <A extends Annotation> A readAnnotation(
413: Class<A> annotationType) {
414: return seed.readAnnotation(annotationType);
415: }
416:
417: public final boolean hasAnnotation(
418: Class<? extends Annotation> annotationType) {
419: return seed.hasAnnotation(annotationType);
420: }
421: }
|