001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.v2.model.impl;
027:
028: import java.lang.annotation.Annotation;
029: import java.util.Collection;
030: import java.util.Collections;
031: import java.util.List;
032:
033: import javax.activation.MimeType;
034: import javax.xml.bind.JAXBElement;
035: import javax.xml.bind.annotation.XmlAttachmentRef;
036: import javax.xml.bind.annotation.XmlElementDecl;
037: import javax.xml.bind.annotation.XmlID;
038: import javax.xml.bind.annotation.XmlIDREF;
039: import javax.xml.bind.annotation.XmlInlineBinaryData;
040: import javax.xml.bind.annotation.XmlSchema;
041: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
042: import javax.xml.namespace.QName;
043:
044: import com.sun.xml.internal.bind.v2.TODO;
045: import com.sun.xml.internal.bind.v2.model.annotation.AnnotationSource;
046: import com.sun.xml.internal.bind.v2.model.annotation.Locatable;
047: import com.sun.xml.internal.bind.v2.model.core.Adapter;
048: import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
049: import com.sun.xml.internal.bind.v2.model.core.ElementInfo;
050: import com.sun.xml.internal.bind.v2.model.core.ElementPropertyInfo;
051: import com.sun.xml.internal.bind.v2.model.core.ID;
052: import com.sun.xml.internal.bind.v2.model.core.NonElement;
053: import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
054: import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
055: import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
056: import com.sun.xml.internal.bind.v2.model.core.TypeRef;
057: import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
058: import com.sun.xml.internal.bind.v2.runtime.Location;
059: import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter;
060: import com.sun.istack.internal.FinalArrayList;
061:
062: /**
063: * {@link ElementInfo} implementation.
064: *
065: * @author Kohsuke Kawaguchi
066: */
067: class ElementInfoImpl<T, C, F, M> extends TypeInfoImpl<T, C, F, M>
068: implements ElementInfo<T, C> {
069:
070: private final QName tagName;
071:
072: private final NonElement<T, C> contentType;
073:
074: private final T elementType;
075:
076: private final ClassInfo<T, C> scope;
077:
078: /**
079: * Annotation that controls the binding.
080: */
081: private final XmlElementDecl anno;
082:
083: /**
084: * If this element can substitute another element, the element name.
085: * @see #link()
086: */
087: private ElementInfoImpl<T, C, F, M> substitutionHead;
088:
089: /**
090: * Lazily constructed list of {@link ElementInfo}s that can substitute this element.
091: * This could be null.
092: * @see #link()
093: */
094: private FinalArrayList<ElementInfoImpl<T, C, F, M>> substitutionMembers;
095:
096: /**
097: * The factory method from which this mapping was created.
098: */
099: private final M method;
100:
101: /**
102: * If the content type is adapter, return that adapter.
103: */
104: private final Adapter<T, C> adapter;
105:
106: private final boolean isCollection;
107:
108: private final ID id;
109:
110: private final PropertyImpl property;
111: private final MimeType expectedMimeType;
112: private final boolean inlineBinary;
113: private final QName schemaType;
114:
115: /**
116: * Singleton instance of {@link ElementPropertyInfo} for this element.
117: */
118: protected class PropertyImpl implements ElementPropertyInfo<T, C>,
119: TypeRef<T, C>, AnnotationSource {
120: //
121: // TypeRef impl
122: //
123: public NonElement<T, C> getTarget() {
124: return contentType;
125: }
126:
127: public QName getTagName() {
128: return tagName;
129: }
130:
131: public List<? extends TypeRef<T, C>> getTypes() {
132: return Collections.singletonList(this );
133: }
134:
135: public List<? extends NonElement<T, C>> ref() {
136: return Collections.singletonList(contentType);
137: }
138:
139: public QName getXmlName() {
140: return tagName;
141: }
142:
143: public boolean isCollectionNillable() {
144: return true;
145: }
146:
147: public boolean isNillable() {
148: return true;
149: }
150:
151: public String getDefaultValue() {
152: String v = anno.defaultValue();
153: if (v.equals("\u0000"))
154: return null;
155: else
156: return v;
157: }
158:
159: public ElementInfoImpl<T, C, F, M> parent() {
160: return ElementInfoImpl.this ;
161: }
162:
163: public String getName() {
164: return "value";
165: }
166:
167: public String displayName() {
168: return "JAXBElement#value";
169: }
170:
171: public boolean isCollection() {
172: return isCollection;
173: }
174:
175: /**
176: * For {@link ElementInfo}s, a collection always means a list of values.
177: */
178: public boolean isValueList() {
179: return isCollection;
180: }
181:
182: public boolean isRequired() {
183: return true;
184: }
185:
186: public PropertyKind kind() {
187: return PropertyKind.ELEMENT;
188: }
189:
190: public Adapter<T, C> getAdapter() {
191: return adapter;
192: }
193:
194: public ID id() {
195: return id;
196: }
197:
198: public MimeType getExpectedMimeType() {
199: return expectedMimeType;
200: }
201:
202: public QName getSchemaType() {
203: return schemaType;
204: }
205:
206: public boolean inlineBinaryData() {
207: return inlineBinary;
208: }
209:
210: public PropertyInfo<T, C> getSource() {
211: return this ;
212: }
213:
214: //
215: //
216: // AnnotationSource impl
217: //
218: //
219: public <A extends Annotation> A readAnnotation(
220: Class<A> annotationType) {
221: return reader().getMethodAnnotation(annotationType, method,
222: ElementInfoImpl.this );
223: }
224:
225: public boolean hasAnnotation(
226: Class<? extends Annotation> annotationType) {
227: return reader().hasMethodAnnotation(annotationType, method);
228: }
229: }
230:
231: /**
232: * @param m
233: * The factory method on ObjectFactory that comes with {@link XmlElementDecl}.
234: */
235: public ElementInfoImpl(ModelBuilder<T, C, F, M> builder,
236: RegistryInfoImpl<T, C, F, M> registry, M m)
237: throws IllegalAnnotationException {
238: super (builder, registry);
239:
240: this .method = m;
241: anno = reader().getMethodAnnotation(XmlElementDecl.class, m,
242: this );
243: assert anno != null; // the caller should check this
244: assert anno instanceof Locatable;
245:
246: elementType = nav().getReturnType(m);
247: T baseClass = nav().getBaseClass(elementType,
248: nav().asDecl(JAXBElement.class));
249: if (baseClass == null)
250: throw new IllegalAnnotationException(
251: Messages.XML_ELEMENT_MAPPING_ON_NON_IXMLELEMENT_METHOD
252: .format(nav().getMethodName(m)), anno);
253:
254: tagName = parseElementName(anno);
255: T[] methodParams = nav().getMethodParameters(m);
256:
257: // adapter
258: Adapter<T, C> a = null;
259: if (methodParams.length > 0) {
260: XmlJavaTypeAdapter adapter = reader().getMethodAnnotation(
261: XmlJavaTypeAdapter.class, m, this );
262: if (adapter != null)
263: a = new Adapter<T, C>(adapter, reader(), nav());
264: else {
265: XmlAttachmentRef xsa = reader().getMethodAnnotation(
266: XmlAttachmentRef.class, m, this );
267: if (xsa != null) {
268: TODO
269: .prototype("in APT swaRefAdapter isn't avaialble, so this returns null");
270: a = new Adapter<T, C>(owner.nav
271: .asDecl(SwaRefAdapter.class), owner.nav);
272: }
273: }
274: }
275: this .adapter = a;
276:
277: if (adapter == null) {
278: // T of JAXBElement<T>
279: T typeType = methodParams.length > 0 ? methodParams[0] // this is more reliable, as it works even for ObjectFactory that sometimes have to return public types
280: : nav().getTypeArgument(baseClass, 0); // fall back to infer from the return type if no parameter.
281: T list = nav().getBaseClass(typeType,
282: nav().asDecl(List.class));
283: if (list == null) {
284: isCollection = false;
285: contentType = builder.getTypeInfo(typeType, this ); // suck this type into the current set.
286: } else {
287: isCollection = true;
288: contentType = builder.getTypeInfo(nav()
289: .getTypeArgument(list, 0), this );
290: }
291: } else {
292: // but if adapted, use the adapted type
293: contentType = builder.getTypeInfo(this .adapter.defaultType,
294: this );
295: isCollection = false;
296: }
297:
298: // scope
299: T s = reader().getClassValue(anno, "scope");
300: if (s.equals(nav().ref(XmlElementDecl.GLOBAL.class)))
301: scope = null;
302: else {
303: // TODO: what happens if there's an error?
304: NonElement<T, C> scp = builder.getClassInfo(
305: nav().asDecl(s), this );
306: if (!(scp instanceof ClassInfo)) {
307: throw new IllegalAnnotationException(
308: Messages.SCOPE_IS_NOT_COMPLEXTYPE.format(nav()
309: .getTypeName(s)), anno);
310: }
311: scope = (ClassInfo<T, C>) scp;
312: }
313:
314: id = calcId();
315:
316: property = createPropertyImpl();
317:
318: this .expectedMimeType = Util.calcExpectedMediaType(property,
319: builder);
320: this .inlineBinary = reader().hasMethodAnnotation(
321: XmlInlineBinaryData.class, method);
322: this .schemaType = Util.calcSchemaType(reader(), property,
323: registry.registryClass, getContentInMemoryType(), this );
324: }
325:
326: final QName parseElementName(XmlElementDecl e) {
327: String local = e.name();
328: String nsUri = e.namespace();
329: if (nsUri.equals("##default")) {
330: // if defaulted ...
331: XmlSchema xs = reader().getPackageAnnotation(
332: XmlSchema.class,
333: nav().getDeclaringClassForMethod(method), this );
334: if (xs != null)
335: nsUri = xs.namespace();
336: else {
337: nsUri = builder.defaultNsUri;
338: }
339: }
340:
341: return new QName(nsUri.intern(), local.intern());
342: }
343:
344: protected PropertyImpl createPropertyImpl() {
345: return new PropertyImpl();
346: }
347:
348: public ElementPropertyInfo<T, C> getProperty() {
349: return property;
350: }
351:
352: public NonElement<T, C> getContentType() {
353: return contentType;
354: }
355:
356: public T getContentInMemoryType() {
357: if (adapter == null) {
358: return contentType.getType();
359: } else {
360: return adapter.customType;
361: }
362: }
363:
364: public QName getElementName() {
365: return tagName;
366: }
367:
368: public T getType() {
369: return elementType;
370: }
371:
372: /**
373: * Leaf-type cannot be referenced from IDREF.
374: *
375: * @deprecated
376: * why are you calling a method whose return value is always known?
377: */
378: public final boolean canBeReferencedByIDREF() {
379: return false;
380: }
381:
382: private ID calcId() {
383: // TODO: share code with PropertyInfoImpl
384: if (reader().hasMethodAnnotation(XmlID.class, method)) {
385: return ID.ID;
386: } else if (reader().hasMethodAnnotation(XmlIDREF.class, method)) {
387: return ID.IDREF;
388: } else {
389: return ID.NONE;
390: }
391: }
392:
393: public ClassInfo<T, C> getScope() {
394: return scope;
395: }
396:
397: public ElementInfo<T, C> getSubstitutionHead() {
398: return substitutionHead;
399: }
400:
401: public Collection<? extends ElementInfoImpl<T, C, F, M>> getSubstitutionMembers() {
402: if (substitutionMembers == null)
403: return Collections.emptyList();
404: else
405: return substitutionMembers;
406: }
407:
408: /**
409: * Called after all the {@link TypeInfo}s are collected into the {@link #owner}.
410: */
411: /*package*/void link() {
412: // substitution head
413: if (anno.substitutionHeadName().length() != 0) {
414: QName name = new QName(anno.substitutionHeadNamespace(),
415: anno.substitutionHeadName());
416: substitutionHead = owner.getElementInfo(null, name);
417: if (substitutionHead == null) {
418: builder.reportError(new IllegalAnnotationException(
419: Messages.NON_EXISTENT_ELEMENT_MAPPING.format(
420: name.getNamespaceURI(), name
421: .getLocalPart()), anno));
422: // recover by ignoring this substitution declaration
423: } else
424: substitutionHead.addSubstitutionMember(this );
425: } else
426: substitutionHead = null;
427: super .link();
428: }
429:
430: private void addSubstitutionMember(ElementInfoImpl<T, C, F, M> child) {
431: if (substitutionMembers == null)
432: substitutionMembers = new FinalArrayList<ElementInfoImpl<T, C, F, M>>();
433: substitutionMembers.add(child);
434: }
435:
436: public Location getLocation() {
437: return nav().getMethodLocation(method);
438: }
439: }
|