View Javadoc
1   /*
2    * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.tools.internal.xjc.reader.xmlschema;
27  
28  import java.io.StringWriter;
29  import java.math.BigInteger;
30  import java.text.ParseException;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.Collections;
34  import java.util.HashMap;
35  import java.util.HashSet;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  import java.util.Stack;
40  
41  import javax.activation.MimeTypeParseException;
42  import javax.xml.bind.DatatypeConverter;
43  
44  import com.sun.codemodel.internal.JJavaName;
45  import com.sun.codemodel.internal.util.JavadocEscapeWriter;
46  import com.sun.xml.internal.bind.v2.WellKnownNamespace;
47  import com.sun.tools.internal.xjc.ErrorReceiver;
48  import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo;
49  import com.sun.tools.internal.xjc.model.CClassInfo;
50  import com.sun.tools.internal.xjc.model.CClassInfoParent;
51  import com.sun.tools.internal.xjc.model.CClassRef;
52  import com.sun.tools.internal.xjc.model.CEnumConstant;
53  import com.sun.tools.internal.xjc.model.CEnumLeafInfo;
54  import com.sun.tools.internal.xjc.model.CNonElement;
55  import com.sun.tools.internal.xjc.model.Model;
56  import com.sun.tools.internal.xjc.model.TypeUse;
57  import com.sun.tools.internal.xjc.model.TypeUseFactory;
58  import com.sun.tools.internal.xjc.reader.Const;
59  import com.sun.tools.internal.xjc.reader.Ring;
60  import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIConversion;
61  import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnum;
62  import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnumMember;
63  import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIProperty;
64  import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo;
65  import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.EnumMemberMode;
66  import com.sun.tools.internal.xjc.util.MimeTypeRange;
67  
68  import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_MIME_URI;
69  
70  import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapterMarker;
71  import com.sun.xml.internal.xsom.XSAttributeDecl;
72  import com.sun.xml.internal.xsom.XSComplexType;
73  import com.sun.xml.internal.xsom.XSComponent;
74  import com.sun.xml.internal.xsom.XSElementDecl;
75  import com.sun.xml.internal.xsom.XSFacet;
76  import com.sun.xml.internal.xsom.XSListSimpleType;
77  import com.sun.xml.internal.xsom.XSRestrictionSimpleType;
78  import com.sun.xml.internal.xsom.XSSimpleType;
79  import com.sun.xml.internal.xsom.XSUnionSimpleType;
80  import com.sun.xml.internal.xsom.XSVariety;
81  import com.sun.xml.internal.xsom.impl.util.SchemaWriter;
82  import com.sun.xml.internal.xsom.visitor.XSSimpleTypeFunction;
83  import com.sun.xml.internal.xsom.visitor.XSVisitor;
84  
85  import org.xml.sax.Locator;
86  
87  /**
88   * Builds {@link TypeUse} from simple types.
89   *
90   * <p>
91   * This code consists of two main portions. The {@link #compose(XSSimpleType)} method
92   * and {@link #composer} forms an outer cycle, which gradually ascends the type
93   * inheritance chain until it finds the suitable binding. When it does this
94   * {@link #initiatingType} is set to the type which started binding, so that we can refer
95   * to the actual constraint facets and such that are applicable on the type.
96   *
97   * <p>
98   * For each intermediate type in the chain, the {@link #find(XSSimpleType)} method
99   * is used to find the binding on that type, sine the outer loop is doing the ascending,
100  * this method only sees if the current type has some binding available.
101  *
102  * <p>
103  * There is at least one ugly code that you need to aware of
104  * when you are modifying the code. See the documentation
105  * about <a href="package.html#stref_cust">
106  * "simple type customization at the point of reference."</a>
107  *
108  *
109  * @author
110  *     Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
111  */
112 public final class SimpleTypeBuilder extends BindingComponent {
113 
114     protected final BGMBuilder builder = Ring.get(BGMBuilder.class);
115 
116     private final Model model = Ring.get(Model.class);
117 
118     /**
119      * The component that is refering to the simple type
120      * which we are building. This is ugly but necessary
121      * to support the customization of simple types at
122      * its point of reference. See my comment at the header
123      * of this class for details.
124      *
125      * UGLY: Implemented as a Stack of XSComponent to fix a bug
126      */
127     public final Stack<XSComponent> refererStack = new Stack<XSComponent>();
128 
129     /**
130      * Records what xmime:expectedContentTypes annotations we honored and processed,
131      * so that we can later check if the user had these annotations in the places
132      * where we didn't anticipate them.
133      */
134     private final Set<XSComponent> acknowledgedXmimeContentTypes = new HashSet<XSComponent>();
135 
136     /**
137      * The type that was originally passed to this {@link SimpleTypeBuilder#build(XSSimpleType)}.
138      * Never null.
139      */
140     private XSSimpleType initiatingType;
141 
142     /** {@link TypeUse}s for the built-in types. Read-only. */
143     public static final Map<String,TypeUse> builtinConversions = new HashMap<String,TypeUse>();
144 
145 
146     /**
147      * Entry point from outside. Builds a BGM type expression
148      * from a simple type schema component.
149      *
150      * @param type
151      *      the simple type to be bound.
152      */
153     public TypeUse build( XSSimpleType type ) {
154         XSSimpleType oldi = initiatingType;
155         this.initiatingType = type;
156 
157         TypeUse e = checkRefererCustomization(type);
158         if(e==null)
159             e = compose(type);
160 
161         initiatingType = oldi;
162 
163         return e;
164     }
165 
166     /**
167      * A version of the {@link #build(XSSimpleType)} method
168      * used to bind the definition of a class generated from
169      * the given simple type.
170      */
171     public TypeUse buildDef( XSSimpleType type ) {
172         XSSimpleType oldi = initiatingType;
173         this.initiatingType = type;
174 
175         TypeUse e = type.apply(composer);
176 
177         initiatingType = oldi;
178 
179         return e;
180     }
181 
182 
183     /**
184      * Returns a javaType customization specified to the referer, if present.
185      * @return can be null.
186      */
187     private BIConversion getRefererCustomization() {
188         BindInfo info = builder.getBindInfo(getReferer());
189         BIProperty prop = info.get(BIProperty.class);
190         if(prop==null)  return null;
191         return prop.getConv();
192     }
193 
194     public XSComponent getReferer() {
195         return refererStack.peek();
196     }
197 
198     /**
199      * Checks if the referer has a conversion customization or not.
200      * If it does, use it to bind this simple type. Otherwise
201      * return null;
202      */
203     private TypeUse checkRefererCustomization( XSSimpleType type ) {
204 
205         // assertion check. referer must be set properly
206         // before the build method is called.
207         // since the handling of the simple type point-of-reference
208         // customization is very error prone, it deserves a strict
209         // assertion check.
210         // UGLY CODE WARNING
211         XSComponent top = getReferer();
212 
213         if( top instanceof XSElementDecl ) {
214             // if the parent is element type, its content type must be us.
215             XSElementDecl eref = (XSElementDecl)top;
216             assert eref.getType()==type;
217 
218             // for elements, you can't use <property>,
219             // so we allow javaType to appear directly.
220             BindInfo info = builder.getBindInfo(top);
221             BIConversion conv = info.get(BIConversion.class);
222             if(conv!=null) {
223                 conv.markAsAcknowledged();
224                 // the conversion is given.
225                 return conv.getTypeUse(type);
226             }
227             detectJavaTypeCustomization();
228         } else
229         if( top instanceof XSAttributeDecl ) {
230             XSAttributeDecl aref = (XSAttributeDecl)top;
231             assert aref.getType()==type;
232             detectJavaTypeCustomization();
233         } else
234         if( top instanceof XSComplexType ) {
235             XSComplexType tref = (XSComplexType)top;
236             assert tref.getBaseType()==type || tref.getContentType()==type;
237             detectJavaTypeCustomization();
238         } else
239         if( top == type ) {
240             // this means the simple type is built by itself and
241             // not because it's referenced by something.
242         } else
243             // unexpected referer type.
244             assert false;
245 
246         // now we are certain that the referer is OK.
247         // see if it has a conversion customization.
248         BIConversion conv = getRefererCustomization();
249         if(conv!=null) {
250             conv.markAsAcknowledged();
251             // the conversion is given.
252             return conv.getTypeUse(type);
253         } else
254             // not found
255             return null;
256     }
257 
258     /**
259      * Detect "javaType" customizations placed directly on simple types, rather
260      * than being enclosed by "property" and "baseType" customizations (see
261      * sec 6.8.1 of the spec).
262      *
263      * Report an error if any exist.
264      */
265     private void detectJavaTypeCustomization() {
266         BindInfo info = builder.getBindInfo(getReferer());
267         BIConversion conv = info.get(BIConversion.class);
268 
269         if( conv != null ) {
270             // ack this conversion to prevent further error messages
271             conv.markAsAcknowledged();
272 
273             // report the error
274             getErrorReporter().error( conv.getLocation(),
275                     Messages.ERR_UNNESTED_JAVATYPE_CUSTOMIZATION_ON_SIMPLETYPE );
276         }
277     }
278 
279     /**
280      * Recursively decend the type inheritance chain to find a binding.
281      */
282     TypeUse compose( XSSimpleType t ) {
283         TypeUse e = find(t);
284         if(e!=null)     return e;
285         return t.apply(composer);
286     }
287 
288     public final XSSimpleTypeFunction<TypeUse> composer = new XSSimpleTypeFunction<TypeUse>() {
289 
290         public TypeUse listSimpleType(XSListSimpleType type) {
291             // bind item type individually and then compose them into a list
292             // facets on the list shouldn't be taken account when binding item types,
293             // so weed to call build(), not compose().
294             XSSimpleType itemType = type.getItemType();
295             refererStack.push(itemType);
296             TypeUse tu = TypeUseFactory.makeCollection(build(type.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 || type.getMember(i).getVariety()==XSVariety.UNION) {
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(XSRestrictionSimpleType type) {
316             // just process the base type.
317             return compose(type.getSimpleBaseType());
318         }
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().error( type.getLocator(),
357                         Messages.ERR_CANNOT_BE_TYPE_SAFE_ENUM_LOCATION );
358                     // recover by ignoring this customization
359                     return null;
360                 }
361 
362                 // reference?
363                 if(en.ref!=null) {
364                     if(!JJavaName.isFullyQualifiedClassName(en.ref)) {
365                         Ring.get(ErrorReceiver.class).error( en.getLocation(),
366                             Messages.format(Messages.ERR_INCORRECT_CLASS_NAME, en.ref) );
367                         // recover by ignoring @ref
368                         return null;
369                     }
370 
371                     return new CClassRef(model, type, en, info.toCustomizationList() );
372                 }
373 
374                 // list and union cannot be mapped to a type-safe enum,
375                 // so in this stage we can safely cast it to XSRestrictionSimpleType
376                 return bindToTypeSafeEnum( (XSRestrictionSimpleType)type,
377                         en.className, en.javadoc, en.members,
378                         getEnumMemberMode().getModeWithEnum(),
379                         en.getLocation() );
380             }
381         }
382 
383 
384         // if the type is built in, look for the default binding
385         if(type.getTargetNamespace().equals(WellKnownNamespace.XML_SCHEMA)) {
386             String name = type.getName();
387             if(name!=null) {
388                 r = lookupBuiltin(name);
389                 if(r!=null)
390                     return r;
391             }
392         }
393 
394         // also check for swaRef
395         if(type.getTargetNamespace().equals(WellKnownNamespace.SWA_URI)) {
396             String name = type.getName();
397             if(name!=null && name.equals("swaRef"))
398                 return CBuiltinLeafInfo.STRING.makeAdapted(SwaRefAdapterMarker.class,false);
399         }
400 
401 
402         // see if this type should be mapped to a type-safe enumeration by default.
403         // if so, built a EnumXDucer from it and return it.
404         if(type.isRestriction() && !noAutoEnum) {
405             XSRestrictionSimpleType rst = type.asRestriction();
406             if(shouldBeMappedToTypeSafeEnumByDefault(rst)) {
407                 r = bindToTypeSafeEnum(rst,null,null, Collections.<String, BIEnumMember>emptyMap(),
408                             getEnumMemberMode(),null);
409                 if(r!=null)
410                     return r;
411             }
412         }
413 
414         return (CNonElement)getClassSelector()._bindToClass(type,null,false);
415     }
416 
417     private static Set<XSRestrictionSimpleType> reportedEnumMemberSizeWarnings;
418 
419     /**
420      * Returns true if a type-safe enum should be created from
421      * the given simple type by default without an explicit &lt;jaxb:enum> customization.
422      */
423     private boolean shouldBeMappedToTypeSafeEnumByDefault( XSRestrictionSimpleType type ) {
424 
425         // if not, there will be a problem wrt the class name of this type safe enum type.
426         if( type.isLocal() )    return false;
427 
428         // if redefined, we should map the new definition, not the old one.
429         if( type.getRedefinedBy()!=null )   return false;
430 
431         List<XSFacet> facets = type.getDeclaredFacets(XSFacet.FACET_ENUMERATION);
432         if( facets.isEmpty() )
433             // if the type itself doesn't have the enumeration facet,
434             // it won't be mapped to a type-safe enum.
435             return false;
436 
437         if(facets.size() > builder.getGlobalBinding().getDefaultEnumMemberSizeCap()) {
438             // if there are too many facets, it's not very useful
439             // produce warning when simple type is not mapped to enum
440             // see issue https://jaxb.dev.java.net/issues/show_bug.cgi?id=711
441 
442             if(reportedEnumMemberSizeWarnings == null)
443                 reportedEnumMemberSizeWarnings = new HashSet<XSRestrictionSimpleType>();
444 
445             if(!reportedEnumMemberSizeWarnings.contains(type)) {
446                 getErrorReporter().warning(type.getLocator(), Messages.WARN_ENUM_MEMBER_SIZE_CAP,
447                         type.getName(), facets.size(), builder.getGlobalBinding().getDefaultEnumMemberSizeCap());
448 
449                 reportedEnumMemberSizeWarnings.add(type);
450             }
451 
452             return false;
453         }
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.getSimpleBaseType() )
464             if( t.isGlobal() && builder.getGlobalBinding().canBeMappedToTypeSafeEnum(t) )
465                 return true;
466 
467         return false;
468     }
469 
470 
471     private static final Set<String> builtinTypeSafeEnumCapableTypes;
472 
473     static {
474         Set<String> s = new HashSet<String>();
475 
476         // see a bullet of 6.5.1 of the spec.
477         String[] typeNames = new String[] {
478             "string", "boolean", "float", "decimal", "double", "anyURI"
479         };
480         s.addAll(Arrays.asList(typeNames));
481 
482         builtinTypeSafeEnumCapableTypes = Collections.unmodifiableSet(s);
483     }
484 
485 
486     /**
487      * Returns true if the given simple type can be mapped to a
488      * type-safe enum class.
489      *
490      * <p>
491      * JAXB spec places a restrictrion as to what type can be
492      * mapped to a type-safe enum. This method enforces this
493      * constraint.
494      */
495     public static boolean canBeMappedToTypeSafeEnum( XSSimpleType type ) {
496         do {
497             if( WellKnownNamespace.XML_SCHEMA.equals(type.getTargetNamespace()) ) {
498                 // type must be derived from one of these types
499                 String localName = type.getName();
500                 if( localName!=null ) {
501                     if( localName.equals("anySimpleType") )
502                         return false;   // catch all case
503                     if( localName.equals("ID") || localName.equals("IDREF") )
504                         return false;   // not ID/IDREF
505 
506                     // other allowed list
507                     if( builtinTypeSafeEnumCapableTypes.contains(localName) )
508                         return true;
509                 }
510             }
511 
512             type = type.getSimpleBaseType();
513         } while( type!=null );
514 
515         return false;
516     }
517 
518 
519 
520     /**
521      * Builds a type-safe enum conversion from a simple type
522      * with enumeration facets.
523      *
524      * @param className
525      *      The class name of the type-safe enum. Or null to
526      *      create a default name.
527      * @param javadoc
528      *      Additional javadoc that will be added at the beginning of the
529      *      class, or null if none is necessary.
530      * @param members
531      *      A map from enumeration values (as String) to BIEnumMember objects.
532      *      if some of the value names need to be overrided.
533      *      Cannot be null, but the map may not contain entries
534      *      for all enumeration values.
535      * @param loc
536      *      The source location where the above customizations are
537      *      specified, or null if none is available.
538      */
539     private TypeUse bindToTypeSafeEnum( XSRestrictionSimpleType type,
540                                         String className, String javadoc, Map<String,BIEnumMember> members,
541                                         EnumMemberMode mode, Locator loc ) {
542 
543         if( loc==null )  // use the location of the simple type as the default
544             loc = type.getLocator();
545 
546         if( className==null ) {
547             // infer the class name. For this to be possible,
548             // the simple type must be a global one.
549             if( !type.isGlobal() ) {
550                 getErrorReporter().error( loc, Messages.ERR_NO_ENUM_NAME_AVAILABLE );
551                 // recover by returning a meaningless conversion
552                 return CBuiltinLeafInfo.STRING;
553             }
554             className = type.getName();
555         }
556 
557         // we apply name conversion in any case
558         className = builder.deriveName(className,type);
559 
560         {// compute Javadoc
561             StringWriter out = new StringWriter();
562             SchemaWriter sw = new SchemaWriter(new JavadocEscapeWriter(out));
563             type.visit((XSVisitor)sw);
564 
565             if(javadoc!=null)   javadoc += "\n\n";
566             else                javadoc = "";
567 
568             javadoc += Messages.format( Messages.JAVADOC_HEADING, type.getName() )
569                 +"\n<p>\n<pre>\n"+out.getBuffer()+"</pre>";
570 
571         }
572 
573         // build base type
574         refererStack.push(type.getSimpleBaseType());
575         TypeUse use = build(type.getSimpleBaseType());
576         refererStack.pop();
577 
578         if(use.isCollection())
579             return null;    // can't bind a list to enum constant
580 
581         CNonElement baseDt = use.getInfo();   // for now just ignore that case
582 
583         if(baseDt instanceof CClassInfo)
584             return null;    // can't bind to an enum if the base is a class, since we don't have the value constrctor
585 
586         // if the member names collide, re-generate numbered constant names.
587         XSFacet[] errorRef = new XSFacet[1];
588         List<CEnumConstant> memberList = buildCEnumConstants(type, false, members, errorRef);
589         if(memberList==null || checkMemberNameCollision(memberList)!=null) {
590             switch(mode) {
591             case SKIP:
592                 // abort
593                 return null;
594             case ERROR:
595                 // error
596                 if(memberList==null) {
597                     getErrorReporter().error( errorRef[0].getLocator(),
598                         Messages.ERR_CANNOT_GENERATE_ENUM_NAME,
599                         errorRef[0].getValue() );
600                 } else {
601                     CEnumConstant[] collision = checkMemberNameCollision(memberList);
602                     getErrorReporter().error( collision[0].getLocator(),
603                         Messages.ERR_ENUM_MEMBER_NAME_COLLISION,
604                         collision[0].getName() );
605                     getErrorReporter().error( collision[1].getLocator(),
606                         Messages.ERR_ENUM_MEMBER_NAME_COLLISION_RELATED );
607                 }
608                 return null;    // recover from error
609             case GENERATE:
610                 // generate
611                 memberList = buildCEnumConstants(type,true,members,null);
612                 break;
613             }
614         }
615         if(memberList.isEmpty()) {
616             getErrorReporter().error( loc, Messages.ERR_NO_ENUM_FACET );
617             return null;
618         }
619 
620         // use the name of the simple type as the name of the class.
621         CClassInfoParent scope;
622         if(type.isGlobal())
623             scope = new CClassInfoParent.Package(getClassSelector().getPackage(type.getTargetNamespace()));
624         else
625             scope = getClassSelector().getClassScope();
626         CEnumLeafInfo xducer = new CEnumLeafInfo( model, BGMBuilder.getName(type), scope,
627             className, baseDt, memberList, type,
628             builder.getBindInfo(type).toCustomizationList(), loc );
629         xducer.javadoc = javadoc;
630 
631         BIConversion conv = new BIConversion.Static( type.getLocator(),xducer);
632         conv.markAsAcknowledged();
633 
634         // attach this new conversion object to this simple type
635         // so that successive look up will use the same object.
636         builder.getOrCreateBindInfo(type).addDecl(conv);
637 
638         return conv.getTypeUse(type);
639     }
640 
641     /**
642      *
643      * @param errorRef
644      *      if constant names couldn't be generated, return a reference to that enum facet.
645      * @return
646      *      null if unable to generate names for some of the constants.
647      */
648     private List<CEnumConstant> buildCEnumConstants(XSRestrictionSimpleType type, boolean needsToGenerateMemberName, Map<String, BIEnumMember> members, XSFacet[] errorRef) {
649         List<CEnumConstant> memberList = new ArrayList<CEnumConstant>();
650         int idx=1;
651         Set<String> enums = new HashSet<String>(); // to avoid duplicates. See issue #366
652 
653         for( XSFacet facet : type.getDeclaredFacets(XSFacet.FACET_ENUMERATION)) {
654             String name=null;
655             String mdoc=builder.getBindInfo(facet).getDocumentation();
656 
657             if(!enums.add(facet.getValue().value))
658                 continue;   // ignore the 2nd occasion
659 
660             if( needsToGenerateMemberName ) {
661                 // generate names for all member names.
662                 // this will even override names specified by the user. that's crazy.
663                 name = "VALUE_"+(idx++);
664             } else {
665                 String facetValue = facet.getValue().value;
666                 BIEnumMember mem = members.get(facetValue);
667                 if( mem==null )
668                     // look at the one attached to the facet object
669                     mem = builder.getBindInfo(facet).get(BIEnumMember.class);
670 
671                 if (mem!=null) {
672                     name = mem.name;
673                     if (mdoc == null) {
674                         mdoc = mem.javadoc;
675                     }
676                 }
677 
678                 if(name==null) {
679                     StringBuilder sb = new StringBuilder();
680                     for( int i=0; i<facetValue.length(); i++) {
681                         char ch = facetValue.charAt(i);
682                         if(Character.isJavaIdentifierPart(ch))
683                             sb.append(ch);
684                         else
685                             sb.append('_');
686                     }
687                     name = model.getNameConverter().toConstantName(sb.toString());
688                 }
689             }
690 
691             if(!JJavaName.isJavaIdentifier(name)) {
692                 if(errorRef!=null)  errorRef[0] = facet;
693                 return null;    // unable to generate a name
694             }
695 
696             memberList.add(new CEnumConstant(name,mdoc,facet.getValue().value,facet,builder.getBindInfo(facet).toCustomizationList(),facet.getLocator()));
697         }
698         return memberList;
699     }
700 
701     /**
702      * Returns non-null if {@link CEnumConstant}s have name collisions among them.
703      *
704      * @return
705      *      if there's a collision, return two {@link CEnumConstant}s that collided.
706      *      otherwise return null.
707      */
708     private CEnumConstant[] checkMemberNameCollision( List<CEnumConstant> memberList ) {
709         Map<String,CEnumConstant> names = new HashMap<String,CEnumConstant>();
710         for (CEnumConstant c : memberList) {
711             CEnumConstant old = names.put(c.getName(),c);
712             if(old!=null)
713                 // collision detected
714                 return new CEnumConstant[]{old,c};
715         }
716         return null;
717     }
718 
719 
720 
721     private EnumMemberMode getEnumMemberMode() {
722         return builder.getGlobalBinding().getEnumMemberMode();
723     }
724 
725     private TypeUse lookupBuiltin( String typeLocalName ) {
726         if(typeLocalName.equals("integer") || typeLocalName.equals("long")) {
727             /*
728                 attempt an optimization so that we can
729                 improve the binding for types like this:
730 
731                 <simpleType>
732                   <restriciton baseType="integer">
733                     <maxInclusive value="100" />
734                   </
735                 </
736 
737                 ... to int, not BigInteger.
738             */
739 
740             BigInteger xe = readFacet(XSFacet.FACET_MAXEXCLUSIVE,-1);
741             BigInteger xi = readFacet(XSFacet.FACET_MAXINCLUSIVE,0);
742             BigInteger max = min(xe,xi);    // most restrictive one takes precedence
743 
744             if(max!=null) {
745                 BigInteger ne = readFacet(XSFacet.FACET_MINEXCLUSIVE,+1);
746                 BigInteger ni = readFacet(XSFacet.FACET_MININCLUSIVE,0);
747                 BigInteger min = max(ne,ni);
748 
749                 if(min!=null) {
750                     if(min.compareTo(INT_MIN )>=0 && max.compareTo(INT_MAX )<=0)
751                         typeLocalName = "int";
752                     else
753                     if(min.compareTo(LONG_MIN)>=0 && max.compareTo(LONG_MAX)<=0)
754                         typeLocalName = "long";
755                 }
756             }
757         } else
758         if(typeLocalName.equals("boolean") && isRestrictedTo0And1()) {
759             // this is seen in the SOAP schema and too common to ignore
760             return CBuiltinLeafInfo.BOOLEAN_ZERO_OR_ONE;
761         } else
762         if(typeLocalName.equals("base64Binary")) {
763             return lookupBinaryTypeBinding();
764         } else
765         if(typeLocalName.equals("anySimpleType")) {
766             if(getReferer() instanceof XSAttributeDecl || getReferer() instanceof XSSimpleType)
767                 return CBuiltinLeafInfo.STRING;
768             else
769                 return CBuiltinLeafInfo.ANYTYPE;
770         }
771         return builtinConversions.get(typeLocalName);
772     }
773 
774     /**
775      * Decides the way xs:base64Binary binds.
776      *
777      * This method checks the expected media type.
778      */
779     private TypeUse lookupBinaryTypeBinding() {
780         XSComponent referer = getReferer();
781         String emt = referer.getForeignAttribute(XML_MIME_URI, Const.EXPECTED_CONTENT_TYPES);
782         if(emt!=null) {
783             acknowledgedXmimeContentTypes.add(referer);
784             try {
785                 // see http://www.xml.com/lpt/a/2004/07/21/dive.html
786                 List<MimeTypeRange> types = MimeTypeRange.parseRanges(emt);
787                 MimeTypeRange mt = MimeTypeRange.merge(types);
788 
789                 // see spec table I-1 in appendix I section 2.1.1 for bindings
790                 if(mt.majorType.equalsIgnoreCase("image"))
791                     return CBuiltinLeafInfo.IMAGE.makeMimeTyped(mt.toMimeType());
792 
793                 if(( mt.majorType.equalsIgnoreCase("application") || mt.majorType.equalsIgnoreCase("text"))
794                         && isXml(mt.subType))
795                     return CBuiltinLeafInfo.XML_SOURCE.makeMimeTyped(mt.toMimeType());
796 
797                 if((mt.majorType.equalsIgnoreCase("text") && (mt.subType.equalsIgnoreCase("plain")) )) {
798                     return CBuiltinLeafInfo.STRING.makeMimeTyped(mt.toMimeType());
799                 }
800 
801                 return CBuiltinLeafInfo.DATA_HANDLER.makeMimeTyped(mt.toMimeType());
802             } catch (ParseException e) {
803                 getErrorReporter().error( referer.getLocator(),
804                     Messages.format(Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,emt, e.getMessage()) );
805                 // recover by using the default
806             } catch (MimeTypeParseException e) {
807                 getErrorReporter().error( referer.getLocator(),
808                     Messages.format(Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,emt, e.getMessage()) );
809             }
810         }
811         // default
812         return CBuiltinLeafInfo.BASE64_BYTE_ARRAY;
813     }
814 
815     public boolean isAcknowledgedXmimeContentTypes(XSComponent c) {
816         return acknowledgedXmimeContentTypes.contains(c);
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.getFacet(XSFacet.FACET_PATTERN);
833         if(pattern!=null) {
834             String v = pattern.getValue().value;
835             if(v.equals("0|1") || v.equals("1|0") || v.equals("\\d"))
836                 return true;
837         }
838         XSFacet enumf = initiatingType.getFacet(XSFacet.FACET_ENUMERATION);
839         if(enumf!=null) {
840             String v = enumf.getValue().value;
841             if(v.equals("0") || v.equals("1"))
842                 return true;
843         }
844         return false;
845     }
846 
847     private BigInteger readFacet(String facetName,int offset) {
848         XSFacet me = initiatingType.getFacet(facetName);
849         if(me==null)
850             return null;
851         BigInteger bi = DatatypeConverter.parseInteger(me.getValue().value);
852         if(offset!=0)
853             bi = bi.add(BigInteger.valueOf(offset));
854         return bi;
855     }
856 
857     private BigInteger min(BigInteger a, BigInteger b) {
858         if(a==null) return b;
859         if(b==null) return a;
860         return a.min(b);
861     }
862 
863     private BigInteger max(BigInteger a, BigInteger b) {
864         if(a==null) return b;
865         if(b==null) return a;
866         return a.max(b);
867     }
868 
869     private static final BigInteger LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE);
870     private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
871     private static final BigInteger INT_MIN = BigInteger.valueOf(Integer.MIN_VALUE);
872     private static final BigInteger INT_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
873 
874     static {
875         // list of datatypes which have built-in conversions.
876         // note that although xs:token and xs:normalizedString are not
877         // specified in the spec, they need to be here because they
878         // have different whitespace normalization semantics.
879         Map<String,TypeUse> m = builtinConversions;
880 
881         // TODO: this is so dumb
882         m.put("string",         CBuiltinLeafInfo.STRING);
883         m.put("anyURI",         CBuiltinLeafInfo.STRING);
884         m.put("boolean",        CBuiltinLeafInfo.BOOLEAN);
885         // we'll also look at the expected media type, so don't just add this to the map
886         // m.put("base64Binary",   CBuiltinLeafInfo.BASE64_BYTE_ARRAY);
887         m.put("hexBinary",      CBuiltinLeafInfo.HEXBIN_BYTE_ARRAY);
888         m.put("float",          CBuiltinLeafInfo.FLOAT);
889         m.put("decimal",        CBuiltinLeafInfo.BIG_DECIMAL);
890         m.put("integer",        CBuiltinLeafInfo.BIG_INTEGER);
891         m.put("long",           CBuiltinLeafInfo.LONG);
892         m.put("unsignedInt",    CBuiltinLeafInfo.LONG);
893         m.put("int",            CBuiltinLeafInfo.INT);
894         m.put("unsignedShort",  CBuiltinLeafInfo.INT);
895         m.put("short",          CBuiltinLeafInfo.SHORT);
896         m.put("unsignedByte",   CBuiltinLeafInfo.SHORT);
897         m.put("byte",           CBuiltinLeafInfo.BYTE);
898         m.put("double",         CBuiltinLeafInfo.DOUBLE);
899         m.put("QName",          CBuiltinLeafInfo.QNAME);
900         m.put("NOTATION",       CBuiltinLeafInfo.QNAME);
901         m.put("dateTime",       CBuiltinLeafInfo.CALENDAR);
902         m.put("date",           CBuiltinLeafInfo.CALENDAR);
903         m.put("time",           CBuiltinLeafInfo.CALENDAR);
904         m.put("gYearMonth",     CBuiltinLeafInfo.CALENDAR);
905         m.put("gYear",          CBuiltinLeafInfo.CALENDAR);
906         m.put("gMonthDay",      CBuiltinLeafInfo.CALENDAR);
907         m.put("gDay",           CBuiltinLeafInfo.CALENDAR);
908         m.put("gMonth",         CBuiltinLeafInfo.CALENDAR);
909         m.put("duration",       CBuiltinLeafInfo.DURATION);
910         m.put("token",          CBuiltinLeafInfo.TOKEN);
911         m.put("normalizedString",CBuiltinLeafInfo.NORMALIZED_STRING);
912         m.put("ID",             CBuiltinLeafInfo.ID);
913         m.put("IDREF",          CBuiltinLeafInfo.IDREF);
914         // TODO: handling dateTime, time, and date type
915 //        String[] names = {
916 //            "date", "dateTime", "time", "hexBinary" };
917     }
918 }