1 /*
   2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.xml.internal.bind.v2.model.impl;
  27 
  28 import java.lang.annotation.Annotation;
  29 import java.util.Collection;
  30 import java.util.Collections;
  31 import java.util.List;
  32 
  33 import javax.activation.MimeType;
  34 import javax.xml.bind.JAXBElement;
  35 import javax.xml.bind.annotation.XmlAttachmentRef;
  36 import javax.xml.bind.annotation.XmlElementDecl;
  37 import javax.xml.bind.annotation.XmlID;
  38 import javax.xml.bind.annotation.XmlIDREF;
  39 import javax.xml.bind.annotation.XmlInlineBinaryData;
  40 import javax.xml.bind.annotation.XmlSchema;
  41 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
  42 import javax.xml.namespace.QName;
  43 
  44 import com.sun.istack.internal.FinalArrayList;
  45 import com.sun.xml.internal.bind.v2.TODO;
  46 import com.sun.xml.internal.bind.v2.model.annotation.AnnotationSource;
  47 import com.sun.xml.internal.bind.v2.model.annotation.Locatable;
  48 import com.sun.xml.internal.bind.v2.model.core.Adapter;
  49 import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
  50 import com.sun.xml.internal.bind.v2.model.core.ElementInfo;
  51 import com.sun.xml.internal.bind.v2.model.core.ElementPropertyInfo;
  52 import com.sun.xml.internal.bind.v2.model.core.ID;
  53 import com.sun.xml.internal.bind.v2.model.core.NonElement;
  54 import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
  55 import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
  56 import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
  57 import com.sun.xml.internal.bind.v2.model.core.TypeRef;
  58 import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
  59 import com.sun.xml.internal.bind.v2.runtime.Location;
  60 import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter;
  61 
  62 /**
  63  * {@link ElementInfo} implementation.
  64  *
  65  * @author Kohsuke Kawaguchi
  66  */
  67 class ElementInfoImpl<T,C,F,M> extends TypeInfoImpl<T,C,F,M> implements ElementInfo<T,C> {
  68 
  69     private final QName tagName;
  70 
  71     private final NonElement<T,C> contentType;
  72 
  73     private final T tOfJAXBElementT;
  74 
  75     private final T elementType;
  76 
  77     private final ClassInfo<T,C> scope;
  78 
  79     /**
  80      * Annotation that controls the binding.
  81      */
  82     private final XmlElementDecl anno;
  83 
  84     /**
  85      * If this element can substitute another element, the element name.
  86      * @see #link()
  87      */
  88     private ElementInfoImpl<T,C,F,M> substitutionHead;
  89 
  90     /**
  91      * Lazily constructed list of {@link ElementInfo}s that can substitute this element.
  92      * This could be null.
  93      * @see #link()
  94      */
  95     private FinalArrayList<ElementInfoImpl<T,C,F,M>> substitutionMembers;
  96 
  97     /**
  98      * The factory method from which this mapping was created.
  99      */
 100     private final M method;
 101 
 102     /**
 103      * If the content type is adapter, return that adapter.
 104      */
 105     private final Adapter<T,C> adapter;
 106 
 107     private final boolean isCollection;
 108 
 109     private final ID id;
 110 
 111     private final PropertyImpl property;
 112     private final MimeType expectedMimeType;
 113     private final boolean inlineBinary;
 114     private final QName schemaType;
 115 
 116     /**
 117      * Singleton instance of {@link ElementPropertyInfo} for this element.
 118      */
 119     protected class PropertyImpl implements
 120             ElementPropertyInfo<T,C>,
 121             TypeRef<T,C>,
 122             AnnotationSource {
 123         //
 124         // TypeRef impl
 125         //
 126         public NonElement<T,C> getTarget() {
 127             return contentType;
 128         }
 129         public QName getTagName() {
 130             return tagName;
 131         }
 132 
 133         public List<? extends TypeRef<T,C>> getTypes() {
 134             return Collections.singletonList(this);
 135         }
 136 
 137         public List<? extends NonElement<T,C>> ref() {
 138             return Collections.singletonList(contentType);
 139         }
 140 
 141         public QName getXmlName() {
 142             return tagName;
 143         }
 144 
 145         public boolean isCollectionRequired() {
 146             return false;
 147         }
 148 
 149         public boolean isCollectionNillable() {
 150             return true;
 151         }
 152 
 153         public boolean isNillable() {
 154             return true;
 155         }
 156 
 157         public String getDefaultValue() {
 158             String v = anno.defaultValue();
 159             if(v.equals("\u0000"))
 160                 return null;
 161             else
 162                 return v;
 163         }
 164 
 165         public ElementInfoImpl<T,C,F,M> parent() {
 166             return ElementInfoImpl.this;
 167         }
 168 
 169         public String getName() {
 170             return "value";
 171         }
 172 
 173         public String displayName() {
 174             return "JAXBElement#value";
 175         }
 176 
 177         public boolean isCollection() {
 178             return isCollection;
 179         }
 180 
 181         /**
 182          * For {@link ElementInfo}s, a collection always means a list of values.
 183          */
 184         public boolean isValueList() {
 185             return isCollection;
 186         }
 187 
 188         public boolean isRequired() {
 189             return true;
 190         }
 191 
 192         public PropertyKind kind() {
 193             return PropertyKind.ELEMENT;
 194         }
 195 
 196         public Adapter<T,C> getAdapter() {
 197             return adapter;
 198         }
 199 
 200         public ID id() {
 201             return id;
 202         }
 203 
 204         public MimeType getExpectedMimeType() {
 205             return expectedMimeType;
 206         }
 207 
 208         public QName getSchemaType() {
 209             return schemaType;
 210         }
 211 
 212         public boolean inlineBinaryData() {
 213             return inlineBinary;
 214         }
 215 
 216         public PropertyInfo<T,C> getSource() {
 217             return this;
 218         }
 219 
 220         //
 221         //
 222         // AnnotationSource impl
 223         //
 224         //
 225         public <A extends Annotation> A readAnnotation(Class<A> annotationType) {
 226             return reader().getMethodAnnotation(annotationType,method,ElementInfoImpl.this);
 227         }
 228 
 229         public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
 230             return reader().hasMethodAnnotation(annotationType,method);
 231         }
 232     }
 233 
 234     /**
 235      * @param m
 236      *      The factory method on ObjectFactory that comes with {@link XmlElementDecl}.
 237      */
 238     public ElementInfoImpl(ModelBuilder<T,C,F,M> builder,
 239                            RegistryInfoImpl<T,C,F,M> registry, M m ) throws IllegalAnnotationException {
 240         super(builder,registry);
 241 
 242         this.method = m;
 243         anno = reader().getMethodAnnotation( XmlElementDecl.class, m, this );
 244         assert anno!=null;  // the caller should check this
 245         assert anno instanceof Locatable;
 246 
 247         elementType = nav().getReturnType(m);
 248         T baseClass = nav().getBaseClass(elementType,nav().asDecl(JAXBElement.class));
 249         if(baseClass==null)
 250             throw new IllegalAnnotationException(
 251                 Messages.XML_ELEMENT_MAPPING_ON_NON_IXMLELEMENT_METHOD.format(nav().getMethodName(m)),
 252                 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(XmlJavaTypeAdapter.class,m,this);
 261             if(adapter!=null)
 262                 a = new Adapter<T,C>(adapter,reader(),nav());
 263             else {
 264                 XmlAttachmentRef xsa = reader().getMethodAnnotation(XmlAttachmentRef.class,m,this);
 265                 if(xsa!=null) {
 266                     TODO.prototype("in Annotation Processing swaRefAdapter isn't avaialble, so this returns null");
 267                     a = new Adapter<T,C>(owner.nav.asDecl(SwaRefAdapter.class),owner.nav);
 268                 }
 269             }
 270         }
 271         this.adapter = a;
 272 
 273         // T of JAXBElement<T>
 274         tOfJAXBElementT =
 275             methodParams.length>0 ? methodParams[0] // this is more reliable, as it works even for ObjectFactory that sometimes have to return public types
 276             : nav().getTypeArgument(baseClass,0); // fall back to infer from the return type if no parameter.
 277 
 278         if(adapter==null) {
 279             T list = nav().getBaseClass(tOfJAXBElementT,nav().asDecl(List.class));
 280             if(list==null) {
 281                 isCollection = false;
 282                 contentType = builder.getTypeInfo(tOfJAXBElementT,this);  // suck this type into the current set.
 283             } else {
 284                 isCollection = true;
 285                 contentType = builder.getTypeInfo(nav().getTypeArgument(list,0),this);
 286             }
 287         } else {
 288             // but if adapted, use the adapted type
 289             contentType = builder.getTypeInfo(this.adapter.defaultType,this);
 290             isCollection = false;
 291         }
 292 
 293         // scope
 294         T s = reader().getClassValue(anno,"scope");
 295         if(nav().isSameType(s, nav().ref(XmlElementDecl.GLOBAL.class)))
 296             scope = null;
 297         else {
 298             // TODO: what happens if there's an error?
 299             NonElement<T,C> scp = builder.getClassInfo(nav().asDecl(s),this);
 300             if(!(scp instanceof ClassInfo)) {
 301                 throw new IllegalAnnotationException(
 302                     Messages.SCOPE_IS_NOT_COMPLEXTYPE.format(nav().getTypeName(s)),
 303                     anno );
 304             }
 305             scope = (ClassInfo<T,C>)scp;
 306         }
 307 
 308         id = calcId();
 309 
 310         property = createPropertyImpl();
 311 
 312         this.expectedMimeType = Util.calcExpectedMediaType(property,builder);
 313         this.inlineBinary = reader().hasMethodAnnotation(XmlInlineBinaryData.class,method);
 314         this.schemaType = Util.calcSchemaType(reader(),property,registry.registryClass,
 315                 getContentInMemoryType(),this);
 316     }
 317 
 318     final QName parseElementName(XmlElementDecl e) {
 319         String local = e.name();
 320         String nsUri = e.namespace();
 321         if(nsUri.equals("##default")) {
 322             // if defaulted ...
 323             XmlSchema xs = reader().getPackageAnnotation(XmlSchema.class,
 324                 nav().getDeclaringClassForMethod(method),this);
 325             if(xs!=null)
 326                 nsUri = xs.namespace();
 327             else {
 328                 nsUri = builder.defaultNsUri;
 329             }
 330         }
 331 
 332         return new QName(nsUri.intern(),local.intern());
 333     }
 334 
 335     protected PropertyImpl createPropertyImpl() {
 336         return new PropertyImpl();
 337     }
 338 
 339     public ElementPropertyInfo<T,C> getProperty() {
 340         return property;
 341     }
 342 
 343     public NonElement<T,C> getContentType() {
 344         return contentType;
 345     }
 346 
 347     public T getContentInMemoryType() {
 348         if(adapter==null) {
 349             return tOfJAXBElementT;
 350         } else {
 351             return adapter.customType;
 352         }
 353     }
 354 
 355     public QName getElementName() {
 356         return tagName;
 357     }
 358 
 359     public T getType() {
 360         return elementType;
 361     }
 362 
 363     /**
 364      * Leaf-type cannot be referenced from IDREF.
 365      *
 366      * @deprecated
 367      *      why are you calling a method whose return value is always known?
 368      */
 369     public final boolean canBeReferencedByIDREF() {
 370         return false;
 371     }
 372 
 373     private ID calcId() {
 374         // TODO: share code with PropertyInfoImpl
 375         if(reader().hasMethodAnnotation(XmlID.class,method)) {
 376             return ID.ID;
 377         } else
 378         if(reader().hasMethodAnnotation(XmlIDREF.class,method)) {
 379             return ID.IDREF;
 380         } else {
 381             return ID.NONE;
 382         }
 383     }
 384 
 385     public ClassInfo<T, C> getScope() {
 386         return scope;
 387     }
 388 
 389     public ElementInfo<T,C> getSubstitutionHead() {
 390         return substitutionHead;
 391     }
 392 
 393     public Collection<? extends ElementInfoImpl<T,C,F,M>> getSubstitutionMembers() {
 394         if(substitutionMembers==null)
 395             return Collections.emptyList();
 396         else
 397             return substitutionMembers;
 398     }
 399 
 400     /**
 401      * Called after all the {@link TypeInfo}s are collected into the {@link #owner}.
 402      */
 403     /*package*/ void link() {
 404         // substitution head
 405         if(anno.substitutionHeadName().length()!=0) {
 406             QName name = new QName(
 407                 anno.substitutionHeadNamespace(), anno.substitutionHeadName() );
 408             substitutionHead = owner.getElementInfo(null,name);
 409             if(substitutionHead==null) {
 410                 builder.reportError(
 411                     new IllegalAnnotationException(Messages.NON_EXISTENT_ELEMENT_MAPPING.format(
 412                         name.getNamespaceURI(),name.getLocalPart()), anno));
 413                 // recover by ignoring this substitution declaration
 414             } else
 415                 substitutionHead.addSubstitutionMember(this);
 416         } else
 417             substitutionHead = null;
 418         super.link();
 419     }
 420 
 421     private void addSubstitutionMember(ElementInfoImpl<T,C,F,M> child) {
 422         if(substitutionMembers==null)
 423             substitutionMembers = new FinalArrayList<ElementInfoImpl<T,C,F,M>>();
 424         substitutionMembers.add(child);
 425     }
 426 
 427     public Location getLocation() {
 428         return nav().getMethodLocation(method);
 429     }
 430 }