1 /*
   2  * Copyright (c) 1997, 2015, 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;
 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 {@code <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 = new HashMap<String,TypeUse>();
 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 
 915         builtinConversions = Collections.unmodifiableMap(m);
 916         // TODO: handling dateTime, time, and date type
 917 //        String[] names = {
 918 //            "date", "dateTime", "time", "hexBinary" };
 919     }
 920 }