001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.tools.xjc.model;
038:
039: import java.util.Collections;
040: import java.util.HashMap;
041: import java.util.HashSet;
042: import java.util.Iterator;
043: import java.util.LinkedHashMap;
044: import java.util.Map;
045: import java.util.Set;
046:
047: import javax.xml.bind.annotation.XmlAttribute;
048: import javax.xml.bind.annotation.XmlNsForm;
049: import javax.xml.bind.annotation.XmlTransient;
050: import javax.xml.namespace.QName;
051: import javax.xml.transform.Result;
052:
053: import com.sun.codemodel.JClass;
054: import com.sun.codemodel.JCodeModel;
055: import com.sun.codemodel.JPackage;
056: import com.sun.tools.xjc.ErrorReceiver;
057: import com.sun.tools.xjc.Options;
058: import com.sun.tools.xjc.Plugin;
059: import com.sun.tools.xjc.api.ClassNameAllocator;
060: import com.sun.tools.xjc.generator.bean.BeanGenerator;
061: import com.sun.tools.xjc.generator.bean.ImplStructureStrategy;
062: import com.sun.tools.xjc.model.nav.NClass;
063: import com.sun.tools.xjc.model.nav.NType;
064: import com.sun.tools.xjc.model.nav.NavigatorImpl;
065: import com.sun.tools.xjc.outline.Outline;
066: import com.sun.tools.xjc.reader.xmlschema.Messages;
067: import com.sun.tools.xjc.util.ErrorReceiverFilter;
068: import com.sun.xml.bind.api.impl.NameConverter;
069: import com.sun.xml.bind.v2.model.core.Ref;
070: import com.sun.xml.bind.v2.model.core.TypeInfoSet;
071: import com.sun.xml.bind.v2.model.nav.Navigator;
072: import com.sun.xml.bind.v2.util.FlattenIterator;
073: import com.sun.xml.xsom.XSComponent;
074: import com.sun.xml.xsom.XSSchemaSet;
075:
076: import org.xml.sax.Locator;
077: import org.xml.sax.SAXException;
078: import org.xml.sax.helpers.LocatorImpl;
079:
080: /**
081: * Root of the object model that represents the code that needs to be generated.
082: *
083: * <p>
084: * A {@link Model} is a schema language neutral representation of the
085: * result of a scehma parsing. The back-end then works against this model
086: * to turn this into a series of Java source code.
087: *
088: * @author Kohsuke Kawaguchi
089: */
090: public final class Model implements
091: TypeInfoSet<NType, NClass, Void, Void>, CCustomizable {
092:
093: /**
094: * Generated beans.
095: */
096: private final Map<NClass, CClassInfo> beans = new LinkedHashMap<NClass, CClassInfo>();
097:
098: /**
099: * Generated enums.
100: */
101: private final Map<NClass, CEnumLeafInfo> enums = new LinkedHashMap<NClass, CEnumLeafInfo>();
102:
103: /**
104: * The element mappings.
105: */
106: private final Map<NClass/*scope*/, Map<QName, CElementInfo>> elementMappings = new HashMap<NClass, Map<QName, CElementInfo>>();
107:
108: private final Iterable<? extends CElementInfo> allElements = new Iterable<CElementInfo>() {
109: public Iterator<CElementInfo> iterator() {
110: return new FlattenIterator<CElementInfo>(elementMappings
111: .values());
112: }
113: };
114:
115: /**
116: * {@link TypeUse}s for all named types.
117: * <p>
118: * I really don't want to promote the notion of a 'type' in any place except in the XML Schema code,
119: * but this needs to be exposed for JAX-RPC. A reference to a named XML type will be converted into
120: * a reference to a Java type with annotations.
121: */
122: private final Map<QName, TypeUse> typeUses = new LinkedHashMap<QName, TypeUse>();
123:
124: /**
125: * {@link NameConverter} to be used.
126: */
127: private NameConverter nameConverter;
128:
129: /**
130: * Single linked list that connects all {@link CCustomizations} that belong to this model.
131: *
132: * @see CCustomizations#next
133: */
134: /*package*/CCustomizations customizations;
135:
136: /**
137: * This field controls the generation of package level annotations for s2j
138: */
139: private boolean packageLevelAnnotations = true;
140:
141: /**
142: * If this model was built from XML Schema, this field
143: * stores the root object of the parse schema model.
144: * Otherwise null.
145: *
146: * @sine 2.1.1
147: */
148: public final XSSchemaSet schemaComponent;
149:
150: private CCustomizations gloablCustomizations = new CCustomizations();
151:
152: /**
153: * @param nc
154: * Usually this should be set in the constructor, but we do allow this parameter
155: * to be initially null, and then set later.
156: * @param schemaComponent
157: * The source schema model, if this is built from XSD.
158: */
159: public Model(Options opts, JCodeModel cm, NameConverter nc,
160: ClassNameAllocator allocator, XSSchemaSet schemaComponent) {
161: this .options = opts;
162: this .codeModel = cm;
163: this .nameConverter = nc;
164: this .defaultSymbolSpace = new SymbolSpace(codeModel);
165: defaultSymbolSpace.setType(codeModel.ref(Object.class));
166:
167: elementMappings.put(null, new HashMap<QName, CElementInfo>());
168:
169: if (opts.automaticNameConflictResolution)
170: allocator = new AutoClassNameAllocator(allocator);
171: this .allocator = new ClassNameAllocatorWrapper(allocator);
172: this .schemaComponent = schemaComponent;
173: this .gloablCustomizations.setParent(this , this );
174: }
175:
176: public void setNameConverter(NameConverter nameConverter) {
177: assert this .nameConverter == null;
178: assert nameConverter != null;
179: this .nameConverter = nameConverter;
180: }
181:
182: /**
183: * Gets the name converter that shall be used to parse XML names into Java names.
184: */
185: public final NameConverter getNameConverter() {
186: return nameConverter;
187: }
188:
189: public boolean isPackageLevelAnnotations() {
190: return packageLevelAnnotations;
191: }
192:
193: public void setPackageLevelAnnotations(
194: boolean packageLevelAnnotations) {
195: this .packageLevelAnnotations = packageLevelAnnotations;
196: }
197:
198: /**
199: * This model uses this code model exclusively.
200: */
201: @XmlTransient
202: public final JCodeModel codeModel;
203:
204: /**
205: * Command-line options used for building this model.
206: */
207: public final Options options;
208:
209: /**
210: * True to generate serializable classes.
211: */
212: @XmlAttribute
213: public boolean serializable;
214:
215: /**
216: * serial version UID to be generated.
217: *
218: * null if not to generate serialVersionUID field.
219: */
220: @XmlAttribute
221: public Long serialVersionUID;
222:
223: /**
224: * If non-null, all the generated classes should eventually derive from this class.
225: */
226: @XmlTransient
227: public JClass rootClass;
228:
229: /**
230: * If non-null, all the generated interfaces should eventually derive from this interface.
231: */
232: @XmlTransient
233: public JClass rootInterface;
234:
235: /**
236: * Specifies the code generation strategy.
237: * Must not be null.
238: */
239: public ImplStructureStrategy strategy = ImplStructureStrategy.BEAN_ONLY;
240:
241: /**
242: * This allocator has the final say on deciding the class name.
243: * Must not be null.
244: *
245: * <p>
246: * Model classes are responsible for using the allocator.
247: * This allocator interaction should be transparent to the user/builder
248: * of the model.
249: */
250: /*package*/final ClassNameAllocatorWrapper allocator;
251:
252: /**
253: * Default ID/IDREF symbol space. Any ID/IDREF without explicit
254: * reference to a symbol space is assumed to use this default
255: * symbol space.
256: */
257: @XmlTransient
258: public final SymbolSpace defaultSymbolSpace;
259:
260: /** All the defined {@link SymbolSpace}s keyed by their name. */
261: private final Map<String, SymbolSpace> symbolSpaces = new HashMap<String, SymbolSpace>();
262:
263: public SymbolSpace getSymbolSpace(String name) {
264: SymbolSpace ss = symbolSpaces.get(name);
265: if (ss == null)
266: symbolSpaces.put(name, ss = new SymbolSpace(codeModel));
267: return ss;
268: }
269:
270: /**
271: * Fully-generate the source code into the given model.
272: *
273: * @return
274: * null if there was any errors. Otherwise it returns a valid
275: * {@link Outline} object, which captures how the model objects
276: * are mapped to the generated source code.
277: * <p>
278: * Add-ons can use those information to further augment the generated
279: * source code.
280: */
281: public Outline generateCode(Options opt, ErrorReceiver receiver) {
282: ErrorReceiverFilter ehf = new ErrorReceiverFilter(receiver);
283:
284: // run extensions
285: for (Plugin ma : opt.activePlugins)
286: ma.postProcessModel(this , ehf);
287:
288: Outline o = BeanGenerator.generate(this , ehf);
289:
290: try {// run extensions
291: for (Plugin ma : opt.activePlugins)
292: ma.run(o, opt, ehf);
293: } catch (SAXException e) {
294: // fatal error. error should have been reported
295: return null;
296: }
297:
298: // check for unused plug-in customizations.
299: // these can be only checked after the plug-ins run, so it's here.
300: // the JAXB bindings are checked by XMLSchema's builder.
301: Set<CCustomizations> check = new HashSet<CCustomizations>();
302: for (CCustomizations c = customizations; c != null; c = c.next) {
303: if (!check.add(c)) {
304: throw new AssertionError(); // detect a loop
305: }
306: for (CPluginCustomization p : c) {
307: if (!p.isAcknowledged()) {
308: ehf.error(p.locator, Messages.format(
309: Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION,
310: p.element.getNodeName()));
311: ehf
312: .error(
313: c.getOwner().getLocator(),
314: Messages
315: .format(Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION_LOCATION));
316: }
317: }
318: }
319:
320: if (ehf.hadError())
321: o = null;
322: return o;
323: }
324:
325: /**
326: * Represents the "top-level binding".
327: *
328: * <p>
329: * This is used to support the use of a schema inside WSDL.
330: * For XML Schema, the top-level binding is a map from
331: * global element declarations to its representation class.
332: *
333: * <p>
334: * For other schema languages, it should follow the appendicies in
335: * WSDL (but in practice no one would use WSDL with a schema language
336: * other than XML Schema, so it doesn't really matter.)
337: *
338: * <p>
339: * This needs to be filled by the front-end.
340: */
341: public final Map<QName, CClassInfo> createTopLevelBindings() {
342: Map<QName, CClassInfo> r = new HashMap<QName, CClassInfo>();
343: for (CClassInfo b : beans().values()) {
344: if (b.isElement())
345: r.put(b.getElementName(), b);
346: }
347: return r;
348: }
349:
350: public Navigator<NType, NClass, Void, Void> getNavigator() {
351: return NavigatorImpl.theInstance;
352: }
353:
354: public CNonElement getTypeInfo(NType type) {
355: CBuiltinLeafInfo leaf = CBuiltinLeafInfo.LEAVES.get(type);
356: if (leaf != null)
357: return leaf;
358:
359: return getClassInfo(getNavigator().asDecl(type));
360: }
361:
362: public CBuiltinLeafInfo getAnyTypeInfo() {
363: return CBuiltinLeafInfo.ANYTYPE;
364: }
365:
366: public CNonElement getTypeInfo(Ref<NType, NClass> ref) {
367: // TODO: handle XmlValueList
368: assert !ref.valueList;
369: return getTypeInfo(ref.type);
370: }
371:
372: public Map<NClass, CClassInfo> beans() {
373: return beans;
374: }
375:
376: public Map<NClass, CEnumLeafInfo> enums() {
377: return enums;
378: }
379:
380: public Map<QName, TypeUse> typeUses() {
381: return typeUses;
382: }
383:
384: /**
385: * No array mapping generation for XJC.
386: */
387: public Map<NType, ? extends CArrayInfo> arrays() {
388: return Collections.emptyMap();
389: }
390:
391: public Map<NType, ? extends CBuiltinLeafInfo> builtins() {
392: return CBuiltinLeafInfo.LEAVES;
393: }
394:
395: public CClassInfo getClassInfo(NClass t) {
396: return beans.get(t);
397: }
398:
399: public CElementInfo getElementInfo(NClass scope, QName name) {
400: Map<QName, CElementInfo> m = elementMappings.get(scope);
401: if (m != null) {
402: CElementInfo r = m.get(name);
403: if (r != null)
404: return r;
405: }
406: return elementMappings.get(null).get(name);
407: }
408:
409: public Map<QName, CElementInfo> getElementMappings(NClass scope) {
410: return elementMappings.get(scope);
411: }
412:
413: public Iterable<? extends CElementInfo> getAllElements() {
414: return allElements;
415: }
416:
417: /**
418: * @deprecated
419: * Always return null. Perhaps you are interested in {@link #schemaComponent}?
420: */
421: public XSComponent getSchemaComponent() {
422: return null;
423: }
424:
425: /**
426: * @deprecated
427: * No line number available for the "root" component.
428: */
429: public Locator getLocator() {
430: LocatorImpl r = new LocatorImpl();
431: r.setLineNumber(-1);
432: r.setColumnNumber(-1);
433: return r;
434: }
435:
436: /**
437: * Gets the global customizations.
438: */
439: public CCustomizations getCustomizations() {
440: return gloablCustomizations;
441: }
442:
443: /**
444: * Not implemented in the compile-time model.
445: */
446: public Map<String, String> getXmlNs(String namespaceUri) {
447: return Collections.emptyMap();
448: }
449:
450: public Map<String, String> getSchemaLocations() {
451: return Collections.emptyMap();
452: }
453:
454: public XmlNsForm getElementFormDefault(String nsUri) {
455: throw new UnsupportedOperationException();
456: }
457:
458: public XmlNsForm getAttributeFormDefault(String nsUri) {
459: throw new UnsupportedOperationException();
460: }
461:
462: public void dump(Result out) {
463: // TODO
464: throw new UnsupportedOperationException();
465: }
466:
467: /*package*/void add(CEnumLeafInfo e) {
468: enums.put(e.getClazz(), e);
469: }
470:
471: /*package*/void add(CClassInfo ci) {
472: beans.put(ci.getClazz(), ci);
473: }
474:
475: /*package*/void add(CElementInfo ei) {
476: NClass clazz = null;
477: if (ei.getScope() != null)
478: clazz = ei.getScope().getClazz();
479:
480: Map<QName, CElementInfo> m = elementMappings.get(clazz);
481: if (m == null)
482: elementMappings.put(clazz,
483: m = new HashMap<QName, CElementInfo>());
484: m.put(ei.getElementName(), ei);
485: }
486:
487: private final Map<JPackage, CClassInfoParent.Package> cache = new HashMap<JPackage, CClassInfoParent.Package>();
488:
489: public CClassInfoParent.Package getPackage(JPackage pkg) {
490: CClassInfoParent.Package r = cache.get(pkg);
491: if (r == null)
492: cache.put(pkg, r = new CClassInfoParent.Package(pkg));
493: return r;
494: }
495:
496: /*package*/static final Locator EMPTY_LOCATOR;
497:
498: static {
499: LocatorImpl l = new LocatorImpl();
500: l.setColumnNumber(-1);
501: l.setLineNumber(-1);
502: EMPTY_LOCATOR = l;
503: }
504: }
|