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