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
26package com.sun.xml.internal.bind.v2.model.impl;
27
28import java.util.AbstractList;
29import java.util.Collections;
30import java.util.List;
31
32import javax.xml.bind.annotation.XmlElement;
33import javax.xml.bind.annotation.XmlElements;
34import javax.xml.bind.annotation.XmlList;
35import javax.xml.namespace.QName;
36
37import com.sun.istack.internal.FinalArrayList;
38import com.sun.xml.internal.bind.v2.model.core.ElementPropertyInfo;
39import com.sun.xml.internal.bind.v2.model.core.ID;
40import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
41import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
42import com.sun.xml.internal.bind.v2.model.core.TypeRef;
43import 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 */
51class 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}
202