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.io.StringWriter;
029: import java.math.BigInteger;
030: import java.text.ParseException;
031: import java.util.ArrayList;
032: import java.util.Collections;
033: import java.util.HashMap;
034: import java.util.HashSet;
035: import java.util.List;
036: import java.util.Map;
037: import java.util.Set;
038: import java.util.Stack;
039:
040: import javax.activation.MimeTypeParseException;
041: import javax.xml.namespace.QName;
042:
043: import com.sun.codemodel.internal.JJavaName;
044: import com.sun.codemodel.internal.util.JavadocEscapeWriter;
045: import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo;
046: import com.sun.tools.internal.xjc.model.CClassInfo;
047: import com.sun.tools.internal.xjc.model.CClassInfoParent;
048: import com.sun.tools.internal.xjc.model.CEnumConstant;
049: import com.sun.tools.internal.xjc.model.CEnumLeafInfo;
050: import com.sun.tools.internal.xjc.model.CNonElement;
051: import com.sun.tools.internal.xjc.model.Model;
052: import com.sun.tools.internal.xjc.model.TypeUse;
053: import com.sun.tools.internal.xjc.model.TypeUseFactory;
054: import com.sun.tools.internal.xjc.reader.Ring;
055: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIConversion;
056: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnum;
057: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnumMember;
058: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIProperty;
059: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo;
060: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.EnumMemberMode;
061: import com.sun.tools.internal.xjc.util.MimeTypeRange;
062: import com.sun.xml.internal.bind.DatatypeConverterImpl;
063: import com.sun.xml.internal.bind.v2.WellKnownNamespace;
064: import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_MIME_URI;
065: import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter;
066: import com.sun.xml.internal.xsom.XSAttributeDecl;
067: import com.sun.xml.internal.xsom.XSComplexType;
068: import com.sun.xml.internal.xsom.XSComponent;
069: import com.sun.xml.internal.xsom.XSElementDecl;
070: import com.sun.xml.internal.xsom.XSFacet;
071: import com.sun.xml.internal.xsom.XSListSimpleType;
072: import com.sun.xml.internal.xsom.XSRestrictionSimpleType;
073: import com.sun.xml.internal.xsom.XSSimpleType;
074: import com.sun.xml.internal.xsom.XSUnionSimpleType;
075: import com.sun.xml.internal.xsom.XSVariety;
076: import com.sun.xml.internal.xsom.impl.util.SchemaWriter;
077: import com.sun.xml.internal.xsom.visitor.XSSimpleTypeFunction;
078: import com.sun.xml.internal.xsom.visitor.XSVisitor;
079:
080: import org.xml.sax.Locator;
081:
082: /**
083: * Builds {@link TypeUse} from simple types.
084: *
085: * <p>
086: * This code consists of two main portions. The {@link #compose(XSSimpleType)} method
087: * and {@link #composer} forms an outer cycle, which gradually ascends the type
088: * inheritance chain until it finds the suitable binding. When it does this
089: * {@link #initiatingType} is set to the type which started binding, so that we can refer
090: * to the actual constraint facets and such that are applicable on the type.
091: *
092: * <p>
093: * For each intermediate type in the chain, the {@link #find(XSSimpleType)} method
094: * is used to find the binding on that type, sine the outer loop is doing the ascending,
095: * this method only sees if the current type has some binding available.
096: *
097: * <p>
098: * There is at least one ugly code that you need to aware of
099: * when you are modifying the code. See the documentation
100: * about <a href="package.html#stref_cust">
101: * "simple type customization at the point of reference."</a>
102: *
103: *
104: * @author
105: * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
106: */
107: public final class SimpleTypeBuilder extends BindingComponent {
108:
109: protected final BGMBuilder builder = Ring.get(BGMBuilder.class);
110:
111: private final Model model = Ring.get(Model.class);
112:
113: /**
114: * The component that is refering to the simple type
115: * which we are building. This is ugly but necessary
116: * to support the customization of simple types at
117: * its point of reference. See my comment at the header
118: * of this class for details.
119: *
120: * UGLY: Implemented as a Stack of XSComponent to fix a bug
121: */
122: public final Stack<XSComponent> refererStack = new Stack<XSComponent>();
123:
124: /**
125: * The type that was originally passed to this {@link SimpleTypeBuilder#build(XSSimpleType)}.
126: * Never null.
127: */
128: private XSSimpleType initiatingType;
129:
130: /** {@link TypeUse}s for the built-in types. Read-only. */
131: public static final Map<String, TypeUse> builtinConversions = new HashMap<String, TypeUse>();
132:
133: /**
134: * Entry point from outside. Builds a BGM type expression
135: * from a simple type schema component.
136: *
137: * @param type
138: * the simple type to be bound.
139: */
140: public TypeUse build(XSSimpleType type) {
141: XSSimpleType oldi = initiatingType;
142: this .initiatingType = type;
143:
144: TypeUse e = checkRefererCustomization(type);
145: if (e == null)
146: e = compose(type);
147:
148: initiatingType = oldi;
149:
150: return e;
151: }
152:
153: /**
154: * A version of the {@link #build(XSSimpleType)} method
155: * used to bind the definition of a class generated from
156: * the given simple type.
157: */
158: public TypeUse buildDef(XSSimpleType type) {
159: XSSimpleType oldi = initiatingType;
160: this .initiatingType = type;
161:
162: TypeUse e = type.apply(composer);
163:
164: initiatingType = oldi;
165:
166: return e;
167: }
168:
169: /**
170: * Returns a javaType customization specified to the referer, if present.
171: * @return can be null.
172: */
173: private BIConversion getRefererCustomization() {
174: BindInfo info = builder.getBindInfo(getReferer());
175: BIProperty prop = info.get(BIProperty.class);
176: if (prop == null)
177: return null;
178: return prop.getConv();
179: }
180:
181: public XSComponent getReferer() {
182: return refererStack.peek();
183: }
184:
185: /**
186: * Checks if the referer has a conversion customization or not.
187: * If it does, use it to bind this simple type. Otherwise
188: * return null;
189: */
190: private TypeUse checkRefererCustomization(XSSimpleType type) {
191:
192: // assertion check. referer must be set properly
193: // before the build method is called.
194: // since the handling of the simple type point-of-reference
195: // customization is very error prone, it deserves a strict
196: // assertion check.
197: // UGLY CODE WARNING
198: XSComponent top = getReferer();
199:
200: if (top instanceof XSElementDecl) {
201: // if the parent is element type, its content type must be us.
202: XSElementDecl eref = (XSElementDecl) top;
203: assert eref.getType() == type;
204:
205: // for elements, you can't use <property>,
206: // so we allow javaType to appear directly.
207: BindInfo info = builder.getBindInfo(top);
208: BIConversion conv = info.get(BIConversion.class);
209: if (conv != null) {
210: conv.markAsAcknowledged();
211: // the conversion is given.
212: return conv.getTypeUse(type);
213: }
214: detectJavaTypeCustomization();
215: } else if (top instanceof XSAttributeDecl) {
216: XSAttributeDecl aref = (XSAttributeDecl) top;
217: assert aref.getType() == type;
218: detectJavaTypeCustomization();
219: } else if (top instanceof XSComplexType) {
220: XSComplexType tref = (XSComplexType) top;
221: assert tref.getBaseType() == type
222: || tref.getContentType() == type;
223: detectJavaTypeCustomization();
224: } else if (top == type) {
225: // this means the simple type is built by itself and
226: // not because it's referenced by something.
227: } else
228: // unexpected referer type.
229: assert false;
230:
231: // now we are certain that the referer is OK.
232: // see if it has a conversion customization.
233: BIConversion conv = getRefererCustomization();
234: if (conv != null) {
235: conv.markAsAcknowledged();
236: // the conversion is given.
237: return conv.getTypeUse(type);
238: } else
239: // not found
240: return null;
241: }
242:
243: /**
244: * Detect "javaType" customizations placed directly on simple types, rather
245: * than being enclosed by "property" and "baseType" customizations (see
246: * sec 6.8.1 of the spec).
247: *
248: * Report an error if any exist.
249: */
250: private void detectJavaTypeCustomization() {
251: BindInfo info = builder.getBindInfo(getReferer());
252: BIConversion conv = info.get(BIConversion.class);
253:
254: if (conv != null) {
255: // ack this conversion to prevent further error messages
256: conv.markAsAcknowledged();
257:
258: // report the error
259: getErrorReporter()
260: .error(
261: conv.getLocation(),
262: Messages.ERR_UNNESTED_JAVATYPE_CUSTOMIZATION_ON_SIMPLETYPE);
263: }
264: }
265:
266: /**
267: * Recursively decend the type inheritance chain to find a binding.
268: */
269: TypeUse compose(XSSimpleType t) {
270: TypeUse e = find(t);
271: if (e != null)
272: return e;
273: return t.apply(composer);
274: }
275:
276: public final XSSimpleTypeFunction<TypeUse> composer = new XSSimpleTypeFunction<TypeUse>() {
277:
278: public TypeUse listSimpleType(XSListSimpleType type) {
279: // bind item type individually and then compose them into a list
280: // facets on the list shouldn't be taken account when binding item types,
281: // so weed to call build(), not compose().
282: XSSimpleType itemType = type.getItemType();
283: refererStack.push(itemType);
284: TypeUse tu = TypeUseFactory.makeCollection(build(type
285: .getItemType()));
286: refererStack.pop();
287: return tu;
288: }
289:
290: public TypeUse unionSimpleType(XSUnionSimpleType type) {
291: boolean isCollection = false;
292: for (int i = 0; i < type.getMemberSize(); i++)
293: if (type.getMember(i).getVariety() == XSVariety.LIST) {
294: isCollection = true;
295: break;
296: }
297:
298: TypeUse r = CBuiltinLeafInfo.STRING;
299: if (isCollection)
300: r = TypeUseFactory.makeCollection(r);
301: return r;
302: }
303:
304: public TypeUse restrictionSimpleType(
305: XSRestrictionSimpleType type) {
306: // just process the base type.
307: return compose(type.getSimpleBaseType());
308: }
309: };
310:
311: /**
312: * Checks if there's any binding available on the given type.
313: *
314: * @return
315: * null if not (which causes the {@link #compose(XSSimpleType)} method
316: * to do ascending.
317: */
318: private TypeUse find(XSSimpleType type) {
319: TypeUse r;
320: boolean noAutoEnum = false;
321:
322: // check for user specified conversion
323: BindInfo info = builder.getBindInfo(type);
324: BIConversion conv = info.get(BIConversion.class);
325:
326: if (conv != null) {
327: // a conversion was found
328: conv.markAsAcknowledged();
329: return conv.getTypeUse(type);
330: }
331:
332: // look for enum customization, which is noather user specified conversion
333: BIEnum en = info.get(BIEnum.class);
334: if (en != null) {
335: en.markAsAcknowledged();
336:
337: if (!en.isMapped()) {
338: noAutoEnum = true;
339: } else {
340: // if an enum customization is specified, make sure
341: // the type is OK
342: if (!canBeMappedToTypeSafeEnum(type)) {
343: getErrorReporter().error(en.getLocation(),
344: Messages.ERR_CANNOT_BE_TYPE_SAFE_ENUM);
345: getErrorReporter()
346: .error(
347: type.getLocator(),
348: Messages.ERR_CANNOT_BE_TYPE_SAFE_ENUM_LOCATION);
349: // recover by ignoring this customization
350: return null;
351: }
352: // list and union cannot be mapped to a type-safe enum,
353: // so in this stage we can safely cast it to XSRestrictionSimpleType
354: return bindToTypeSafeEnum(
355: (XSRestrictionSimpleType) type, en.className,
356: en.javadoc, en.members, getEnumMemberMode()
357: .getModeWithEnum(), en.getLocation());
358: }
359: }
360:
361: // if the type is built in, look for the default binding
362: if (type.getTargetNamespace().equals(
363: WellKnownNamespace.XML_SCHEMA)) {
364: String name = type.getName();
365: if (name != null) {
366: r = lookupBuiltin(name);
367: if (r != null)
368: return r;
369: }
370: }
371:
372: // also check for swaRef
373: if (type.getTargetNamespace()
374: .equals(WellKnownNamespace.SWA_URI)) {
375: String name = type.getName();
376: if (name != null && name.equals("swaRef"))
377: return CBuiltinLeafInfo.STRING.makeAdapted(
378: SwaRefAdapter.class, false);
379: }
380:
381: // see if this type should be mapped to a type-safe enumeration by default.
382: // if so, built a EnumXDucer from it and return it.
383: if (type.isRestriction() && !noAutoEnum) {
384: XSRestrictionSimpleType rst = type.asRestriction();
385: if (shouldBeMappedToTypeSafeEnumByDefault(rst)) {
386: r = bindToTypeSafeEnum(rst, null, null, Collections
387: .<String, BIEnumMember> emptyMap(),
388: getEnumMemberMode(), null);
389: if (r != null)
390: return r;
391: }
392: }
393:
394: return getClassSelector()._bindToClass(type, false);
395: }
396:
397: /**
398: * Returns true if a type-safe enum should be created from
399: * the given simple type by default without an explicit <jaxb:enum> customization.
400: */
401: private boolean shouldBeMappedToTypeSafeEnumByDefault(
402: XSRestrictionSimpleType type) {
403:
404: // if not, there will be a problem wrt the class name of this type safe enum type.
405: if (type.isLocal())
406: return false;
407:
408: // if redefined, we should map the new definition, not the old one.
409: if (type.getRedefinedBy() != null)
410: return false;
411:
412: List<XSFacet> facets = type
413: .getDeclaredFacets(XSFacet.FACET_ENUMERATION);
414: if (facets.isEmpty()
415: || facets.size() > builder.getGlobalBinding()
416: .getDefaultEnumMemberSizeCap())
417: // if the type itself doesn't have the enumeration facet,
418: // it won't be mapped to a type-safe enum.
419: //
420: // if there are too many facets, it's not very useful
421: return false;
422:
423: if (!canBeMappedToTypeSafeEnum(type))
424: // we simply can't map this to an enumeration
425: return false;
426:
427: // check for collisions among constant names. if a collision will happen,
428: // don't try to bind it to an enum.
429:
430: // return true only when this type is derived from one of the "enum base type".
431: for (XSSimpleType t = type; t != null; t = t
432: .getSimpleBaseType())
433: if (t.isGlobal()
434: && builder.getGlobalBinding()
435: .canBeMappedToTypeSafeEnum(t))
436: return true;
437:
438: return false;
439: }
440:
441: private static final Set<String> builtinTypeSafeEnumCapableTypes;
442:
443: static {
444: Set<String> s = new HashSet<String>();
445:
446: // see a bullet of 6.5.1 of the spec.
447: String[] typeNames = new String[] { "string", "boolean",
448: "float", "decimal", "double", "anyURI" };
449:
450: for (String type : typeNames)
451: s.add(type);
452:
453: builtinTypeSafeEnumCapableTypes = Collections
454: .unmodifiableSet(s);
455: }
456:
457: /**
458: * Returns true if the given simple type can be mapped to a
459: * type-safe enum class.
460: *
461: * <p>
462: * JAXB spec places a restrictrion as to what type can be
463: * mapped to a type-safe enum. This method enforces this
464: * constraint.
465: */
466: public static boolean canBeMappedToTypeSafeEnum(XSSimpleType type) {
467: do {
468: if (WellKnownNamespace.XML_SCHEMA.equals(type
469: .getTargetNamespace())) {
470: // type must be derived from one of these types
471: String localName = type.getName();
472: if (localName != null) {
473: if (localName.equals("anySimpleType"))
474: return false; // catch all case
475: if (localName.equals("ID")
476: || localName.equals("IDREF"))
477: return false; // not ID/IDREF
478:
479: // other allowed list
480: if (builtinTypeSafeEnumCapableTypes
481: .contains(localName))
482: return true;
483: }
484: }
485:
486: type = type.getSimpleBaseType();
487: } while (type != null);
488:
489: return false;
490: }
491:
492: /**
493: * Builds a type-safe enum conversion from a simple type
494: * with enumeration facets.
495: *
496: * @param className
497: * The class name of the type-safe enum. Or null to
498: * create a default name.
499: * @param javadoc
500: * Additional javadoc that will be added at the beginning of the
501: * class, or null if none is necessary.
502: * @param members
503: * A map from enumeration values (as String) to BIEnumMember objects.
504: * if some of the value names need to be overrided.
505: * Cannot be null, but the map may not contain entries
506: * for all enumeration values.
507: * @param loc
508: * The source location where the above customizations are
509: * specified, or null if none is available.
510: */
511: private TypeUse bindToTypeSafeEnum(XSRestrictionSimpleType type,
512: String className, String javadoc,
513: Map<String, BIEnumMember> members, EnumMemberMode mode,
514: Locator loc) {
515:
516: if (loc == null) // use the location of the simple type as the default
517: loc = type.getLocator();
518:
519: if (className == null) {
520: // infer the class name. For this to be possible,
521: // the simple type must be a global one.
522: if (!type.isGlobal()) {
523: getErrorReporter().error(loc,
524: Messages.ERR_NO_ENUM_NAME_AVAILABLE);
525: // recover by returning a meaningless conversion
526: return CBuiltinLeafInfo.STRING;
527: }
528: className = type.getName();
529: }
530: // we apply name conversion in any case
531: className = builder.getNameConverter().toClassName(className);
532:
533: {// compute Javadoc
534: StringWriter out = new StringWriter();
535: SchemaWriter sw = new SchemaWriter(new JavadocEscapeWriter(
536: out));
537: type.visit((XSVisitor) sw);
538:
539: if (javadoc != null)
540: javadoc += "\n\n";
541: else
542: javadoc = "";
543:
544: javadoc += Messages.format(Messages.JAVADOC_HEADING, type
545: .getName())
546: + "\n<p>\n<pre>\n" + out.getBuffer() + "</pre>";
547:
548: }
549:
550: // build base type
551: refererStack.push(type.getSimpleBaseType());
552: TypeUse use = build(type.getSimpleBaseType());
553: refererStack.pop();
554:
555: if (use.isCollection())
556: return null; // can't bind a list to enum constant
557:
558: CNonElement baseDt = (CNonElement) use.getInfo(); // for now just ignore that case
559:
560: if (baseDt instanceof CClassInfo)
561: return null; // can't bind to an enum if the base is a class, since we don't have the value constrctor
562:
563: // if the member names collide, re-generate numbered constant names.
564: XSFacet[] errorRef = new XSFacet[1];
565: List<CEnumConstant> memberList = buildCEnumConstants(type,
566: false, members, errorRef);
567: if (memberList == null
568: || checkMemberNameCollision(memberList) != null) {
569: switch (mode) {
570: case SKIP:
571: // abort
572: return null;
573: case ERROR:
574: // error
575: if (memberList == null) {
576: getErrorReporter().error(errorRef[0].getLocator(),
577: Messages.ERR_CANNOT_GENERATE_ENUM_NAME,
578: errorRef[0].getValue());
579: } else {
580: CEnumConstant[] collision = checkMemberNameCollision(memberList);
581: getErrorReporter().error(collision[0].getLocator(),
582: Messages.ERR_ENUM_MEMBER_NAME_COLLISION,
583: collision[0].getName());
584: getErrorReporter()
585: .error(
586: collision[1].getLocator(),
587: Messages.ERR_ENUM_MEMBER_NAME_COLLISION_RELATED);
588: }
589: return null; // recover from error
590: case GENERATE:
591: // generate
592: memberList = buildCEnumConstants(type, true, members,
593: null);
594: break;
595: }
596: }
597:
598: QName typeName = null;
599: if (type.isGlobal())
600: typeName = new QName(type.getTargetNamespace(), type
601: .getName());
602:
603: // use the name of the simple type as the name of the class.
604: CClassInfoParent scope;
605: if (type.isGlobal())
606: scope = new CClassInfoParent.Package(getClassSelector()
607: .getPackage(type.getTargetNamespace()));
608: else
609: scope = getClassSelector().getClassScope();
610: CEnumLeafInfo xducer = new CEnumLeafInfo(model, typeName,
611: scope, className, baseDt, memberList, type, builder
612: .getBindInfo(type).toCustomizationList(), loc);
613: xducer.javadoc = javadoc;
614:
615: BIConversion conv = new BIConversion.Static(type.getLocator(),
616: xducer);
617: conv.markAsAcknowledged();
618:
619: // attach this new conversion object to this simple type
620: // so that successive look up will use the same object.
621: builder.getOrCreateBindInfo(type).addDecl(conv);
622:
623: return conv.getTypeUse(type);
624: }
625:
626: /**
627: *
628: * @param errorRef
629: * if constant names couldn't be generated, return a reference to that enum facet.
630: * @return
631: * null if unable to generate names for some of the constants.
632: */
633: private List<CEnumConstant> buildCEnumConstants(
634: XSRestrictionSimpleType type,
635: boolean needsToGenerateMemberName,
636: Map<String, BIEnumMember> members, XSFacet[] errorRef) {
637: List<CEnumConstant> memberList = new ArrayList<CEnumConstant>();
638: int idx = 1;
639: for (XSFacet facet : type
640: .getDeclaredFacets(XSFacet.FACET_ENUMERATION)) {
641: String name = null;
642: String mdoc = null;
643:
644: if (needsToGenerateMemberName) {
645: // generate names for all member names.
646: // this will even override names specified by the user. that's crazy.
647: name = "VALUE_" + (idx++);
648: } else {
649: String facetValue = facet.getValue().value;
650: BIEnumMember mem = members.get(facetValue);
651: if (mem == null)
652: // look at the one attached to the facet object
653: mem = builder.getBindInfo(facet).get(
654: BIEnumMember.class);
655:
656: if (mem != null) {
657: name = mem.name;
658: mdoc = mem.javadoc;
659: }
660:
661: if (name == null) {
662: StringBuilder sb = new StringBuilder();
663: for (int i = 0; i < facetValue.length(); i++) {
664: char ch = facetValue.charAt(i);
665: if (Character.isJavaIdentifierPart(ch))
666: sb.append(ch);
667: else
668: sb.append('_');
669: }
670: name = model.getNameConverter().toConstantName(
671: sb.toString());
672: }
673: }
674:
675: if (!JJavaName.isJavaIdentifier(name)) {
676: if (errorRef != null)
677: errorRef[0] = facet;
678: return null; // unable to generate a name
679: }
680:
681: memberList.add(new CEnumConstant(name, mdoc, facet
682: .getValue().value, facet.getLocator()));
683: }
684: return memberList;
685: }
686:
687: /**
688: * Returns non-null if {@link CEnumConstant}s have name collisions among them.
689: *
690: * @return
691: * if there's a collision, return two {@link CEnumConstant}s that collided.
692: * otherwise return null.
693: */
694: private CEnumConstant[] checkMemberNameCollision(
695: List<CEnumConstant> memberList) {
696: Map<String, CEnumConstant> names = new HashMap<String, CEnumConstant>();
697: for (CEnumConstant c : memberList) {
698: CEnumConstant old = names.put(c.getName(), c);
699: if (old != null)
700: // collision detected
701: return new CEnumConstant[] { old, c };
702: }
703: return null;
704: }
705:
706: private EnumMemberMode getEnumMemberMode() {
707: return builder.getGlobalBinding().getEnumMemberMode();
708: }
709:
710: private TypeUse lookupBuiltin(String typeLocalName) {
711: if (typeLocalName.equals("integer")
712: || typeLocalName.equals("long")) {
713: /*
714: attempt an optimization so that we can
715: improve the binding for types like this:
716:
717: <simpleType>
718: <restriciton baseType="integer">
719: <maxInclusive value="100" />
720: </
721: </
722:
723: ... to int, not BigInteger.
724: */
725:
726: BigInteger xe = readFacet(XSFacet.FACET_MAXEXCLUSIVE, -1);
727: BigInteger xi = readFacet(XSFacet.FACET_MAXINCLUSIVE, 0);
728: BigInteger max = min(xe, xi); // most restrictive one takes precedence
729:
730: if (max != null) {
731: BigInteger ne = readFacet(XSFacet.FACET_MINEXCLUSIVE,
732: +1);
733: BigInteger ni = readFacet(XSFacet.FACET_MININCLUSIVE, 0);
734: BigInteger min = max(ne, ni);
735:
736: if (min != null) {
737: if (min.compareTo(INT_MIN) >= 0
738: && max.compareTo(INT_MAX) <= 0)
739: typeLocalName = "int";
740: else if (min.compareTo(LONG_MIN) >= 0
741: && max.compareTo(LONG_MAX) <= 0)
742: typeLocalName = "long";
743: }
744: }
745: } else if (typeLocalName.equals("boolean")
746: && isRestrictedTo0And1()) {
747: // this is seen in the SOAP schema and too common to ignore
748: return CBuiltinLeafInfo.BOOLEAN_ZERO_OR_ONE;
749: } else if (typeLocalName.equals("base64Binary")) {
750: return lookupBinaryTypeBinding();
751: } else if (typeLocalName.equals("anySimpleType")) {
752: if (getReferer() instanceof XSAttributeDecl
753: || getReferer() instanceof XSSimpleType)
754: return CBuiltinLeafInfo.STRING;
755: else
756: return CBuiltinLeafInfo.ANYTYPE;
757: }
758: return builtinConversions.get(typeLocalName);
759: }
760:
761: /**
762: * Decides the way xs:base64Binary binds.
763: *
764: * This method checks the expected media type.
765: */
766: private TypeUse lookupBinaryTypeBinding() {
767: XSComponent referer = getReferer();
768: String emt = referer.getForeignAttribute(XML_MIME_URI,
769: "expectedContentTypes");
770: if (emt != null) {
771: try {
772: // see http://www.xml.com/lpt/a/2004/07/21/dive.html
773: List<MimeTypeRange> types = MimeTypeRange
774: .parseRanges(emt);
775: MimeTypeRange mt = MimeTypeRange.merge(types);
776:
777: // see spec table I-1 in appendix I section 2.1.1 for bindings
778: if (mt.majorType.equals("image"))
779: return CBuiltinLeafInfo.IMAGE.makeMimeTyped(mt
780: .toMimeType());
781:
782: if ((mt.majorType.equals("application") || mt.majorType
783: .equals("text"))
784: && isXml(mt.subType))
785: return CBuiltinLeafInfo.XML_SOURCE.makeMimeTyped(mt
786: .toMimeType());
787:
788: if ((mt.majorType.equals("text") && (mt.subType
789: .equals("plain")))) {
790: return CBuiltinLeafInfo.STRING.makeMimeTyped(mt
791: .toMimeType());
792: }
793:
794: return CBuiltinLeafInfo.DATA_HANDLER.makeMimeTyped(mt
795: .toMimeType());
796: } catch (ParseException e) {
797: getErrorReporter()
798: .error(
799: referer.getLocator(),
800: Messages
801: .format(
802: Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,
803: emt, e.getMessage()));
804: // recover by using the default
805: } catch (MimeTypeParseException e) {
806: getErrorReporter()
807: .error(
808: referer.getLocator(),
809: Messages
810: .format(
811: Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,
812: emt, e.getMessage()));
813: }
814: }
815: // default
816: return CBuiltinLeafInfo.BASE64_BYTE_ARRAY;
817: }
818:
819: /**
820: * Returns true if the specified sub-type is an XML type.
821: */
822: private boolean isXml(String subType) {
823: return subType.equals("xml") || subType.endsWith("+xml");
824: }
825:
826: /**
827: * Returns true if the {@link #initiatingType} is restricted
828: * to '0' and '1'. This logic is not complete, but it at least
829: * finds the such definition in SOAP @mustUnderstand.
830: */
831: private boolean isRestrictedTo0And1() {
832: XSFacet pattern = initiatingType
833: .getFacet(XSFacet.FACET_PATTERN);
834: if (pattern != null) {
835: String v = pattern.getValue().value;
836: if (v.equals("0|1") || v.equals("1|0") || v.equals("\\d"))
837: return true;
838: }
839: XSFacet enumf = initiatingType
840: .getFacet(XSFacet.FACET_ENUMERATION);
841: if (enumf != null) {
842: String v = enumf.getValue().value;
843: if (v.equals("0") || v.equals("1"))
844: return true;
845: }
846: return false;
847: }
848:
849: private BigInteger readFacet(String facetName, int offset) {
850: XSFacet me = initiatingType.getFacet(facetName);
851: if (me == null)
852: return null;
853: BigInteger bi = DatatypeConverterImpl._parseInteger(me
854: .getValue().value);
855: if (offset != 0)
856: bi = bi.add(BigInteger.valueOf(offset));
857: return bi;
858: }
859:
860: private BigInteger min(BigInteger a, BigInteger b) {
861: if (a == null)
862: return b;
863: if (b == null)
864: return a;
865: return a.min(b);
866: }
867:
868: private BigInteger max(BigInteger a, BigInteger b) {
869: if (a == null)
870: return b;
871: if (b == null)
872: return a;
873: return a.max(b);
874: }
875:
876: private static final BigInteger LONG_MIN = BigInteger
877: .valueOf(Long.MIN_VALUE);
878: private static final BigInteger LONG_MAX = BigInteger
879: .valueOf(Long.MAX_VALUE);
880: private static final BigInteger INT_MIN = BigInteger
881: .valueOf(Integer.MIN_VALUE);
882: private static final BigInteger INT_MAX = BigInteger
883: .valueOf(Integer.MAX_VALUE);
884:
885: static {
886: // list of datatypes which have built-in conversions.
887: // note that although xs:token and xs:normalizedString are not
888: // specified in the spec, they need to be here because they
889: // have different whitespace normalization semantics.
890: Map<String, TypeUse> m = builtinConversions;
891:
892: // TODO: this is so dumb
893: m.put("string", CBuiltinLeafInfo.STRING);
894: m.put("anyURI", CBuiltinLeafInfo.STRING);
895: m.put("boolean", CBuiltinLeafInfo.BOOLEAN);
896: // we'll also look at the expected media type, so don't just add this to the map
897: // m.put("base64Binary", CBuiltinLeafInfo.BASE64_BYTE_ARRAY);
898: m.put("hexBinary", CBuiltinLeafInfo.HEXBIN_BYTE_ARRAY);
899: m.put("float", CBuiltinLeafInfo.FLOAT);
900: m.put("decimal", CBuiltinLeafInfo.BIG_DECIMAL);
901: m.put("integer", CBuiltinLeafInfo.BIG_INTEGER);
902: m.put("long", CBuiltinLeafInfo.LONG);
903: m.put("unsignedInt", CBuiltinLeafInfo.LONG);
904: m.put("int", CBuiltinLeafInfo.INT);
905: m.put("unsignedShort", CBuiltinLeafInfo.INT);
906: m.put("short", CBuiltinLeafInfo.SHORT);
907: m.put("unsignedByte", CBuiltinLeafInfo.SHORT);
908: m.put("byte", CBuiltinLeafInfo.BYTE);
909: m.put("double", CBuiltinLeafInfo.DOUBLE);
910: m.put("QName", CBuiltinLeafInfo.QNAME);
911: m.put("NOTATION", CBuiltinLeafInfo.QNAME);
912: m.put("dateTime", CBuiltinLeafInfo.CALENDAR);
913: m.put("date", CBuiltinLeafInfo.CALENDAR);
914: m.put("time", CBuiltinLeafInfo.CALENDAR);
915: m.put("gYearMonth", CBuiltinLeafInfo.CALENDAR);
916: m.put("gYear", CBuiltinLeafInfo.CALENDAR);
917: m.put("gMonthDay", CBuiltinLeafInfo.CALENDAR);
918: m.put("gDay", CBuiltinLeafInfo.CALENDAR);
919: m.put("gMonth", CBuiltinLeafInfo.CALENDAR);
920: m.put("duration", CBuiltinLeafInfo.DURATION);
921: m.put("token", CBuiltinLeafInfo.TOKEN);
922: m.put("normalizedString", CBuiltinLeafInfo.NORMALIZED_STRING);
923: m.put("ID", CBuiltinLeafInfo.ID);
924: m.put("IDREF", CBuiltinLeafInfo.IDREF);
925: // TODO: handling dateTime, time, and date type
926: // String[] names = {
927: // "date", "dateTime", "time", "hexBinary" };
928: }
929: }
|