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