001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.internal.xjc.model;
027:
028: import java.util.Collections;
029: import java.util.HashMap;
030: import java.util.HashSet;
031: import java.util.Iterator;
032: import java.util.LinkedHashMap;
033: import java.util.Map;
034: import java.util.Set;
035:
036: import javax.xml.bind.annotation.XmlAttribute;
037: import javax.xml.bind.annotation.XmlNsForm;
038: import javax.xml.bind.annotation.XmlTransient;
039: import javax.xml.namespace.QName;
040: import javax.xml.transform.Result;
041:
042: import com.sun.codemodel.internal.JClass;
043: import com.sun.codemodel.internal.JCodeModel;
044: import com.sun.codemodel.internal.JPackage;
045: import com.sun.tools.internal.xjc.ErrorReceiver;
046: import com.sun.tools.internal.xjc.Options;
047: import com.sun.tools.internal.xjc.Plugin;
048: import com.sun.tools.internal.xjc.api.ClassNameAllocator;
049: import com.sun.tools.internal.xjc.generator.bean.BeanGenerator;
050: import com.sun.tools.internal.xjc.generator.bean.ImplStructureStrategy;
051: import com.sun.tools.internal.xjc.model.nav.NClass;
052: import com.sun.tools.internal.xjc.model.nav.NType;
053: import com.sun.tools.internal.xjc.model.nav.NavigatorImpl;
054: import com.sun.tools.internal.xjc.outline.Outline;
055: import com.sun.tools.internal.xjc.reader.xmlschema.Messages;
056: import com.sun.tools.internal.xjc.util.ErrorReceiverFilter;
057: import com.sun.xml.internal.bind.api.impl.NameConverter;
058: import com.sun.xml.internal.bind.v2.model.core.Ref;
059: import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
060: import com.sun.xml.internal.bind.v2.model.nav.Navigator;
061: import com.sun.xml.internal.bind.v2.util.FlattenIterator;
062:
063: import org.xml.sax.Locator;
064: import org.xml.sax.helpers.LocatorImpl;
065:
066: /**
067: * Root of the object model that represents the code that needs to be generated.
068: *
069: * <p>
070: * A {@link Model} is a schema language neutral representation of the
071: * result of a scehma parsing. The back-end then works against this model
072: * to turn this into a series of Java source code.
073: *
074: * @author Kohsuke Kawaguchi
075: */
076: public final class Model implements
077: TypeInfoSet<NType, NClass, Void, Void> {
078:
079: /**
080: * Generated beans.
081: */
082: private final Map<NClass, CClassInfo> beans = new LinkedHashMap<NClass, CClassInfo>();
083:
084: /**
085: * Generated enums.
086: */
087: private final Map<NClass, CEnumLeafInfo> enums = new LinkedHashMap<NClass, CEnumLeafInfo>();
088:
089: /**
090: * The element mappings.
091: */
092: private final Map<NClass/*scope*/, Map<QName, CElementInfo>> elementMappings = new HashMap<NClass, Map<QName, CElementInfo>>();
093:
094: private final Iterable<? extends CElementInfo> allElements = new Iterable<CElementInfo>() {
095: public Iterator<CElementInfo> iterator() {
096: return new FlattenIterator<CElementInfo>(elementMappings
097: .values());
098: }
099: };
100:
101: /**
102: * {@link TypeUse}s for all named types.
103: * <p>
104: * I really don't want to promote the notion of a 'type' in any place except in the XML Schema code,
105: * but this needs to be exposed for JAX-RPC. A reference to a named XML type will be converted into
106: * a reference to a Java type with annotations.
107: */
108: private final Map<QName, TypeUse> typeUses = new LinkedHashMap<QName, TypeUse>();
109:
110: /**
111: * {@link NameConverter} to be used.
112: */
113: private NameConverter nameConverter;
114:
115: /**
116: * Single linked list that connects all {@link CCustomizations} that belong to this model.
117: *
118: * @see CCustomizations#next
119: */
120: /*package*/CCustomizations customizations;
121:
122: /**
123: * This field controls the generation of package level annotations for s2j
124: */
125: private boolean packageLevelAnnotations = true;
126:
127: /**
128: * @param nc
129: * Usually this should be set in the constructor, but we do allow this parameter
130: * to be initially null, and then set later.
131: */
132: public Model(Options opts, JCodeModel cm, NameConverter nc,
133: ClassNameAllocator allocator) {
134: this .options = opts;
135: this .codeModel = cm;
136: this .nameConverter = nc;
137: this .defaultSymbolSpace = new SymbolSpace(codeModel);
138: defaultSymbolSpace.setType(codeModel.ref(Object.class));
139:
140: elementMappings.put(null, new HashMap<QName, CElementInfo>());
141:
142: this .allocator = new ClassNameAllocatorWrapper(allocator);
143: }
144:
145: public void setNameConverter(NameConverter nameConverter) {
146: assert this .nameConverter == null;
147: assert nameConverter != null;
148: this .nameConverter = nameConverter;
149: }
150:
151: /**
152: * Gets the name converter that shall be used to parse XML names into Java names.
153: */
154: public final NameConverter getNameConverter() {
155: return nameConverter;
156: }
157:
158: public boolean isPackageLevelAnnotations() {
159: return packageLevelAnnotations;
160: }
161:
162: public void setPackageLevelAnnotations(
163: boolean packageLevelAnnotations) {
164: this .packageLevelAnnotations = packageLevelAnnotations;
165: }
166:
167: /**
168: * This model uses this code model exclusively.
169: */
170: @XmlTransient
171: public final JCodeModel codeModel;
172:
173: /**
174: * Command-line options used for building this model.
175: */
176: public final Options options;
177:
178: /**
179: * True to generate serializable classes.
180: */
181: @XmlAttribute
182: public boolean serializable;
183:
184: /**
185: * serial version UID to be generated.
186: *
187: * null if not to generate serialVersionUID field.
188: */
189: @XmlAttribute
190: public Long serialVersionUID;
191:
192: /**
193: * If non-null, all the generated classes should eventually derive from this class.
194: */
195: @XmlTransient
196: public JClass rootClass;
197:
198: /**
199: * If non-null, all the generated interfaces should eventually derive from this interface.
200: */
201: @XmlTransient
202: public JClass rootInterface;
203:
204: /**
205: * Specifies the code generation strategy.
206: * Must not be null.
207: */
208: public ImplStructureStrategy strategy = ImplStructureStrategy.BEAN_ONLY;
209:
210: /**
211: * This allocator has the final say on deciding the class name.
212: * Must not be null.
213: *
214: * <p>
215: * Model classes are responsible for using the allocator.
216: * This allocator interaction should be transparent to the user/builder
217: * of the model.
218: */
219: /*package*/final ClassNameAllocatorWrapper allocator;
220:
221: /**
222: * Default ID/IDREF symbol space. Any ID/IDREF without explicit
223: * reference to a symbol space is assumed to use this default
224: * symbol space.
225: */
226: @XmlTransient
227: public final SymbolSpace defaultSymbolSpace;
228:
229: /** All the defined {@link SymbolSpace}s keyed by their name. */
230: private final Map<String, SymbolSpace> symbolSpaces = new HashMap<String, SymbolSpace>();
231:
232: public SymbolSpace getSymbolSpace(String name) {
233: SymbolSpace ss = symbolSpaces.get(name);
234: if (ss == null)
235: symbolSpaces.put(name, ss = new SymbolSpace(codeModel));
236: return ss;
237: }
238:
239: /**
240: * Fully-generate the source code into the given model.
241: *
242: * @return
243: * null if there was any errors. Otherwise it returns a valid
244: * {@link Outline} object, which captures how the model objects
245: * are mapped to the generated source code.
246: * <p>
247: * Add-ons can use those information to further augment the generated
248: * source code.
249: */
250: public Outline generateCode(Options opt, ErrorReceiver receiver) {
251: ErrorReceiverFilter ehf = new ErrorReceiverFilter(receiver);
252:
253: // run extensions
254: for (Plugin ma : opt.activePlugins)
255: ma.postProcessModel(this , ehf);
256:
257: Outline o = BeanGenerator.generate(this , ehf);
258:
259: // run extensions
260: for (Plugin ma : opt.activePlugins)
261: ma.run(o, opt, ehf);
262:
263: // check for unused plug-in customizations.
264: // these can be only checked after the plug-ins run, so it's here.
265: // the JAXB bindings are checked by XMLSchema's builder.
266: Set<CCustomizations> check = new HashSet<CCustomizations>();
267: for (CCustomizations c = customizations; c != null; c = c.next) {
268: if (!check.add(c)) {
269: throw new AssertionError(); // detect a loop
270: }
271: for (CPluginCustomization p : c) {
272: if (!p.isAcknowledged()) {
273: ehf.error(p.locator, Messages.format(
274: Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION,
275: p.element.getNodeName()));
276: ehf
277: .error(
278: c.getOwner().getLocator(),
279: Messages
280: .format(Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION_LOCATION));
281: }
282: }
283: }
284:
285: if (ehf.hadError())
286: o = null;
287: return o;
288: }
289:
290: /**
291: * Represents the "top-level binding".
292: *
293: * <p>
294: * This is used to support the use of a schema inside WSDL.
295: * For XML Schema, the top-level binding is a map from
296: * global element declarations to its representation class.
297: *
298: * <p>
299: * For other schema languages, it should follow the appendicies in
300: * WSDL (but in practice no one would use WSDL with a schema language
301: * other than XML Schema, so it doesn't really matter.)
302: *
303: * <p>
304: * This needs to be filled by the front-end.
305: */
306: public final Map<QName, CClassInfo> createTopLevelBindings() {
307: Map<QName, CClassInfo> r = new HashMap<QName, CClassInfo>();
308: for (CClassInfo b : beans().values()) {
309: if (b.isElement())
310: r.put(b.getElementName(), b);
311: }
312: return r;
313: }
314:
315: public Navigator<NType, NClass, Void, Void> getNavigator() {
316: return NavigatorImpl.theInstance;
317: }
318:
319: public CNonElement getTypeInfo(NType type) {
320: CBuiltinLeafInfo leaf = CBuiltinLeafInfo.LEAVES.get(type);
321: if (leaf != null)
322: return leaf;
323:
324: return getClassInfo(getNavigator().asDecl(type));
325: }
326:
327: public CBuiltinLeafInfo getAnyTypeInfo() {
328: return CBuiltinLeafInfo.ANYTYPE;
329: }
330:
331: public CNonElement getTypeInfo(Ref<NType, NClass> ref) {
332: // TODO: handle XmlValueList
333: assert !ref.valueList;
334: return getTypeInfo(ref.type);
335: }
336:
337: public Map<NClass, CClassInfo> beans() {
338: return beans;
339: }
340:
341: public Map<NClass, CEnumLeafInfo> enums() {
342: return enums;
343: }
344:
345: public Map<QName, TypeUse> typeUses() {
346: return typeUses;
347: }
348:
349: /**
350: * No array mapping generation for XJC.
351: */
352: public Map<NType, ? extends CArrayInfo> arrays() {
353: return Collections.emptyMap();
354: }
355:
356: public Map<NType, ? extends CBuiltinLeafInfo> builtins() {
357: return CBuiltinLeafInfo.LEAVES;
358: }
359:
360: public CClassInfo getClassInfo(NClass t) {
361: return beans.get(t);
362: }
363:
364: public CElementInfo getElementInfo(NClass scope, QName name) {
365: Map<QName, CElementInfo> m = elementMappings.get(scope);
366: if (m != null) {
367: CElementInfo r = m.get(name);
368: if (r != null)
369: return r;
370: }
371: return elementMappings.get(null).get(name);
372: }
373:
374: public Map<QName, CElementInfo> getElementMappings(NClass scope) {
375: return elementMappings.get(scope);
376: }
377:
378: public Iterable<? extends CElementInfo> getAllElements() {
379: return allElements;
380: }
381:
382: /**
383: * Not implemented in the compile-time model.
384: */
385: public Map<String, String> getXmlNs(String namespaceUri) {
386: return Collections.emptyMap();
387: }
388:
389: public XmlNsForm getElementFormDefault(String nsUri) {
390: throw new UnsupportedOperationException();
391: }
392:
393: public XmlNsForm getAttributeFormDefault(String nsUri) {
394: throw new UnsupportedOperationException();
395: }
396:
397: public void dump(Result out) {
398: // TODO
399: throw new UnsupportedOperationException();
400: }
401:
402: /*package*/void add(CEnumLeafInfo e) {
403: enums.put(e.getClazz(), e);
404: }
405:
406: /*package*/void add(CClassInfo ci) {
407: beans.put(ci.getClazz(), ci);
408: }
409:
410: /*package*/void add(CElementInfo ei) {
411: NClass clazz = null;
412: if (ei.getScope() != null)
413: clazz = ei.getScope().getClazz();
414:
415: Map<QName, CElementInfo> m = elementMappings.get(clazz);
416: if (m == null)
417: elementMappings.put(clazz,
418: m = new HashMap<QName, CElementInfo>());
419: m.put(ei.getElementName(), ei);
420: }
421:
422: private final Map<JPackage, CClassInfoParent.Package> cache = new HashMap<JPackage, CClassInfoParent.Package>();
423:
424: public CClassInfoParent.Package getPackage(JPackage pkg) {
425: CClassInfoParent.Package r = cache.get(pkg);
426: if (r == null)
427: cache.put(pkg, r = new CClassInfoParent.Package(pkg));
428: return r;
429: }
430:
431: /*package*/static final Locator EMPTY_LOCATOR;
432:
433: static {
434: LocatorImpl l = new LocatorImpl();
435: l.setColumnNumber(-1);
436: l.setLineNumber(-1);
437: EMPTY_LOCATOR = l;
438: }
439: }
|