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