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 com.sun.codemodel.internal.JClass;
29import com.sun.codemodel.internal.JCodeModel;
30import com.sun.codemodel.internal.JPackage;
31import com.sun.tools.internal.xjc.ErrorReceiver;
32import com.sun.tools.internal.xjc.Options;
33import com.sun.tools.internal.xjc.Plugin;
34import com.sun.tools.internal.xjc.api.ClassNameAllocator;
35import com.sun.tools.internal.xjc.generator.bean.BeanGenerator;
36import com.sun.tools.internal.xjc.generator.bean.ImplStructureStrategy;
37import com.sun.tools.internal.xjc.model.nav.NClass;
38import com.sun.tools.internal.xjc.model.nav.NType;
39import com.sun.tools.internal.xjc.model.nav.NavigatorImpl;
40import com.sun.tools.internal.xjc.outline.Outline;
41import com.sun.tools.internal.xjc.reader.xmlschema.Messages;
42import com.sun.tools.internal.xjc.util.ErrorReceiverFilter;
43import com.sun.xml.internal.bind.api.impl.NameConverter;
44import com.sun.xml.internal.bind.v2.model.core.Ref;
45import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
46import com.sun.xml.internal.bind.v2.model.nav.Navigator;
47import com.sun.xml.internal.bind.v2.util.FlattenIterator;
48import com.sun.xml.internal.xsom.XSComponent;
49import com.sun.xml.internal.xsom.XSSchemaSet;
50import org.xml.sax.Locator;
51import org.xml.sax.SAXException;
52import org.xml.sax.helpers.LocatorImpl;
53
54import javax.xml.bind.annotation.XmlAttribute;
55import javax.xml.bind.annotation.XmlNsForm;
56import javax.xml.bind.annotation.XmlTransient;
57import javax.xml.namespace.QName;
58import javax.xml.transform.Result;
59import java.util.Collections;
60import java.util.HashMap;
61import java.util.HashSet;
62import java.util.Iterator;
63import java.util.LinkedHashMap;
64import java.util.Map;
65import java.util.Set;
66
67/**
68 * Root of the object model that represents the code that needs to be generated.
69 *
70 * <p>
71 * A {@link Model} is a schema language neutral representation of the
72 * result of a schema parsing. The back-end then works against this model
73 * to turn this into a series of Java source code.
74 *
75 * @author Kohsuke Kawaguchi
76 */
77public final class Model implements TypeInfoSet<NType,NClass,Void,Void>, CCustomizable {
78
79    /**
80     * Generated beans.
81     */
82    private final Map<NClass,CClassInfo> beans = new LinkedHashMap<NClass,CClassInfo>();
83
84    /**
85     * Generated enums.
86     */
87    private final Map<NClass,CEnumLeafInfo> enums = new LinkedHashMap<NClass,CEnumLeafInfo>();
88
89    /**
90     * The element mappings.
91     */
92    private final Map<NClass/*scope*/,Map<QName,CElementInfo>> elementMappings =
93            new LinkedHashMap<NClass, Map<QName, CElementInfo>>();
94
95    private final Iterable<? extends CElementInfo> allElements =
96        new Iterable<CElementInfo>() {
97            public Iterator<CElementInfo> iterator() {
98                return new FlattenIterator<CElementInfo>(elementMappings.values());
99            }
100        };
101
102    /**
103     * {@link TypeUse}s for all named types.
104     * <p>
105     * I really don't want to promote the notion of a 'type' in any place except in the XML Schema code,
106     * but this needs to be exposed for JAX-RPC. A reference to a named XML type will be converted into
107     * a reference to a Java type with annotations.
108     */
109    private final Map<QName,TypeUse> typeUses = new LinkedHashMap<QName, TypeUse>();
110
111    /**
112     * {@link NameConverter} to be used.
113     */
114    private NameConverter nameConverter;
115
116    /**
117     * Single linked list that connects all {@link CCustomizations} that belong to this model.
118     *
119     * @see CCustomizations#next
120     */
121    /*package*/ CCustomizations customizations;
122
123    /**
124     * This field controls the generation of package level annotations for s2j
125     */
126    private boolean packageLevelAnnotations = true;
127
128    /**
129     * If this model was built from XML Schema, this field
130     * stores the root object of the parse schema model.
131     * Otherwise null.
132     *
133     * @since 2.1.1
134     */
135    public final XSSchemaSet schemaComponent;
136
137    private CCustomizations globalCustomizations = new CCustomizations();
138
139    /**
140     * @param nc
141     *      Usually this should be set in the constructor, but we do allow this parameter
142     *      to be initially null, and then set later.
143     * @param schemaComponent
144     *      The source schema model, if this is built from XSD.
145     */
146    public Model( Options opts, JCodeModel cm, NameConverter nc, ClassNameAllocator allocator, XSSchemaSet schemaComponent ) {
147        this.options = opts;
148        this.codeModel = cm;
149        this.nameConverter = nc;
150        this.defaultSymbolSpace = new SymbolSpace(codeModel);
151        defaultSymbolSpace.setType(codeModel.ref(Object.class));
152
153        elementMappings.put(null, new LinkedHashMap<QName, CElementInfo>());
154
155        if(opts.automaticNameConflictResolution)
156            allocator = new AutoClassNameAllocator(allocator);
157        this.allocator = new ClassNameAllocatorWrapper(allocator);
158        this.schemaComponent = schemaComponent;
159        this.globalCustomizations.setParent(this, this);
160    }
161
162    public void setNameConverter(NameConverter nameConverter) {
163        assert this.nameConverter==null;
164        assert nameConverter!=null;
165        this.nameConverter = nameConverter;
166    }
167
168    /**
169     * Gets the name converter that shall be used to parse XML names into Java names.
170     */
171    public final NameConverter getNameConverter() {
172        return nameConverter;
173    }
174
175    public boolean isPackageLevelAnnotations() {
176        return packageLevelAnnotations;
177    }
178
179    public void setPackageLevelAnnotations(boolean packageLevelAnnotations) {
180        this.packageLevelAnnotations = packageLevelAnnotations;
181    }
182
183    /**
184     * This model uses this code model exclusively.
185     */
186    @XmlTransient
187    public final JCodeModel codeModel;
188
189    /**
190     * Command-line options used for building this model.
191     */
192    public final Options options;
193
194    /**
195     * True to generate serializable classes.
196     */
197    @XmlAttribute
198    public boolean serializable;
199
200    /**
201     * serial version UID to be generated.
202     *
203     * null if not to generate serialVersionUID field.
204     */
205    @XmlAttribute
206    public Long serialVersionUID;
207
208    /**
209     * If non-null, all the generated classes should eventually derive from this class.
210     */
211    @XmlTransient
212    public JClass rootClass;
213
214    /**
215     * If non-null, all the generated interfaces should eventually derive from this interface.
216     */
217    @XmlTransient
218    public JClass rootInterface;
219
220    /**
221     * Specifies the code generation strategy.
222     * Must not be null.
223     */
224    public ImplStructureStrategy strategy = ImplStructureStrategy.BEAN_ONLY;
225
226    /**
227     * This allocator has the final say on deciding the class name.
228     * Must not be null.
229     *
230     * <p>
231     * Model classes are responsible for using the allocator.
232     * This allocator interaction should be transparent to the user/builder
233     * of the model.
234     */
235    /*package*/ final ClassNameAllocatorWrapper allocator;
236
237    /**
238     * Default ID/IDREF symbol space. Any ID/IDREF without explicit
239     * reference to a symbol space is assumed to use this default
240     * symbol space.
241     */
242    @XmlTransient
243    public final SymbolSpace defaultSymbolSpace;
244
245    /** All the defined {@link SymbolSpace}s keyed by their name. */
246    private final Map<String,SymbolSpace> symbolSpaces = new HashMap<String,SymbolSpace>();
247
248    public SymbolSpace getSymbolSpace( String name ) {
249        SymbolSpace ss = symbolSpaces.get(name);
250        if(ss==null)
251            symbolSpaces.put(name,ss=new SymbolSpace(codeModel));
252        return ss;
253    }
254
255    /**
256     * Fully-generate the source code into the given model.
257     *
258     * @return
259     *      null if there was any errors. Otherwise it returns a valid
260     *      {@link Outline} object, which captures how the model objects
261     *      are mapped to the generated source code.
262     *      <p>
263     *      Add-ons can use those information to further augment the generated
264     *      source code.
265     */
266    public Outline generateCode(Options opt,ErrorReceiver receiver) {
267        ErrorReceiverFilter ehf = new ErrorReceiverFilter(receiver);
268
269        // run extensions // moved to BGMBuilder._build() - issue with hyperjaxb3
270//        for( Plugin ma : opt.activePlugins )
271//            ma.postProcessModel(this,ehf);
272
273        Outline o = BeanGenerator.generate(this, ehf);
274
275        try {// run extensions
276            for( Plugin ma : opt.activePlugins )
277                ma.run(o,opt,ehf);
278        } catch (SAXException e) {
279            // fatal error. error should have been reported
280            return null;
281        }
282
283        // check for unused plug-in customizations.
284        // these can be only checked after the plug-ins run, so it's here.
285        // the JAXB bindings are checked by XMLSchema's builder.
286        Set<CCustomizations> check = new HashSet<CCustomizations>();
287        for( CCustomizations c=customizations; c!=null; c=c.next ) {
288            if(!check.add(c)) {
289                throw new AssertionError(); // detect a loop
290            }
291            for (CPluginCustomization p : c) {
292                if(!p.isAcknowledged()) {
293                    ehf.error(
294                        p.locator,
295                        Messages.format(
296                            Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION,
297                            p.element.getNodeName()
298                        ));
299                    ehf.error(
300                        c.getOwner().getLocator(),
301                        Messages.format(
302                            Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION_LOCATION));
303                }
304            }
305        }
306
307        if(ehf.hadError())
308            o = null;
309        return o;
310    }
311
312    /**
313     * Represents the "top-level binding".
314     *
315     * <p>
316     * This is used to support the use of a schema inside WSDL.
317     * For XML Schema, the top-level binding is a map from
318     * global element declarations to its representation class.
319     *
320     * <p>
321     * For other schema languages, it should follow the appendices in
322     * WSDL (but in practice no one would use WSDL with a schema language
323     * other than XML Schema, so it doesn't really matter.)
324     *
325     * <p>
326     * This needs to be filled by the front-end.
327     */
328    public final Map<QName,CClassInfo> createTopLevelBindings() {
329        Map<QName,CClassInfo> r = new HashMap<QName,CClassInfo>();
330        for( CClassInfo b : beans().values() ) {
331            if(b.isElement())
332                r.put(b.getElementName(),b);
333        }
334        return r;
335    }
336
337    public Navigator<NType,NClass,Void,Void> getNavigator() {
338        return NavigatorImpl.theInstance;
339    }
340
341    public CNonElement getTypeInfo(NType type) {
342        CBuiltinLeafInfo leaf = CBuiltinLeafInfo.LEAVES.get(type);
343        if(leaf!=null)      return leaf;
344
345        return getClassInfo(getNavigator().asDecl(type));
346    }
347
348    public CBuiltinLeafInfo getAnyTypeInfo() {
349        return CBuiltinLeafInfo.ANYTYPE;
350    }
351
352    public CNonElement getTypeInfo(Ref<NType,NClass> ref) {
353        // TODO: handle XmlValueList
354        assert !ref.valueList;
355        return getTypeInfo(ref.type);
356    }
357
358    public Map<NClass,CClassInfo> beans() {
359        return beans;
360    }
361
362    public Map<NClass,CEnumLeafInfo> enums() {
363        return enums;
364    }
365
366    public Map<QName,TypeUse> typeUses() {
367        return typeUses;
368    }
369
370    /**
371     * No array mapping generation for XJC.
372     */
373    public Map<NType, ? extends CArrayInfo> arrays() {
374        return Collections.emptyMap();
375    }
376
377    public Map<NType, ? extends CBuiltinLeafInfo> builtins() {
378        return CBuiltinLeafInfo.LEAVES;
379    }
380
381    public CClassInfo getClassInfo(NClass t) {
382        return beans.get(t);
383    }
384
385    public CElementInfo getElementInfo(NClass scope,QName name) {
386        Map<QName,CElementInfo> m = elementMappings.get(scope);
387        if(m!=null) {
388            CElementInfo r = m.get(name);
389            if(r!=null)     return r;
390        }
391        return elementMappings.get(null).get(name);
392    }
393
394    public Map<QName,CElementInfo> getElementMappings(NClass scope) {
395        return elementMappings.get(scope);
396    }
397
398    public Iterable<? extends CElementInfo> getAllElements() {
399        return allElements;
400    }
401
402    /**
403     * @deprecated
404     *      Always return null. Perhaps you are interested in {@link #schemaComponent}?
405     */
406    public XSComponent getSchemaComponent() {
407        return null;
408    }
409
410    /**
411     * @deprecated
412     *      No line number available for the "root" component.
413     */
414    public Locator getLocator() {
415        LocatorImpl r = new LocatorImpl();
416        r.setLineNumber(-1);
417        r.setColumnNumber(-1);
418        return r;
419    }
420
421    /**
422     * Gets the global customizations.
423     */
424    public CCustomizations getCustomizations() {
425        return globalCustomizations;
426    }
427
428    /**
429     * Not implemented in the compile-time model.
430     */
431    public Map<String, String> getXmlNs(String namespaceUri) {
432        return Collections.emptyMap();
433    }
434
435    public Map<String, String> getSchemaLocations() {
436        return Collections.emptyMap();
437    }
438
439    public XmlNsForm getElementFormDefault(String nsUri) {
440        throw new UnsupportedOperationException();
441    }
442
443    public XmlNsForm getAttributeFormDefault(String nsUri) {
444        throw new UnsupportedOperationException();
445    }
446
447    public void dump(Result out) {
448        // TODO
449        throw new UnsupportedOperationException();
450    }
451
452    /*package*/ void add( CEnumLeafInfo e ) {
453        enums.put( e.getClazz(), e );
454    }
455
456    /*package*/ void add( CClassInfo ci ) {
457        beans.put( ci.getClazz(), ci );
458    }
459
460    /*package*/ void add( CElementInfo ei ) {
461        NClass clazz = null;
462        if(ei.getScope()!=null)
463            clazz = ei.getScope().getClazz();
464
465        Map<QName,CElementInfo> m = elementMappings.get(clazz);
466        if(m==null)
467            elementMappings.put(clazz, m = new LinkedHashMap<QName, CElementInfo>());
468        m.put(ei.getElementName(),ei);
469    }
470
471
472    private final Map<JPackage,CClassInfoParent.Package> cache = new HashMap<JPackage,CClassInfoParent.Package>();
473
474    public CClassInfoParent.Package getPackage(JPackage pkg) {
475        CClassInfoParent.Package r = cache.get(pkg);
476        if(r==null)
477            cache.put(pkg,r=new CClassInfoParent.Package(pkg));
478        return r;
479    }
480
481    /*package*/ static final Locator EMPTY_LOCATOR;
482
483    static {
484        LocatorImpl l = new LocatorImpl();
485        l.setColumnNumber(-1);
486        l.setLineNumber(-1);
487        EMPTY_LOCATOR = l;
488    }
489}
490