1/*
2 * Copyright (c) 1997, 2014, 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.tools.internal.xjc.model;
27
28import java.util.ArrayList;
29import java.util.Collection;
30import java.util.HashSet;
31import java.util.Iterator;
32import java.util.List;
33import java.util.Set;
34
35import javax.xml.bind.annotation.XmlElement;
36import javax.xml.bind.annotation.XmlID;
37import javax.xml.bind.annotation.XmlIDREF;
38import javax.xml.bind.annotation.XmlRootElement;
39import javax.xml.namespace.QName;
40
41import com.sun.codemodel.internal.JClass;
42import com.sun.codemodel.internal.JCodeModel;
43import com.sun.codemodel.internal.JPackage;
44import com.sun.istack.internal.Nullable;
45import com.sun.tools.internal.xjc.Language;
46import com.sun.tools.internal.xjc.model.nav.NClass;
47import com.sun.tools.internal.xjc.model.nav.NType;
48import com.sun.tools.internal.xjc.outline.Aspect;
49import com.sun.tools.internal.xjc.outline.Outline;
50import com.sun.tools.internal.xjc.reader.Ring;
51import com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder;
52import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIFactoryMethod;
53import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
54import com.sun.xml.internal.bind.v2.model.core.Element;
55import com.sun.xml.internal.xsom.XSComponent;
56
57import org.xml.sax.Locator;
58
59/**
60 * Mutable {@link ClassInfo} representation.
61 *
62 * <p>
63 * Schema parsers build these objects.
64 *
65 * @author Kohsuke Kawaguchi
66 */
67public 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}
502