View Javadoc
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.ArrayList;
29  import java.util.Collection;
30  import java.util.HashSet;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Set;
34  
35  import javax.xml.bind.annotation.XmlElement;
36  import javax.xml.bind.annotation.XmlID;
37  import javax.xml.bind.annotation.XmlIDREF;
38  import javax.xml.bind.annotation.XmlRootElement;
39  import javax.xml.namespace.QName;
40  
41  import com.sun.codemodel.internal.JClass;
42  import com.sun.codemodel.internal.JCodeModel;
43  import com.sun.codemodel.internal.JPackage;
44  import com.sun.istack.internal.Nullable;
45  import com.sun.tools.internal.xjc.Language;
46  import com.sun.tools.internal.xjc.model.nav.NClass;
47  import com.sun.tools.internal.xjc.model.nav.NType;
48  import com.sun.tools.internal.xjc.outline.Aspect;
49  import com.sun.tools.internal.xjc.outline.Outline;
50  import com.sun.tools.internal.xjc.reader.Ring;
51  import com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder;
52  import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIFactoryMethod;
53  import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
54  import com.sun.xml.internal.bind.v2.model.core.Element;
55  import com.sun.xml.internal.xsom.XSComponent;
56  
57  import org.xml.sax.Locator;
58  
59  /**
60   * Mutable {@link ClassInfo} represenatation.
61   *
62   * <p>
63   * Schema parsers build these objects.
64   *
65   * @author Kohsuke Kawaguchi
66   */
67  public final class CClassInfo extends AbstractCElement implements ClassInfo<NType,NClass>, CClassInfoParent, CClass, NClass {
68  
69      @XmlIDREF
70      private CClass baseClass;
71  
72      /**
73       * List of all subclasses, together with {@link #nextSibling}.
74       *
75       * If this class has no sub-class, this field is null. Otherwise,
76       * this field points to a sub-class of this class. From there you can enumerate
77       * all the sub-classes by using {@link #nextSibling}.
78       */
79      private CClassInfo firstSubclass;
80  
81      /**
82       * @see #firstSubclass
83       */
84      private CClassInfo nextSibling = null;
85  
86      /**
87       * @see #getTypeName()
88       */
89      private final QName typeName;
90  
91      /**
92       * Custom {@link #getSqueezedName() squeezed name}, if any.
93       */
94      private /*almost final*/ @Nullable String squeezedName;
95  
96      /**
97       * If this class also gets {@link XmlRootElement}, the class name.
98       */
99      private final @Nullable QName elementName;
100 
101     private boolean isOrdered = true;
102 
103     private final List<CPropertyInfo> properties = new ArrayList<CPropertyInfo>();
104 
105     /**
106      * TODO: revisit this design.
107      * we should at least do a basic encapsulation to avoid careless
108      * mistakes. Maybe we should even differ the javadoc generation
109      * by queueing runners.
110      */
111     public String javadoc;
112 
113     @XmlIDREF
114     private final CClassInfoParent parent;
115 
116     /**
117      * short name.
118      */
119     public final String shortName;
120 
121     /**
122      * Optional user-specified implementation override class.
123      */
124     private @Nullable String implClass;
125 
126     /**
127      * The {@link Model} object to which this bean belongs.
128      */
129     public final Model model;
130 
131     /**
132      * @see #hasAttributeWildcard()
133      */
134     private boolean hasAttributeWildcard;
135 
136 
137     public CClassInfo(Model model,JPackage pkg, String shortName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) {
138         this(model,model.getPackage(pkg),shortName,location,typeName,elementName,source,customizations);
139     }
140 
141     public CClassInfo(Model model,CClassInfoParent p, String shortName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) {
142         super(model,source,location,customizations);
143         this.model = model;
144         this.parent = p;
145         this.shortName = model.allocator.assignClassName(parent,shortName);
146         this.typeName = typeName;
147         this.elementName = elementName;
148 
149         Language schemaLanguage = model.options.getSchemaLanguage();
150         if ((schemaLanguage != null) &&
151             (schemaLanguage.equals(Language.XMLSCHEMA) || schemaLanguage.equals(Language.WSDL))) {
152             BIFactoryMethod factoryMethod = Ring.get(BGMBuilder.class).getBindInfo(source).get(BIFactoryMethod.class);
153             if(factoryMethod!=null) {
154                 factoryMethod.markAsAcknowledged();
155                 this.squeezedName = factoryMethod.name;
156             }
157         }
158 
159         model.add(this);
160     }
161 
162     public CClassInfo(Model model,JCodeModel cm, String fullName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) {
163         super(model,source,location,customizations);
164         this.model = model;
165         int idx = fullName.indexOf('.');
166         if(idx<0) {
167             this.parent = model.getPackage(cm.rootPackage());
168             this.shortName = model.allocator.assignClassName(parent,fullName);
169         } else {
170             this.parent = model.getPackage(cm._package(fullName.substring(0,idx)));
171             this.shortName = model.allocator.assignClassName(parent,fullName.substring(idx+1));
172         }
173         this.typeName = typeName;
174         this.elementName = elementName;
175 
176         model.add(this);
177     }
178 
179     public boolean hasAttributeWildcard() {
180         return hasAttributeWildcard;
181     }
182 
183     public void hasAttributeWildcard(boolean hasAttributeWildcard) {
184         this.hasAttributeWildcard = hasAttributeWildcard;
185     }
186 
187     public boolean hasSubClasses() {
188         return firstSubclass!=null;
189     }
190 
191     /**
192      * Returns true if a new attribute wildcard property needs to be
193      * declared on this class.
194      */
195     public boolean declaresAttributeWildcard() {
196         return hasAttributeWildcard && !inheritsAttributeWildcard();
197     }
198 
199     /**
200      * Returns true if this class inherits a wildcard attribute property
201      * from its ancestor classes.
202      */
203     public boolean inheritsAttributeWildcard() {
204         if (getRefBaseClass() != null) {
205             CClassRef cref = (CClassRef)baseClass;
206             if (cref.getSchemaComponent().getForeignAttributes().size() > 0) {
207                 return true;
208             }
209         } else {
210             for( CClassInfo c=getBaseClass(); c!=null; c=c.getBaseClass() ) {
211                 if(c.hasAttributeWildcard)
212                     return true;
213             }
214         }
215         return false;
216     }
217 
218 
219     public NClass getClazz() {
220         return this;
221     }
222 
223     public CClassInfo getScope() {
224         return null;
225     }
226 
227     @XmlID
228     public String getName() {
229         return fullName();
230     }
231 
232     /**
233      * Returns the "squeezed name" of this bean token.
234      * <p>
235      * The squeezed name of a bean is the concatenation of
236      * the names of its outer classes and itself.
237      * <p>
238      * Thus if the bean is "org.acme.foo.Bean", then the squeezed name is "Bean",
239      * if the bean is "org.acme.foo.Outer1.Outer2.Bean", then "Outer1Outer2Bean".
240      * <p>
241      * This is used by the code generator
242      */
243     @XmlElement
244     public String getSqueezedName() {
245         if (squeezedName != null)  return squeezedName;
246         return calcSqueezedName.onBean(this);
247     }
248 
249     private static final CClassInfoParent.Visitor<String> calcSqueezedName = new Visitor<String>() {
250         public String onBean(CClassInfo bean) {
251             return bean.parent.accept(this)+bean.shortName;
252         }
253 
254         public String onElement(CElementInfo element) {
255             return element.parent.accept(this)+element.shortName();
256         }
257 
258         public String onPackage(JPackage pkg) {
259             return "";
260         }
261     };
262 
263     /**
264      * Returns a mutable list.
265      */
266     public List<CPropertyInfo> getProperties() {
267         return properties;
268     }
269 
270     public boolean hasValueProperty() {
271         throw new UnsupportedOperationException();
272     }
273 
274     /**
275      * Gets a propery by name.
276      */
277     public CPropertyInfo getProperty(String name) {
278         // TODO: does this method need to be fast?
279         for( CPropertyInfo p : properties )
280             if(p.getName(false).equals(name))
281                 return p;
282         return null;
283     }
284 
285     public boolean hasProperties() {
286         return !getProperties().isEmpty();
287     }
288 
289     public boolean isElement() {
290         return elementName!=null;
291     }
292 
293     /**
294      * Guaranteed to return this.
295      */
296     @Deprecated
297     public CNonElement getInfo() {
298         return this;
299     }
300 
301     public Element<NType,NClass> asElement() {
302         if(isElement())
303             return this;
304         else
305             return null;
306     }
307 
308     public boolean isOrdered() {
309         return isOrdered;
310     }
311 
312     /**
313      * @deprecated
314      *      if you are calling this method directly, you must be doing something wrong.
315      */
316     public boolean isFinal() {
317         return false;
318     }
319 
320     public void setOrdered(boolean value) {
321         isOrdered = value;
322     }
323 
324     public QName getElementName() {
325         return elementName;
326     }
327 
328     public QName getTypeName() {
329         return typeName;
330     }
331 
332     public boolean isSimpleType() {
333         throw new UnsupportedOperationException();
334     }
335 
336     /**
337      * Returns the FQCN of this bean.
338      */
339     public String fullName() {
340         String r = parent.fullName();
341         if(r.length()==0)   return shortName;
342         else                return r+'.'+shortName;
343     }
344 
345     public CClassInfoParent parent() {
346         return parent;
347     }
348 
349     public void setUserSpecifiedImplClass(String implClass) {
350         assert this.implClass==null;
351         assert implClass!=null;
352         this.implClass = implClass;
353     }
354 
355     public String getUserSpecifiedImplClass() {
356         return implClass;
357     }
358 
359 
360     /**
361      * Adds a new property.
362      */
363     public void addProperty(CPropertyInfo prop) {
364         if(prop.ref().isEmpty())
365             // this property isn't contributing anything
366             // this happens when you try to map an empty sequence to a property
367             return;
368         prop.setParent(this);
369         properties.add(prop);
370     }
371 
372     /**
373      * This method accepts both {@link CClassInfo} (which means the base class
374      * is also generated), or {@link CClassRef} (which means the base class is
375      * already generated and simply referenced.)
376      *
377      * The latter is treated somewhat special --- from the rest of the model
378      * this external base class is invisible. This modeling might need more
379      * thoughts to get right.
380      */
381     public void setBaseClass(CClass base) {
382         assert baseClass==null;
383         assert base!=null;
384         baseClass = base;
385 
386         assert nextSibling==null;
387         if (base instanceof CClassInfo) {
388             CClassInfo realBase = (CClassInfo) base;
389             this.nextSibling = realBase.firstSubclass;
390             realBase.firstSubclass = this;
391         }
392     }
393 
394     /**
395      * This inherited version returns null if this class extends from {@link CClassRef}.
396      *
397      * @see #getRefBaseClass()
398      */
399     public CClassInfo getBaseClass() {
400         if (baseClass instanceof CClassInfo) {
401             return (CClassInfo) baseClass;
402         } else {
403             return null;
404         }
405     }
406 
407     public CClassRef getRefBaseClass() {
408         if (baseClass instanceof CClassRef) {
409             return (CClassRef) baseClass;
410         } else {
411             return null;
412         }
413     }
414 
415     /**
416      * Enumerates all the sub-classes of this class.
417      */
418     public Iterator<CClassInfo> listSubclasses() {
419         return new Iterator<CClassInfo>() {
420             CClassInfo cur = firstSubclass;
421             public boolean hasNext() {
422                 return cur!=null;
423             }
424 
425             public CClassInfo next() {
426                 CClassInfo r = cur;
427                 cur = cur.nextSibling;
428                 return r;
429             }
430 
431             public void remove() {
432                 throw new UnsupportedOperationException();
433             }
434         };
435     }
436 
437     public CClassInfo getSubstitutionHead() {
438         CClassInfo c=getBaseClass();
439         while(c!=null && !c.isElement())
440             c=c.getBaseClass();
441         return c;
442     }
443 
444 
445     /**
446      * Interfaces to be implemented.
447      * Lazily constructed.
448      */
449     private Set<JClass> _implements = null;
450 
451     public void _implements(JClass c) {
452         if(_implements==null)
453             _implements = new HashSet<JClass>();
454         _implements.add(c);
455     }
456 
457 
458     /** Constructor declarations. array of {@link Constructor}s. */
459     private final List<Constructor> constructors = new ArrayList<Constructor>(1);
460 
461     /** Creates a new constructor declaration and adds it. */
462     public void addConstructor( String... fieldNames ) {
463         constructors.add(new Constructor(fieldNames));
464     }
465 
466     /** list all constructor declarations. */
467     public Collection<? extends Constructor> getConstructors() {
468         return constructors;
469     }
470 
471     public final <T> T accept(Visitor<T> visitor) {
472         return visitor.onBean(this);
473     }
474 
475     public JPackage getOwnerPackage() {
476         return parent.getOwnerPackage();
477     }
478 
479     public final NClass getType() {
480         return this;
481     }
482 
483     public final JClass toType(Outline o, Aspect aspect) {
484         switch(aspect) {
485         case IMPLEMENTATION:
486             return o.getClazz(this).implRef;
487         case EXPOSED:
488             return o.getClazz(this).ref;
489         default:
490             throw new IllegalStateException();
491         }
492     }
493 
494     public boolean isBoxedType() {
495         return false;
496     }
497 
498     public String toString() {
499         return fullName();
500     }
501 }