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