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.reader.xmlschema;
027:
028: import java.util.ArrayList;
029: import java.util.HashMap;
030: import java.util.Map;
031: import java.util.Set;
032:
033: import javax.xml.bind.DatatypeConverter;
034: import javax.xml.namespace.QName;
035: import javax.xml.transform.Transformer;
036: import javax.xml.transform.TransformerConfigurationException;
037: import javax.xml.transform.TransformerFactory;
038:
039: import com.sun.codemodel.internal.JCodeModel;
040: import com.sun.codemodel.internal.fmt.JTextFile;
041: import com.sun.istack.internal.NotNull;
042: import com.sun.tools.internal.xjc.ErrorReceiver;
043: import com.sun.tools.internal.xjc.Options;
044: import com.sun.tools.internal.xjc.generator.bean.field.FieldRendererFactory;
045: import com.sun.tools.internal.xjc.model.CClassInfoParent;
046: import com.sun.tools.internal.xjc.model.Model;
047: import com.sun.tools.internal.xjc.reader.ModelChecker;
048: import com.sun.tools.internal.xjc.reader.Ring;
049: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDeclaration;
050: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDom;
051: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIGlobalBinding;
052: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BISchemaBinding;
053: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BISerializable;
054: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo;
055: import com.sun.tools.internal.xjc.util.CodeModelClassFactory;
056: import com.sun.tools.internal.xjc.util.ErrorReceiverFilter;
057: import com.sun.xml.internal.bind.DatatypeConverterImpl;
058: import com.sun.xml.internal.bind.api.impl.NameConverter;
059: import com.sun.xml.internal.bind.v2.WellKnownNamespace;
060: import com.sun.xml.internal.xsom.XSAnnotation;
061: import com.sun.xml.internal.xsom.XSAttributeUse;
062: import com.sun.xml.internal.xsom.XSComponent;
063: import com.sun.xml.internal.xsom.XSParticle;
064: import com.sun.xml.internal.xsom.XSSchema;
065: import com.sun.xml.internal.xsom.XSSchemaSet;
066: import com.sun.xml.internal.xsom.XSSimpleType;
067: import com.sun.xml.internal.xsom.XSTerm;
068: import com.sun.xml.internal.xsom.XSType;
069: import com.sun.xml.internal.xsom.XSWildcard;
070: import com.sun.xml.internal.xsom.util.XSFinder;
071:
072: import org.xml.sax.Locator;
073:
074: /**
075: * Root of the XML Schema binder.
076: *
077: * <div><img src="doc-files/binding_chart.png"/></div>
078: *
079: * @author Kohsuke Kawaguchi
080: */
081: public class BGMBuilder extends BindingComponent {
082:
083: /**
084: * Entry point.
085: */
086: public static Model build(XSSchemaSet _schemas,
087: JCodeModel codeModel, ErrorReceiver _errorReceiver,
088: Options opts) {
089: // set up a ring
090: final Ring old = Ring.begin();
091: try {
092: ErrorReceiverFilter ef = new ErrorReceiverFilter(
093: _errorReceiver);
094:
095: Ring.add(XSSchemaSet.class, _schemas);
096: Ring.add(codeModel);
097: Model model = new Model(opts, codeModel,
098: null/*set later*/, opts.classNameAllocator);
099: Ring.add(model);
100: Ring.add(ErrorReceiver.class, ef);
101: Ring.add(CodeModelClassFactory.class,
102: new CodeModelClassFactory(ef));
103:
104: BGMBuilder builder = new BGMBuilder(opts.defaultPackage,
105: opts.defaultPackage2,
106: opts.compatibilityMode == Options.EXTENSION, opts
107: .getFieldRendererFactory());
108: builder._build();
109:
110: if (ef.hadError())
111: return null;
112: else
113: return model;
114: } finally {
115: Ring.end(old);
116: }
117: }
118:
119: /**
120: * True if the compiler is running in the extension mode
121: * (as opposed to the strict conformance mode.)
122: */
123: public final boolean inExtensionMode;
124:
125: /**
126: * If this is non-null, this package name takes over
127: * all the schema customizations.
128: */
129: public final String defaultPackage1;
130:
131: /**
132: * If this is non-null, this package name will be
133: * used when no customization is specified.
134: */
135: public final String defaultPackage2;
136:
137: private final BindGreen green = Ring.get(BindGreen.class);
138: private final BindPurple purple = Ring.get(BindPurple.class);
139:
140: public final Model model = Ring.get(Model.class);
141:
142: public final FieldRendererFactory fieldRendererFactory;
143:
144: /**
145: * Lazily computed {@link RefererFinder}.
146: *
147: * @see #getReferer
148: */
149: private RefererFinder refFinder;
150:
151: protected BGMBuilder(String defaultPackage1,
152: String defaultPackage2, boolean _inExtensionMode,
153: FieldRendererFactory fieldRendererFactory) {
154: this .inExtensionMode = _inExtensionMode;
155: this .defaultPackage1 = defaultPackage1;
156: this .defaultPackage2 = defaultPackage2;
157: this .fieldRendererFactory = fieldRendererFactory;
158:
159: DatatypeConverter
160: .setDatatypeConverter(DatatypeConverterImpl.theInstance);
161:
162: promoteGlobalBindings();
163: }
164:
165: private void _build() {
166: // do the binding
167: buildContents();
168: getClassSelector().executeTasks();
169:
170: // additional error check
171: // Reports unused customizations to the user as errors.
172: Ring.get(UnusedCustomizationChecker.class).run();
173:
174: Ring.get(ModelChecker.class).check();
175: }
176:
177: /** List up all the global bindings. */
178: private void promoteGlobalBindings() {
179: // promote any global bindings in the schema
180: XSSchemaSet schemas = Ring.get(XSSchemaSet.class);
181:
182: for (XSSchema s : schemas.getSchemas()) {
183: BindInfo bi = getBindInfo(s);
184:
185: BIGlobalBinding gb = bi.get(BIGlobalBinding.class);
186: if (gb == null)
187: continue;
188:
189: if (globalBinding == null) {
190: globalBinding = gb;
191: globalBinding.markAsAcknowledged();
192: } else {
193: // acknowledge this customization and report an error
194: // otherwise the user will see "customization is attached to a wrong place" error,
195: // which is incorrect
196: gb.markAsAcknowledged();
197: getErrorReporter().error(gb.getLocation(),
198: Messages.ERR_MULTIPLE_GLOBAL_BINDINGS);
199: getErrorReporter().error(globalBinding.getLocation(),
200: Messages.ERR_MULTIPLE_GLOBAL_BINDINGS_OTHER);
201: }
202: }
203:
204: if (globalBinding == null) {
205: // no global customization is present.
206: // use the default one
207: globalBinding = new BIGlobalBinding();
208: BindInfo big = new BindInfo();
209: big.addDecl(globalBinding);
210: big.setOwner(this , null);
211: }
212:
213: // code generation mode
214: model.strategy = globalBinding.getCodeGenerationStrategy();
215: model.rootClass = globalBinding.getSuperClass();
216: model.rootInterface = globalBinding.getSuperInterface();
217:
218: particleBinder = globalBinding.isSimpleMode() ? new ExpressionParticleBinder()
219: : new DefaultParticleBinder();
220:
221: // check XJC extensions and realize them
222: BISerializable serial = globalBinding.getSerializable();
223: if (serial != null) {
224: model.serializable = true;
225: model.serialVersionUID = serial.uid;
226: }
227:
228: // obtain the name conversion mode
229: if (globalBinding.nameConverter != null)
230: model.setNameConverter(globalBinding.nameConverter);
231:
232: // attach global conversions to the appropriate simple types
233: globalBinding.dispatchGlobalConversions(schemas);
234:
235: globalBinding.errorCheck();
236: }
237:
238: /**
239: * Global bindings.
240: *
241: * The empty global binding is set as the default, so that
242: * there will be no need to test if the value is null.
243: */
244: private BIGlobalBinding globalBinding;
245:
246: /**
247: * Gets the global bindings.
248: */
249: public @NotNull
250: BIGlobalBinding getGlobalBinding() {
251: return globalBinding;
252: }
253:
254: private ParticleBinder particleBinder;
255:
256: /**
257: * Gets the particle binder for this binding.
258: */
259: public @NotNull
260: ParticleBinder getParticleBinder() {
261: return particleBinder;
262: }
263:
264: /**
265: * Name converter that implements "XML->Java name conversion"
266: * as specified in the spec.
267: *
268: * This object abstracts the detail that we use different name
269: * conversion depending on the customization.
270: *
271: * <p>
272: * This object should be used to perform any name conversion
273: * needs, instead of the JJavaName class in CodeModel.
274: */
275: public NameConverter getNameConverter() {
276: return model.getNameConverter();
277: }
278:
279: /** Fill-in the contents of each classes. */
280: private void buildContents() {
281: ClassSelector cs = getClassSelector();
282: SimpleTypeBuilder stb = Ring.get(SimpleTypeBuilder.class);
283:
284: for (XSSchema s : Ring.get(XSSchemaSet.class).getSchemas()) {
285: getClassSelector().pushClassScope(
286: new CClassInfoParent.Package(getClassSelector()
287: .getPackage(s.getTargetNamespace())));
288:
289: if (!s.getTargetNamespace().equals(
290: WellKnownNamespace.XML_SCHEMA)) {
291: checkMultipleSchemaBindings(s);
292: processPackageJavadoc(s);
293: populate(s.getAttGroupDecls());
294: populate(s.getAttributeDecls());
295: populate(s.getElementDecls());
296: populate(s.getModelGroupDecls());
297: }
298:
299: // fill in typeUses
300: for (XSType t : s.getTypes().values()) {
301: stb.refererStack.push(t);
302: model.typeUses().put(
303: new QName(t.getTargetNamespace(), t.getName()),
304: cs.bindToType(t));
305: stb.refererStack.pop();
306: }
307:
308: getClassSelector().popClassScope();
309: }
310: }
311:
312: /** Reports an error if there are more than one jaxb:schemaBindings customization. */
313: private void checkMultipleSchemaBindings(XSSchema schema) {
314: ArrayList<Locator> locations = new ArrayList<Locator>();
315:
316: BindInfo bi = getBindInfo(schema);
317: for (BIDeclaration bid : bi) {
318: if (bid.getName() == BISchemaBinding.NAME)
319: locations.add(bid.getLocation());
320: }
321: if (locations.size() <= 1)
322: return; // OK
323:
324: // error
325: getErrorReporter().error(locations.get(0),
326: Messages.ERR_MULTIPLE_SCHEMA_BINDINGS,
327: schema.getTargetNamespace());
328: for (int i = 1; i < locations.size(); i++)
329: getErrorReporter().error((Locator) locations.get(i),
330: Messages.ERR_MULTIPLE_SCHEMA_BINDINGS_LOCATION);
331: }
332:
333: /**
334: * Calls {@link ClassSelector} for each item in the iterator
335: * to populate class items if there is any.
336: */
337: private void populate(Map<String, ? extends XSComponent> col) {
338: ClassSelector cs = getClassSelector();
339: for (XSComponent sc : col.values())
340: cs.bindToType(sc);
341: }
342:
343: /**
344: * Generates <code>package.html</code> if the customization
345: * says so.
346: */
347: private void processPackageJavadoc(XSSchema s) {
348: // look for the schema-wide customization
349: BISchemaBinding cust = getBindInfo(s)
350: .get(BISchemaBinding.class);
351: if (cust == null)
352: return; // not present
353:
354: cust.markAsAcknowledged();
355: if (cust.getJavadoc() == null)
356: return; // no javadoc customization
357:
358: // produce a HTML file
359: JTextFile html = new JTextFile("package.html");
360: html.setContents(cust.getJavadoc());
361: getClassSelector().getPackage(s.getTargetNamespace())
362: .addResourceFile(html);
363: }
364:
365: /**
366: * Gets or creates the BindInfo object associated to a schema component.
367: *
368: * @return
369: * Always return a non-null valid BindInfo object.
370: * Even if no declaration was specified, this method creates
371: * a new BindInfo so that new decls can be added.
372: */
373: public BindInfo getOrCreateBindInfo(XSComponent schemaComponent) {
374:
375: BindInfo bi = _getBindInfoReadOnly(schemaComponent);
376: if (bi != null)
377: return bi;
378:
379: // XSOM is read-only, so we cannot add new annotations.
380: // for components that didn't have annotations,
381: // we maintain an external map.
382: bi = new BindInfo();
383: bi.setOwner(this , schemaComponent);
384: externalBindInfos.put(schemaComponent, bi);
385: return bi;
386: }
387:
388: /**
389: * Used as a constant instance to represent the empty {@link BindInfo}.
390: */
391: private final BindInfo emptyBindInfo = new BindInfo();
392:
393: /**
394: * Gets the BindInfo object associated to a schema component.
395: *
396: * @return
397: * always return a valid {@link BindInfo} object. If none
398: * is specified for the given component, a dummy empty BindInfo
399: * will be returned.
400: */
401: public BindInfo getBindInfo(XSComponent schemaComponent) {
402: BindInfo bi = _getBindInfoReadOnly(schemaComponent);
403: if (bi != null)
404: return bi;
405: else
406: return emptyBindInfo;
407: }
408:
409: /**
410: * Gets the BindInfo object associated to a schema component.
411: *
412: * @return
413: * null if no bind info is associated to this schema component.
414: */
415: private BindInfo _getBindInfoReadOnly(XSComponent schemaComponent) {
416:
417: BindInfo bi = externalBindInfos.get(schemaComponent);
418: if (bi != null)
419: return bi;
420:
421: XSAnnotation annon = schemaComponent.getAnnotation();
422: if (annon != null) {
423: bi = (BindInfo) annon.getAnnotation();
424: if (bi != null) {
425: if (bi.getOwner() == null)
426: bi.setOwner(this , schemaComponent);
427: return bi;
428: }
429: }
430:
431: return null;
432: }
433:
434: /**
435: * A map that stores binding declarations augmented by XJC.
436: */
437: private final Map<XSComponent, BindInfo> externalBindInfos = new HashMap<XSComponent, BindInfo>();
438:
439: /**
440: * Gets the {@link BIDom} object that applies to the given particle.
441: */
442: protected final BIDom getLocalDomCustomization(XSParticle p) {
443: BIDom dom = getBindInfo(p).get(BIDom.class);
444: if (dom != null)
445: return dom;
446:
447: // if not, the term might have one.
448: dom = getBindInfo(p.getTerm()).get(BIDom.class);
449: if (dom != null)
450: return dom;
451:
452: XSTerm t = p.getTerm();
453: // type could also have one, in case of the dom customization
454: if (t.isElementDecl())
455: return getBindInfo(t.asElementDecl().getType()).get(
456: BIDom.class);
457: // similarly the model group in a model group definition may have one.
458: if (t.isModelGroupDecl())
459: return getBindInfo(t.asModelGroupDecl().getModelGroup())
460: .get(BIDom.class);
461:
462: return null;
463: }
464:
465: /**
466: * Returns true if the component should be processed by purple.
467: */
468: private final XSFinder toPurple = new XSFinder() {
469: public Boolean attributeUse(XSAttributeUse use) {
470: // attribute use always maps to a property
471: return true;
472: }
473:
474: public Boolean simpleType(XSSimpleType xsSimpleType) {
475: // simple type always maps to a type, hence we should take purple
476: return true;
477: }
478:
479: public Boolean wildcard(XSWildcard xsWildcard) {
480: // attribute wildcards always maps to a property.
481: // element wildcards should have been processed with particle binders
482: return true;
483: }
484: };
485:
486: /**
487: * If the component maps to a property, forwards to purple, otherwise to green.
488: *
489: * If the component is mapped to a type, this method needs to return true.
490: * See the chart at the class javadoc.
491: */
492: public void ying(XSComponent sc) {
493: if (sc.apply(toPurple) == true
494: || getClassSelector().bindToType(sc) != null)
495: sc.visit(purple);
496: else
497: sc.visit(green);
498: }
499:
500: private Transformer identityTransformer;
501:
502: /**
503: * Gets the shared instance of the identity transformer.
504: */
505: public Transformer getIdentityTransformer() {
506: try {
507: if (identityTransformer == null)
508: identityTransformer = TransformerFactory.newInstance()
509: .newTransformer();
510: return identityTransformer;
511: } catch (TransformerConfigurationException e) {
512: throw new Error(e); // impossible
513: }
514: }
515:
516: /**
517: * Find all types that refer to the given complex type.
518: */
519: public Set<XSComponent> getReferer(XSType c) {
520: if (refFinder == null) {
521: refFinder = new RefererFinder();
522: refFinder.schemaSet(Ring.get(XSSchemaSet.class));
523: }
524: return refFinder.getReferer(c);
525: }
526: }
|