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.util.AbstractList;
  29 import java.util.Collections;
  30 import java.util.List;
  31 
  32 import javax.xml.bind.annotation.XmlElement;
  33 import javax.xml.bind.annotation.XmlElements;
  34 import javax.xml.bind.annotation.XmlList;
  35 import javax.xml.namespace.QName;
  36 
  37 import com.sun.istack.internal.FinalArrayList;
  38 import com.sun.xml.internal.bind.v2.model.core.ElementPropertyInfo;
  39 import com.sun.xml.internal.bind.v2.model.core.ID;
  40 import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
  41 import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
  42 import com.sun.xml.internal.bind.v2.model.core.TypeRef;
  43 import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
  44 
  45 /**
  46  * Common {@link ElementPropertyInfo} implementation used for both
  47  * Annotation Processing and runtime.
  48  *
  49  * @author Kohsuke Kawaguchi
  50  */
  51 class ElementPropertyInfoImpl<TypeT,ClassDeclT,FieldT,MethodT>
  52     extends ERPropertyInfoImpl<TypeT,ClassDeclT,FieldT,MethodT>
  53     implements ElementPropertyInfo<TypeT,ClassDeclT>
  54 {
  55     /**
  56      * Lazily computed.
  57      * @see #getTypes()
  58      */
  59     private List<TypeRefImpl<TypeT,ClassDeclT>> types;
  60 
  61     private final List<TypeInfo<TypeT,ClassDeclT>> ref = new AbstractList<TypeInfo<TypeT,ClassDeclT>>() {
  62         public TypeInfo<TypeT,ClassDeclT> get(int index) {
  63             return getTypes().get(index).getTarget();
  64         }
  65 
  66         public int size() {
  67             return getTypes().size();
  68         }
  69     };
  70 
  71     /**
  72      * Lazily computed.
  73      * @see #isRequired()
  74      */
  75     private Boolean isRequired;
  76 
  77     /**
  78      * @see #isValueList()
  79      */
  80     private final boolean isValueList;
  81 
  82     ElementPropertyInfoImpl(
  83         ClassInfoImpl<TypeT,ClassDeclT,FieldT,MethodT> parent,
  84         PropertySeed<TypeT,ClassDeclT,FieldT,MethodT> propertySeed) {
  85         super(parent, propertySeed);
  86 
  87         isValueList = seed.hasAnnotation(XmlList.class);
  88 
  89     }
  90 
  91     public List<? extends TypeRefImpl<TypeT,ClassDeclT>> getTypes() {
  92         if(types==null) {
  93             types = new FinalArrayList<TypeRefImpl<TypeT,ClassDeclT>>();
  94             XmlElement[] ann=null;
  95 
  96             XmlElement xe = seed.readAnnotation(XmlElement.class);
  97             XmlElements xes = seed.readAnnotation(XmlElements.class);
  98 
  99             if(xe!=null && xes!=null) {
 100                 parent.builder.reportError(new IllegalAnnotationException(
 101                         Messages.MUTUALLY_EXCLUSIVE_ANNOTATIONS.format(
 102                                 nav().getClassName(parent.getClazz())+'#'+seed.getName(),
 103                                 xe.annotationType().getName(), xes.annotationType().getName()),
 104                         xe, xes ));
 105             }
 106 
 107             isRequired = true;
 108 
 109             if(xe!=null)
 110                 ann = new XmlElement[]{xe};
 111             else
 112             if(xes!=null)
 113                 ann = xes.value();
 114 
 115             if(ann==null) {
 116                 // default
 117                 TypeT t = getIndividualType();
 118                 if(!nav().isPrimitive(t) || isCollection())
 119                     isRequired = false;
 120                 // nillableness defaults to true if it's collection
 121                 types.add(createTypeRef(calcXmlName((XmlElement)null),t,isCollection(),null));
 122             } else {
 123                 for( XmlElement item : ann ) {
 124                     // TODO: handle defaulting in names.
 125                     QName name = calcXmlName(item);
 126                     TypeT type = reader().getClassValue(item, "type");
 127                     if (nav().isSameType(type, nav().ref(XmlElement.DEFAULT.class)))
 128                         type = getIndividualType();
 129                     if((!nav().isPrimitive(type) || isCollection()) && !item.required())
 130                         isRequired = false;
 131                     types.add(createTypeRef(name, type, item.nillable(), getDefaultValue(item.defaultValue()) ));
 132                 }
 133             }
 134             types = Collections.unmodifiableList(types);
 135             assert !types.contains(null);
 136         }
 137         return types;
 138     }
 139 
 140     private String getDefaultValue(String value) {
 141         if(value.equals("\u0000"))
 142             return null;
 143         else
 144             return value;
 145     }
 146 
 147     /**
 148      * Used by {@link PropertyInfoImpl} to create new instances of {@link TypeRef}
 149      */
 150     protected TypeRefImpl<TypeT,ClassDeclT> createTypeRef(QName name,TypeT type,boolean isNillable,String defaultValue) {
 151         return new TypeRefImpl<TypeT,ClassDeclT>(this,name,type,isNillable,defaultValue);
 152     }
 153 
 154     public boolean isValueList() {
 155         return isValueList;
 156     }
 157 
 158     public boolean isRequired() {
 159         if(isRequired==null)
 160             getTypes(); // compute the value
 161         return isRequired;
 162     }
 163 
 164     public List<? extends TypeInfo<TypeT,ClassDeclT>> ref() {
 165         return ref;
 166     }
 167 
 168     public final PropertyKind kind() {
 169         return PropertyKind.ELEMENT;
 170     }
 171 
 172     protected void link() {
 173         super.link();
 174         for (TypeRefImpl<TypeT, ClassDeclT> ref : getTypes() ) {
 175             ref.link();
 176         }
 177 
 178         if(isValueList()) {
 179             // ugly test, because IDREF's are represented as text on the wire,
 180             // it's OK to be a value list in that case.
 181             if(id()!= ID.IDREF) {
 182                 // check if all the item types are simple types
 183                 // this can't be done when we compute types because
 184                 // not all TypeInfos are available yet
 185                 for (TypeRefImpl<TypeT,ClassDeclT> ref : types) {
 186                     if(!ref.getTarget().isSimpleType()) {
 187                         parent.builder.reportError(new IllegalAnnotationException(
 188                         Messages.XMLLIST_NEEDS_SIMPLETYPE.format(
 189                             nav().getTypeName(ref.getTarget().getType())), this ));
 190                         break;
 191                     }
 192                 }
 193             }
 194 
 195             if(!isCollection())
 196                 parent.builder.reportError(new IllegalAnnotationException(
 197                     Messages.XMLLIST_ON_SINGLE_PROPERTY.format(), this
 198                 ));
 199         }
 200     }
 201 }