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.Set;
029:
030: import javax.xml.namespace.QName;
031:
032: import com.sun.codemodel.internal.JJavaName;
033: import com.sun.codemodel.internal.JPackage;
034: import com.sun.istack.internal.NotNull;
035: import com.sun.istack.internal.Nullable;
036: import com.sun.tools.internal.xjc.ErrorReceiver;
037: import com.sun.tools.internal.xjc.model.CClassInfo;
038: import com.sun.tools.internal.xjc.model.CClassInfoParent;
039: import com.sun.tools.internal.xjc.model.CCustomizations;
040: import com.sun.tools.internal.xjc.model.CElement;
041: import com.sun.tools.internal.xjc.model.CElementInfo;
042: import com.sun.tools.internal.xjc.model.Model;
043: import com.sun.tools.internal.xjc.reader.Ring;
044: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIClass;
045: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIGlobalBinding;
046: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BISchemaBinding;
047: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo;
048: import com.sun.xml.internal.bind.v2.TODO;
049: import com.sun.xml.internal.xsom.XSAnnotation;
050: import com.sun.xml.internal.xsom.XSAttGroupDecl;
051: import com.sun.xml.internal.xsom.XSAttributeDecl;
052: import com.sun.xml.internal.xsom.XSAttributeUse;
053: import com.sun.xml.internal.xsom.XSComplexType;
054: import com.sun.xml.internal.xsom.XSComponent;
055: import com.sun.xml.internal.xsom.XSContentType;
056: import com.sun.xml.internal.xsom.XSDeclaration;
057: import com.sun.xml.internal.xsom.XSElementDecl;
058: import com.sun.xml.internal.xsom.XSFacet;
059: import com.sun.xml.internal.xsom.XSIdentityConstraint;
060: import com.sun.xml.internal.xsom.XSModelGroup;
061: import com.sun.xml.internal.xsom.XSModelGroupDecl;
062: import com.sun.xml.internal.xsom.XSNotation;
063: import com.sun.xml.internal.xsom.XSParticle;
064: import com.sun.xml.internal.xsom.XSSchema;
065: import com.sun.xml.internal.xsom.XSSimpleType;
066: import com.sun.xml.internal.xsom.XSType;
067: import com.sun.xml.internal.xsom.XSWildcard;
068: import com.sun.xml.internal.xsom.XSXPath;
069:
070: /**
071: * Default classBinder implementation. Honors <jaxb:class> customizations
072: * and default bindings.
073: */
074: final class DefaultClassBinder implements ClassBinder {
075: private final SimpleTypeBuilder stb = Ring
076: .get(SimpleTypeBuilder.class);
077: private final Model model = Ring.get(Model.class);
078:
079: protected final BGMBuilder builder = Ring.get(BGMBuilder.class);
080: protected final ClassSelector selector = Ring
081: .get(ClassSelector.class);
082:
083: public CElement attGroupDecl(XSAttGroupDecl decl) {
084: return allow(decl, decl.getName());
085: }
086:
087: public CElement attributeDecl(XSAttributeDecl decl) {
088: return allow(decl, decl.getName());
089: }
090:
091: public CElement modelGroup(XSModelGroup mgroup) {
092: return never();
093: }
094:
095: public CElement modelGroupDecl(XSModelGroupDecl decl) {
096: return never();
097: }
098:
099: public CElement complexType(XSComplexType type) {
100: CElement ci = allow(type, type.getName());
101: if (ci != null)
102: return ci;
103:
104: // no customization is given -- do as the default binding.
105:
106: BindInfo bi = builder.getBindInfo(type);
107:
108: if (type.isGlobal()) {
109: QName tagName = null;
110: String className = deriveName(type);
111: if (getGlobalBinding().isSimpleMode()) {
112: // in the simple mode, we may optimize it away
113: XSElementDecl referer = getSoleElementReferer(type);
114: if (referer != null && isCollapsable(referer)) {
115: // if a global element contains
116: // a collpsable complex type, we bind this element to a named one
117: // and collapses element and complex type.
118: tagName = new QName(referer.getTargetNamespace(),
119: referer.getName());
120: className = deriveName(referer);
121: }
122: }
123:
124: // by default, global ones get their own classes.
125:
126: JPackage pkg = selector.getPackage(type
127: .getTargetNamespace());
128:
129: return new CClassInfo(model, pkg, className, type
130: .getLocator(), getTypeName(type), tagName, type, bi
131: .toCustomizationList());
132: } else {
133: XSElementDecl element = type.getScope();
134:
135: if (element.isGlobal() && isCollapsable(element)) {
136: if (builder.getBindInfo(element).get(BIClass.class) != null)
137: // the parent element was bound to a class. Don't bind this again to
138: // cause unnecessary wrapping
139: return null;
140:
141: // generate one class from element and complex type together.
142: // this needs to be done before selector.isBound to avoid infinite recursion.
143:
144: // but avoid doing so when the element is mapped to a class,
145: // which creates unnecessary classes
146: return new CClassInfo(model, selector.getClassScope(),
147: deriveName(element), element.getLocator(),
148: null, new QName(element.getTargetNamespace(),
149: element.getName()), element, bi
150: .toCustomizationList());
151: }
152:
153: CElement parentType = selector.isBound(element);
154:
155: String className;
156: CClassInfoParent scope;
157:
158: if (parentType != null
159: && parentType instanceof CElementInfo
160: && ((CElementInfo) parentType).hasClass()) {
161: // special case where we put a nested 'Type' element
162: scope = parentType;
163: className = "Type";
164: } else {
165: // since the parent element isn't bound to a type, merge the customizations associated to it, too.
166: // custs = CCustomizations.merge( custs, builder.getBindInfo(type.getScope()).toCustomizationList());
167: className = builder.getNameConverter().toClassName(
168: element.getName());
169:
170: BISchemaBinding sb = builder.getBindInfo(
171: type.getOwnerSchema()).get(
172: BISchemaBinding.class);
173: if (sb != null)
174: className = sb
175: .mangleAnonymousTypeClassName(className);
176: scope = selector.getClassScope();
177: }
178:
179: return new CClassInfo(model, scope, className, type
180: .getLocator(), null, null, type, bi
181: .toCustomizationList());
182: }
183: }
184:
185: private QName getTypeName(XSType type) {
186: return new QName(type.getTargetNamespace(), type.getName());
187: }
188:
189: private QName getTypeName(XSComplexType type) {
190: if (type.getRedefinedBy() != null)
191: return null;
192: else
193: return getTypeName((XSType) type);
194: }
195:
196: /**
197: * Returns true if the complex type of the given element can be "optimized away"
198: * and unified with its parent element decl to form a single class.
199: */
200: private boolean isCollapsable(XSElementDecl decl) {
201: XSType type = decl.getType();
202:
203: if (!type.isComplexType())
204: return false; // not a complex type
205:
206: if (decl.getSubstitutables().size() > 1
207: || decl.getSubstAffiliation() != null)
208: // because element substitution calls for a proper JAXBElement hierarchy
209: return false;
210:
211: if (decl.isNillable())
212: // because nillable needs JAXBElement to represent correctly
213: return false;
214:
215: if (getGlobalBinding().isSimpleMode() && decl.isGlobal()) {
216: // in the simple mode, we do more aggressive optimization, and get rid of
217: // a complex type class if it's only used once from a global element
218: XSElementDecl referer = getSoleElementReferer(decl
219: .getType());
220: if (referer != null) {
221: assert referer == decl; // I must be the sole referer
222: return true;
223: }
224: }
225:
226: if (!type.isLocal() || !type.isComplexType())
227: return false;
228:
229: return true;
230: }
231:
232: /**
233: * If only one global {@link XSElementDecl} is refering to {@link XSType},
234: * return that element, otherwise null.
235: */
236: private @Nullable
237: XSElementDecl getSoleElementReferer(@NotNull
238: XSType t) {
239: Set<XSComponent> referer = builder.getReferer(t);
240:
241: XSElementDecl sole = null;
242: for (XSComponent r : referer) {
243: if (r instanceof XSElementDecl) {
244: XSElementDecl x = (XSElementDecl) r;
245: if (!x.isGlobal())
246: // local element references can be ignored, as their names are either given
247: // by the property, or by the JAXBElement (for things like mixed contents)
248: continue;
249: if (sole == null)
250: sole = x;
251: else
252: return null; // more than one
253: } else {
254: // if another type refers to this type, that means
255: // this type has a sub-type, so type substitution is possible now.
256: return null;
257: }
258: }
259:
260: return sole;
261: }
262:
263: public CElement elementDecl(XSElementDecl decl) {
264: CElement r = allow(decl, decl.getName());
265:
266: if (r == null) {
267: QName tagName = new QName(decl.getTargetNamespace(), decl
268: .getName());
269: CCustomizations custs = builder.getBindInfo(decl)
270: .toCustomizationList();
271:
272: if (decl.isGlobal()) {
273: if (isCollapsable(decl)) {
274: // we want the returned type to be built as a complex type,
275: // so the binding cannot be delayed.
276: return selector.bindToType(decl.getType()
277: .asComplexType(), true);
278: } else {
279: String className = null;
280: if (getGlobalBinding().isGenerateElementClass())
281: className = deriveName(decl);
282:
283: // otherwise map global elements to JAXBElement
284: CElementInfo cei = new CElementInfo(model, tagName,
285: selector.getClassScope(), className, custs,
286: decl.getLocator());
287: selector.boundElements.put(decl, cei);
288:
289: stb.refererStack.push(decl); // referer is element
290: cei.initContentType(selector.bindToType(decl
291: .getType()), decl, decl.getDefaultValue());
292: stb.refererStack.pop();
293: r = cei;
294: }
295: }
296: }
297:
298: // have the substitution member derive from the substitution head
299: XSElementDecl top = decl.getSubstAffiliation();
300: if (top != null) {
301: CElement topci = selector.bindToType(top);
302:
303: if (r instanceof CClassInfo && topci instanceof CClassInfo)
304: ((CClassInfo) r).setBaseClass((CClassInfo) topci);
305: if (r instanceof CElementInfo
306: && topci instanceof CElementInfo)
307: ((CElementInfo) r)
308: .setSubstitutionHead((CElementInfo) topci);
309: }
310:
311: TODO.checkSpec();
312:
313: return r;
314: }
315:
316: public CClassInfo empty(XSContentType ct) {
317: return null;
318: }
319:
320: public CClassInfo identityConstraint(
321: XSIdentityConstraint xsIdentityConstraint) {
322: return never();
323: }
324:
325: public CClassInfo xpath(XSXPath xsxPath) {
326: return never();
327: }
328:
329: public CClassInfo attributeUse(XSAttributeUse use) {
330: return never();
331: }
332:
333: public CElement simpleType(XSSimpleType type) {
334: CElement c = allow(type, type.getName());
335: if (c != null)
336: return c;
337:
338: if (getGlobalBinding().isSimpleTypeSubstitution()
339: && type.isGlobal()) {
340: return new CClassInfo(model, selector.getClassScope(),
341: deriveName(type), type.getLocator(),
342: getTypeName(type), null, type, null);
343: }
344:
345: return never();
346: }
347:
348: public CClassInfo particle(XSParticle particle) {
349: return never();
350: }
351:
352: public CClassInfo wildcard(XSWildcard wc) {
353: return never();
354: }
355:
356: // these methods won't be used
357: public CClassInfo annotation(XSAnnotation annon) {
358: assert false;
359: return null;
360: }
361:
362: public CClassInfo notation(XSNotation not) {
363: assert false;
364: return null;
365: }
366:
367: public CClassInfo facet(XSFacet decl) {
368: assert false;
369: return null;
370: }
371:
372: public CClassInfo schema(XSSchema schema) {
373: assert false;
374: return null;
375: }
376:
377: /**
378: * Makes sure that the component doesn't carry a {@link BIClass}
379: * customization.
380: *
381: * @return
382: * return value is unused. Since most of the caller needs to
383: * return null, to make the code a little bit shorter, this
384: * method always return null (so that the caller can always
385: * say <code>return never(sc);</code>.
386: */
387: private CClassInfo never() {
388: // all we need to do here is just not to acknowledge
389: // any class customization. Then this class customization
390: // will be reported as an error later when we check all
391: // unacknowledged customizations.
392:
393: // BIDeclaration cust=owner.getBindInfo(component).get(BIClass.NAME);
394: // if(cust!=null) {
395: // // error
396: // owner.errorReporter.error(
397: // cust.getLocation(),
398: // "test {0}", NameGetter.get(component) );
399: // }
400: return null;
401: }
402:
403: /**
404: * Checks if a component carries a customization to map it to a class.
405: * If so, make it a class.
406: *
407: * @param defaultBaseName
408: * The token which will be used as the basis of the class name
409: * if the class name is not specified in the customization.
410: * This is usually the name of an element declaration, and so on.
411: *
412: * This parameter can be null, in that case it would be an error
413: * if a name is not given by the customization.
414: */
415: private CElement allow(XSComponent component, String defaultBaseName) {
416: BindInfo bindInfo = builder.getBindInfo(component);
417: BIClass decl = bindInfo.get(BIClass.class);
418: if (decl == null)
419: return null;
420:
421: decl.markAsAcknowledged();
422:
423: // determine the package to put this class in.
424:
425: String clsName = decl.getClassName();
426: if (clsName == null) {
427: // if the customiztion doesn't give us a name, derive one
428: // from the current component.
429: if (defaultBaseName == null) {
430: Ring
431: .get(ErrorReceiver.class)
432: .error(
433: decl.getLocation(),
434: Messages
435: .format(Messages.ERR_CLASS_NAME_IS_REQUIRED));
436:
437: // recover by generating a pseudo-random name
438: defaultBaseName = "undefined" + component.hashCode();
439: }
440: clsName = deriveName(defaultBaseName, component);
441: } else {
442: if (!JJavaName.isJavaIdentifier(clsName)) {
443: // not a valid Java class name
444: Ring.get(ErrorReceiver.class).error(
445: decl.getLocation(),
446: Messages.format(
447: Messages.ERR_INCORRECT_CLASS_NAME,
448: clsName));
449: // recover by a dummy name
450: clsName = "Undefined" + component.hashCode();
451: }
452: }
453:
454: QName typeName = null;
455: QName elementName = null;
456:
457: if (component instanceof XSType) {
458: XSType t = (XSType) component;
459: if (t.isGlobal())
460: typeName = new QName(t.getTargetNamespace(), t
461: .getName());
462: }
463:
464: if (component instanceof XSElementDecl) {
465: XSElementDecl e = (XSElementDecl) component;
466: elementName = new QName(e.getTargetNamespace(), e.getName());
467: }
468:
469: if (component instanceof XSElementDecl
470: && !isCollapsable((XSElementDecl) component)) {
471: XSElementDecl e = ((XSElementDecl) component);
472:
473: CElementInfo cei = new CElementInfo(model, elementName,
474: selector.getClassScope(), clsName, bindInfo
475: .toCustomizationList(), decl.getLocation());
476: selector.boundElements.put(e, cei);
477:
478: stb.refererStack.push(component); // referer is element
479: cei.initContentType(selector.bindToType(e.getType()), e, e
480: .getDefaultValue());
481: stb.refererStack.pop();
482: return cei;
483: // TODO: support javadoc and userSpecifiedImplClass
484: } else {
485: CClassInfo bt = new CClassInfo(model, selector
486: .getClassScope(), clsName, decl.getLocation(),
487: typeName, elementName, component, bindInfo
488: .toCustomizationList());
489:
490: // set javadoc class comment.
491: if (decl.getJavadoc() != null)
492: bt.javadoc = decl.getJavadoc() + "\n\n";
493: // add extra blank lines so that the schema fragment
494: // and user-specified javadoc would be separated
495:
496: // if the implClass is given, set it to ClassItem
497: String implClass = decl.getUserSpecifiedImplClass();
498: if (implClass != null)
499: bt.setUserSpecifiedImplClass(implClass);
500:
501: return bt;
502: }
503: }
504:
505: private BIGlobalBinding getGlobalBinding() {
506: return builder.getGlobalBinding();
507: }
508:
509: /**
510: * Derives a name from a schema component.
511: * Use the name of the schema component as the default name.
512: */
513: private String deriveName(XSDeclaration comp) {
514: return deriveName(comp.getName(), comp);
515: }
516:
517: /**
518: * Derives a name from a schema component.
519: * For complex types, we take redefinition into account when
520: * deriving a default name.
521: */
522: private String deriveName(XSComplexType comp) {
523: String seed = deriveName(comp.getName(), comp);
524: int cnt = comp.getRedefinedCount();
525: for (; cnt > 0; cnt--)
526: seed = "Original" + seed;
527: return seed;
528: }
529:
530: /**
531: * Derives a name from a schema component.
532: *
533: * This method handles prefix/suffix modification and
534: * XML-to-Java name conversion.
535: *
536: * @param name
537: * The base name. This should be things like element names
538: * or type names.
539: * @param comp
540: * The component from which the base name was taken.
541: * Used to determine how names are modified.
542: */
543: private String deriveName(String name, XSComponent comp) {
544: XSSchema owner = comp.getOwnerSchema();
545:
546: name = builder.getNameConverter().toClassName(name);
547:
548: if (owner != null) {
549: BISchemaBinding sb = builder.getBindInfo(owner).get(
550: BISchemaBinding.class);
551:
552: if (sb != null)
553: name = sb.mangleClassName(name, comp);
554: }
555:
556: return name;
557: }
558: }
|