src/share/jaxws_classes/com/sun/tools/internal/xjc/reader/xmlschema/SimpleTypeBuilder.java

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 368
0989ad8c0860
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

     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  */
    26 package com.sun.tools.internal.xjc.reader.xmlschema;
    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;
    41 import javax.activation.MimeTypeParseException;
    42 import javax.xml.bind.DatatypeConverter;
    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;
    68 import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_MIME_URI;
    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;
    85 import org.xml.sax.Locator;
    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 {
   114     protected final BGMBuilder builder = Ring.get(BGMBuilder.class);
   116     private final Model model = Ring.get(Model.class);
   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>();
   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>();
   136     /**
   137      * The type that was originally passed to this {@link SimpleTypeBuilder#build(XSSimpleType)}.
   138      * Never null.
   139      */
   140     private XSSimpleType initiatingType;
   142     /** {@link TypeUse}s for the built-in types. Read-only. */
   143     public static final Map<String,TypeUse> builtinConversions = new HashMap<String,TypeUse>();
   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;
   157         TypeUse e = checkRefererCustomization(type);
   158         if(e==null)
   159             e = compose(type);
   161         initiatingType = oldi;
   163         return e;
   164     }
   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;
   175         TypeUse e = type.apply(composer);
   177         initiatingType = oldi;
   179         return e;
   180     }
   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     }
   194     public XSComponent getReferer() {
   195         return refererStack.peek();
   196     }
   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 ) {
   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();
   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;
   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;
   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     }
   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);
   269         if( conv != null ) {
   270             // ack this conversion to prevent further error messages
   271             conv.markAsAcknowledged();
   273             // report the error
   274             getErrorReporter().error( conv.getLocation(),
   275                     Messages.ERR_UNNESTED_JAVATYPE_CUSTOMIZATION_ON_SIMPLETYPE );
   276         }
   277     }
   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     }
   288     public final XSSimpleTypeFunction<TypeUse> composer = new XSSimpleTypeFunction<TypeUse>() {
   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         }
   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                 }
   309             TypeUse r = CBuiltinLeafInfo.STRING;
   310             if(isCollection)
   311                 r = TypeUseFactory.makeCollection(r);
   312             return r;
   313         }
   315         public TypeUse restrictionSimpleType(XSRestrictionSimpleType type) {
   316             // just process the base type.
   317             return compose(type.getSimpleBaseType());
   318         }
   319     };
   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;
   333         // check for user specified conversion
   334         BindInfo info = builder.getBindInfo(type);
   335         BIConversion conv = info.get(BIConversion.class);
   337         if( conv!=null ) {
   338             // a conversion was found
   339             conv.markAsAcknowledged();
   340             return conv.getTypeUse(type);
   341         }
   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();
   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                 }
   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                     }
   371                     return new CClassRef(model, type, en, info.toCustomizationList() );
   372                 }
   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         }
   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         }
   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         }
   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         }
   414         return (CNonElement)getClassSelector()._bindToClass(type,null,false);
   415     }
   417     private static Set<XSRestrictionSimpleType> reportedEnumMemberSizeWarnings;
   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 ) {
   425         // if not, there will be a problem wrt the class name of this type safe enum type.
   426         if( type.isLocal() )    return false;
   428         // if redefined, we should map the new definition, not the old one.
   429         if( type.getRedefinedBy()!=null )   return false;
   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;
   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
   442             if(reportedEnumMemberSizeWarnings == null)
   443                 reportedEnumMemberSizeWarnings = new HashSet<XSRestrictionSimpleType>();
   445             if(!reportedEnumMemberSizeWarnings.contains(type)) {
   446                 getErrorReporter().warning(type.getLocator(), Messages.WARN_ENUM_MEMBER_SIZE_CAP,
   447                         type.getName(), facets.size(), builder.getGlobalBinding().getDefaultEnumMemberSizeCap());
   449                 reportedEnumMemberSizeWarnings.add(type);
   450             }
   452             return false;
   453         }
   455         if( !canBeMappedToTypeSafeEnum(type) )
   456             // we simply can't map this to an enumeration
   457             return false;
   459         // check for collisions among constant names. if a collision will happen,
   460         // don't try to bind it to an enum.
   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;
   467         return false;
   468     }
   471     private static final Set<String> builtinTypeSafeEnumCapableTypes;
   473     static {
   474         Set<String> s = new HashSet<String>();
   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));
   482         builtinTypeSafeEnumCapableTypes = Collections.unmodifiableSet(s);
   483     }
   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
   506                     // other allowed list
   507                     if( builtinTypeSafeEnumCapableTypes.contains(localName) )
   508                         return true;
   509                 }
   510             }
   512             type = type.getSimpleBaseType();
   513         } while( type!=null );
   515         return false;
   516     }
   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 ) {
   543         if( loc==null )  // use the location of the simple type as the default
   544             loc = type.getLocator();
   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         }
   557         // we apply name conversion in any case
   558         className = builder.deriveName(className,type);
   560         {// compute Javadoc
   561             StringWriter out = new StringWriter();
   562             SchemaWriter sw = new SchemaWriter(new JavadocEscapeWriter(out));
   563             type.visit((XSVisitor)sw);
   565             if(javadoc!=null)   javadoc += "\n\n";
   566             else                javadoc = "";
   568             javadoc += Messages.format( Messages.JAVADOC_HEADING, type.getName() )
   569                 +"\n<p>\n<pre>\n"+out.getBuffer()+"</pre>";
   571         }
   573         // build base type
   574         refererStack.push(type.getSimpleBaseType());
   575         TypeUse use = build(type.getSimpleBaseType());
   576         refererStack.pop();
   578         if(use.isCollection())
   579             return null;    // can't bind a list to enum constant
   581         CNonElement baseDt = use.getInfo();   // for now just ignore that case
   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
   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         }
   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;
   631         BIConversion conv = new BIConversion.Static( type.getLocator(),xducer);
   632         conv.markAsAcknowledged();
   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);
   638         return conv.getTypeUse(type);
   639     }
   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
   653         for( XSFacet facet : type.getDeclaredFacets(XSFacet.FACET_ENUMERATION)) {
   654             String name=null;
   655             String mdoc=builder.getBindInfo(facet).getDocumentation();
   657             if(!enums.add(facet.getValue().value))
   658                 continue;   // ignore the 2nd occasion
   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);
   671                 if (mem!=null) {
   672                     name = mem.name;
   673                     if (mdoc == null) {
   674                         mdoc = mem.javadoc;
   675                     }
   676                 }
   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             }
   691             if(!JJavaName.isJavaIdentifier(name)) {
   692                 if(errorRef!=null)  errorRef[0] = facet;
   693                 return null;    // unable to generate a name
   694             }
   696             memberList.add(new CEnumConstant(name,mdoc,facet.getValue().value,facet,builder.getBindInfo(facet).toCustomizationList(),facet.getLocator()));
   697         }
   698         return memberList;
   699     }
   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     }
   721     private EnumMemberMode getEnumMemberMode() {
   722         return builder.getGlobalBinding().getEnumMemberMode();
   723     }
   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:
   731                 <simpleType>
   732                   <restriciton baseType="integer">
   733                     <maxInclusive value="100" />
   734                   </
   735                 </
   737                 ... to int, not BigInteger.
   738             */
   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
   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);
   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     }
   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);
   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());
   793                 if(( mt.majorType.equalsIgnoreCase("application") || mt.majorType.equalsIgnoreCase("text"))
   794                         && isXml(mt.subType))
   795                     return CBuiltinLeafInfo.XML_SOURCE.makeMimeTyped(mt.toMimeType());
   797                 if((mt.majorType.equalsIgnoreCase("text") && (mt.subType.equalsIgnoreCase("plain")) )) {
   798                     return CBuiltinLeafInfo.STRING.makeMimeTyped(mt.toMimeType());
   799                 }
   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     }
   815     public boolean isAcknowledgedXmimeContentTypes(XSComponent c) {
   816         return acknowledgedXmimeContentTypes.contains(c);
   817     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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);
   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;
   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 }

mercurial