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