1 /*
2  * Copyright (c) 1997, 2011, 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.tools.internal.xjc.model;
27 
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Set;
33 
34 import javax.xml.bind.JAXBElement;
35 import javax.xml.bind.annotation.XmlElement;
36 import javax.xml.namespace.QName;
37 
38 import com.sun.codemodel.internal.JPackage;
39 import com.sun.codemodel.internal.JType;
40 import com.sun.istack.internal.Nullable;
41 import static com.sun.tools.internal.xjc.model.CElementPropertyInfo.CollectionMode.NOT_REPEATED;
42 import static com.sun.tools.internal.xjc.model.CElementPropertyInfo.CollectionMode.REPEATED_VALUE;
43 import com.sun.tools.internal.xjc.model.nav.NClass;
44 import com.sun.tools.internal.xjc.model.nav.NType;
45 import com.sun.tools.internal.xjc.model.nav.NavigatorImpl;
46 import com.sun.tools.internal.xjc.outline.Aspect;
47 import com.sun.tools.internal.xjc.outline.Outline;
48 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIInlineBinaryData;
49 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIFactoryMethod;
50 import com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder;
51 import com.sun.tools.internal.xjc.reader.Ring;
52 import com.sun.xml.internal.bind.v2.model.core.ElementInfo;
53 import com.sun.xml.internal.xsom.XSElementDecl;
54 import com.sun.xml.internal.xsom.XmlString;
55 
56 import org.xml.sax.Locator;
57 
58 /**
59  * {@link ElementInfo} implementation for the compile-time model.
60  *
61  * <p>
62  * As an NType, it represents the Java representation of this element
63  * (either JAXBElement&lt;T> or Foo).
64  *
65  * @author Kohsuke Kawaguchi
66  */
67 public final class CElementInfo extends AbstractCElement
68     implements ElementInfo<NType,NClass>, NType, CClassInfoParent {
69 
70     private final QName tagName;
71 
72     /**
73      * Represents {@code JAXBElement&lt;ContentType>}.
74      */
75     private NType type;
76 
77     /**
78      * If this element produces its own class, the short name of that class.
79      * Otherwise null.
80      */
81     private String className;
82 
83     /**
84      * If this element is global, the element info is considered to be
85      * package-level, and this points to the package in which this element
86      * lives in.
87      *
88      * <p>
89      * For local elements, this points to the parent {@link CClassInfo}.
90      */
91     public final CClassInfoParent parent;
92 
93     private CElementInfo substitutionHead;
94 
95     /**
96      * Lazily computed.
97      */
98     private Set<CElementInfo> substitutionMembers;
99 
100     /**
101      * {@link Model} that owns this object.
102      */
103     private final Model model;
104 
105     private CElementPropertyInfo property;
106 
107     /**
108      * Custom {@link #getSqueezedName() squeezed name}, if any.
109      */
110     private /*almost final*/ @Nullable String squeezedName;
111 
112     /**
113      * Creates an element in the given parent.
114      *
115      * <p>
116      * When using this construction, {@link #initContentType(TypeUse, XSElementDecl, XmlString)}
117      * must not be invoked.
118      */
CElementInfo(Model model,QName tagName, CClassInfoParent parent, TypeUse contentType, XmlString defaultValue, XSElementDecl source, CCustomizations customizations, Locator location )119     public CElementInfo(Model model,QName tagName, CClassInfoParent parent, TypeUse contentType, XmlString defaultValue, XSElementDecl source, CCustomizations customizations, Locator location ) {
120         super(model,source,location,customizations);
121         this.tagName = tagName;
122         this.model = model;
123         this.parent = parent;
124         if(contentType!=null)
125             initContentType(contentType, source, defaultValue);
126 
127         model.add(this);
128     }
129 
130     /**
131      * Creates an element with a class in the given parent.
132      *
133      * <p>
134      * When using this construction, the caller must use
135      * {@link #initContentType(TypeUse, XSElementDecl, XmlString)} to fill in the content type
136      * later.
137      *
138      * This is to avoid a circular model construction dependency between buidling a type
139      * inside an element and element itself. To build a content type, you need to have
140      * {@link CElementInfo} for a parent, so we can't take it as a constructor parameter.
141      */
CElementInfo(Model model,QName tagName, CClassInfoParent parent, String className, CCustomizations customizations, Locator location )142     public CElementInfo(Model model,QName tagName, CClassInfoParent parent, String className, CCustomizations customizations, Locator location ) {
143         this(model,tagName,parent,null,null,null,customizations,location);
144         this.className = className;
145     }
146 
initContentType(TypeUse contentType, @Nullable XSElementDecl source, XmlString defaultValue)147     public void initContentType(TypeUse contentType, @Nullable XSElementDecl source, XmlString defaultValue) {
148         assert this.property==null; // must not be called twice
149 
150         this.property = new CElementPropertyInfo("Value",
151                 contentType.isCollection()?REPEATED_VALUE:NOT_REPEATED,
152                 contentType.idUse(),
153                 contentType.getExpectedMimeType(),
154                 source,null,getLocator(),true);
155         this.property.setAdapter(contentType.getAdapterUse());
156         BIInlineBinaryData.handle(source,property);
157         property.getTypes().add(new CTypeRef(contentType.getInfo(),tagName,CTypeRef.getSimpleTypeName(source), true,defaultValue));
158         this.type = NavigatorImpl.createParameterizedType(
159             NavigatorImpl.theInstance.ref(JAXBElement.class),
160             getContentInMemoryType() );
161 
162         BIFactoryMethod factoryMethod = Ring.get(BGMBuilder.class).getBindInfo(source).get(BIFactoryMethod.class);
163         if(factoryMethod!=null) {
164             factoryMethod.markAsAcknowledged();
165             this.squeezedName = factoryMethod.name;
166         }
167 
168     }
169 
getDefaultValue()170     public final String getDefaultValue() {
171         return getProperty().getTypes().get(0).getDefaultValue();
172     }
173 
_package()174     public final JPackage _package() {
175         return parent.getOwnerPackage();
176     }
177 
getContentType()178     public CNonElement getContentType() {
179         return getProperty().ref().get(0);
180     }
181 
getContentInMemoryType()182     public NType getContentInMemoryType() {
183         if(getProperty().getAdapter()==null) {
184             NType itemType = getContentType().getType();
185             if(!property.isCollection())
186                 return itemType;
187 
188             return NavigatorImpl.createParameterizedType(List.class,itemType);
189         } else {
190             return getProperty().getAdapter().customType;
191         }
192     }
193 
getProperty()194     public CElementPropertyInfo getProperty() {
195         return property;
196     }
197 
getScope()198     public CClassInfo getScope() {
199         if(parent instanceof CClassInfo)
200             return (CClassInfo)parent;
201         return null;
202     }
203 
204     /**
205      * @deprecated why are you calling a method that returns this?
206      */
getType()207     public NType getType() {
208         return this;
209     }
210 
getElementName()211     public QName getElementName() {
212         return tagName;
213     }
214 
toType(Outline o, Aspect aspect)215     public JType toType(Outline o, Aspect aspect) {
216         if(className==null)
217             return type.toType(o,aspect);
218         else
219             return o.getElement(this).implClass;
220     }
221 
222     /**
223      * Returns the "squeezed name" of this element.
224      *
225      * @see CClassInfo#getSqueezedName()
226      */
227     @XmlElement
getSqueezedName()228     public String getSqueezedName() {
229         if(squeezedName!=null)  return squeezedName;
230 
231         StringBuilder b = new StringBuilder();
232         CClassInfo s = getScope();
233         if(s!=null)
234             b.append(s.getSqueezedName());
235         if(className!=null)
236             b.append(className);
237         else
238             b.append( model.getNameConverter().toClassName(tagName.getLocalPart()));
239         return b.toString();
240     }
241 
getSubstitutionHead()242     public CElementInfo getSubstitutionHead() {
243         return substitutionHead;
244     }
245 
getSubstitutionMembers()246     public Collection<CElementInfo> getSubstitutionMembers() {
247         if(substitutionMembers==null)
248             return Collections.emptyList();
249         else
250             return substitutionMembers;
251     }
252 
setSubstitutionHead(CElementInfo substitutionHead)253     public void setSubstitutionHead(CElementInfo substitutionHead) {
254         // don't set it twice
255         assert this.substitutionHead==null;
256         assert substitutionHead!=null;
257         this.substitutionHead = substitutionHead;
258 
259         if(substitutionHead.substitutionMembers==null)
260             substitutionHead.substitutionMembers = new HashSet<CElementInfo>();
261         substitutionHead.substitutionMembers.add(this);
262     }
263 
isBoxedType()264     public boolean isBoxedType() {
265         return false;
266     }
267 
fullName()268     public String fullName() {
269         if(className==null)
270             return type.fullName();
271         else {
272             String r = parent.fullName();
273             if(r.length()==0)   return className;
274             else                return r+'.'+className;
275         }
276     }
277 
accept(Visitor<T> visitor)278     public <T> T accept(Visitor<T> visitor) {
279         return visitor.onElement(this);
280     }
281 
getOwnerPackage()282     public JPackage getOwnerPackage() {
283         return parent.getOwnerPackage();
284     }
285 
shortName()286     public String shortName() {
287         return className;
288     }
289 
290     /**
291      * True if this element has its own class
292      * (as opposed to be represented as an instance of {@link JAXBElement}.
293      */
hasClass()294     public boolean hasClass() {
295         return className!=null;
296     }
297 }
298