| /* |
| * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.tools.internal.xjc.reader.xmlschema; |
| |
| import java.io.StringWriter; |
| import java.math.BigInteger; |
| import java.text.ParseException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Stack; |
| |
| import javax.activation.MimeTypeParseException; |
| import javax.xml.bind.DatatypeConverter; |
| |
| import com.sun.codemodel.internal.JJavaName; |
| import com.sun.codemodel.internal.util.JavadocEscapeWriter; |
| import com.sun.xml.internal.bind.v2.WellKnownNamespace; |
| import com.sun.tools.internal.xjc.ErrorReceiver; |
| import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo; |
| import com.sun.tools.internal.xjc.model.CClassInfo; |
| import com.sun.tools.internal.xjc.model.CClassInfoParent; |
| import com.sun.tools.internal.xjc.model.CClassRef; |
| import com.sun.tools.internal.xjc.model.CEnumConstant; |
| import com.sun.tools.internal.xjc.model.CEnumLeafInfo; |
| import com.sun.tools.internal.xjc.model.CNonElement; |
| import com.sun.tools.internal.xjc.model.Model; |
| import com.sun.tools.internal.xjc.model.TypeUse; |
| import com.sun.tools.internal.xjc.model.TypeUseFactory; |
| import com.sun.tools.internal.xjc.reader.Const; |
| import com.sun.tools.internal.xjc.reader.Ring; |
| import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIConversion; |
| import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnum; |
| import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnumMember; |
| import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIProperty; |
| import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo; |
| import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.EnumMemberMode; |
| import com.sun.tools.internal.xjc.util.MimeTypeRange; |
| |
| import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_MIME_URI; |
| |
| import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapterMarker; |
| import com.sun.xml.internal.xsom.XSAttributeDecl; |
| import com.sun.xml.internal.xsom.XSComplexType; |
| import com.sun.xml.internal.xsom.XSComponent; |
| import com.sun.xml.internal.xsom.XSElementDecl; |
| import com.sun.xml.internal.xsom.XSFacet; |
| import com.sun.xml.internal.xsom.XSListSimpleType; |
| import com.sun.xml.internal.xsom.XSRestrictionSimpleType; |
| import com.sun.xml.internal.xsom.XSSimpleType; |
| import com.sun.xml.internal.xsom.XSUnionSimpleType; |
| import com.sun.xml.internal.xsom.XSVariety; |
| import com.sun.xml.internal.xsom.impl.util.SchemaWriter; |
| import com.sun.xml.internal.xsom.visitor.XSSimpleTypeFunction; |
| import com.sun.xml.internal.xsom.visitor.XSVisitor; |
| |
| import org.xml.sax.Locator; |
| |
| /** |
| * Builds {@link TypeUse} from simple types. |
| * |
| * <p> |
| * This code consists of two main portions. The {@link #compose(XSSimpleType)} method |
| * and {@link #composer} forms an outer cycle, which gradually ascends the type |
| * inheritance chain until it finds the suitable binding. When it does this |
| * {@link #initiatingType} is set to the type which started binding, so that we can refer |
| * to the actual constraint facets and such that are applicable on the type. |
| * |
| * <p> |
| * For each intermediate type in the chain, the {@link #find(XSSimpleType)} method |
| * is used to find the binding on that type, sine the outer loop is doing the ascending, |
| * this method only sees if the current type has some binding available. |
| * |
| * <p> |
| * There is at least one ugly code that you need to aware of |
| * when you are modifying the code. See the documentation |
| * about <a href="package.html#stref_cust"> |
| * "simple type customization at the point of reference."</a> |
| * |
| * |
| * @author |
| * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) |
| */ |
| public final class SimpleTypeBuilder extends BindingComponent { |
| |
| protected final BGMBuilder builder = Ring.get(BGMBuilder.class); |
| |
| private final Model model = Ring.get(Model.class); |
| |
| /** |
| * The component that is refering to the simple type |
| * which we are building. This is ugly but necessary |
| * to support the customization of simple types at |
| * its point of reference. See my comment at the header |
| * of this class for details. |
| * |
| * UGLY: Implemented as a Stack of XSComponent to fix a bug |
| */ |
| public final Stack<XSComponent> refererStack = new Stack<XSComponent>(); |
| |
| /** |
| * Records what xmime:expectedContentTypes annotations we honored and processed, |
| * so that we can later check if the user had these annotations in the places |
| * where we didn't anticipate them. |
| */ |
| private final Set<XSComponent> acknowledgedXmimeContentTypes = new HashSet<XSComponent>(); |
| |
| /** |
| * The type that was originally passed to this {@link SimpleTypeBuilder#build(XSSimpleType)}. |
| * Never null. |
| */ |
| private XSSimpleType initiatingType; |
| |
| /** {@link TypeUse}s for the built-in types. Read-only. */ |
| public static final Map<String,TypeUse> builtinConversions = new HashMap<String,TypeUse>(); |
| |
| |
| /** |
| * Entry point from outside. Builds a BGM type expression |
| * from a simple type schema component. |
| * |
| * @param type |
| * the simple type to be bound. |
| */ |
| public TypeUse build( XSSimpleType type ) { |
| XSSimpleType oldi = initiatingType; |
| this.initiatingType = type; |
| |
| TypeUse e = checkRefererCustomization(type); |
| if(e==null) |
| e = compose(type); |
| |
| initiatingType = oldi; |
| |
| return e; |
| } |
| |
| /** |
| * A version of the {@link #build(XSSimpleType)} method |
| * used to bind the definition of a class generated from |
| * the given simple type. |
| */ |
| public TypeUse buildDef( XSSimpleType type ) { |
| XSSimpleType oldi = initiatingType; |
| this.initiatingType = type; |
| |
| TypeUse e = type.apply(composer); |
| |
| initiatingType = oldi; |
| |
| return e; |
| } |
| |
| |
| /** |
| * Returns a javaType customization specified to the referer, if present. |
| * @return can be null. |
| */ |
| private BIConversion getRefererCustomization() { |
| BindInfo info = builder.getBindInfo(getReferer()); |
| BIProperty prop = info.get(BIProperty.class); |
| if(prop==null) return null; |
| return prop.getConv(); |
| } |
| |
| public XSComponent getReferer() { |
| return refererStack.peek(); |
| } |
| |
| /** |
| * Checks if the referer has a conversion customization or not. |
| * If it does, use it to bind this simple type. Otherwise |
| * return null; |
| */ |
| private TypeUse checkRefererCustomization( XSSimpleType type ) { |
| |
| // assertion check. referer must be set properly |
| // before the build method is called. |
| // since the handling of the simple type point-of-reference |
| // customization is very error prone, it deserves a strict |
| // assertion check. |
| // UGLY CODE WARNING |
| XSComponent top = getReferer(); |
| |
| if( top instanceof XSElementDecl ) { |
| // if the parent is element type, its content type must be us. |
| XSElementDecl eref = (XSElementDecl)top; |
| assert eref.getType()==type; |
| |
| // for elements, you can't use <property>, |
| // so we allow javaType to appear directly. |
| BindInfo info = builder.getBindInfo(top); |
| BIConversion conv = info.get(BIConversion.class); |
| if(conv!=null) { |
| conv.markAsAcknowledged(); |
| // the conversion is given. |
| return conv.getTypeUse(type); |
| } |
| detectJavaTypeCustomization(); |
| } else |
| if( top instanceof XSAttributeDecl ) { |
| XSAttributeDecl aref = (XSAttributeDecl)top; |
| assert aref.getType()==type; |
| detectJavaTypeCustomization(); |
| } else |
| if( top instanceof XSComplexType ) { |
| XSComplexType tref = (XSComplexType)top; |
| assert tref.getBaseType()==type || tref.getContentType()==type; |
| detectJavaTypeCustomization(); |
| } else |
| if( top == type ) { |
| // this means the simple type is built by itself and |
| // not because it's referenced by something. |
| } else |
| // unexpected referer type. |
| assert false; |
| |
| // now we are certain that the referer is OK. |
| // see if it has a conversion customization. |
| BIConversion conv = getRefererCustomization(); |
| if(conv!=null) { |
| conv.markAsAcknowledged(); |
| // the conversion is given. |
| return conv.getTypeUse(type); |
| } else |
| // not found |
| return null; |
| } |
| |
| /** |
| * Detect "javaType" customizations placed directly on simple types, rather |
| * than being enclosed by "property" and "baseType" customizations (see |
| * sec 6.8.1 of the spec). |
| * |
| * Report an error if any exist. |
| */ |
| private void detectJavaTypeCustomization() { |
| BindInfo info = builder.getBindInfo(getReferer()); |
| BIConversion conv = info.get(BIConversion.class); |
| |
| if( conv != null ) { |
| // ack this conversion to prevent further error messages |
| conv.markAsAcknowledged(); |
| |
| // report the error |
| getErrorReporter().error( conv.getLocation(), |
| Messages.ERR_UNNESTED_JAVATYPE_CUSTOMIZATION_ON_SIMPLETYPE ); |
| } |
| } |
| |
| /** |
| * Recursively decend the type inheritance chain to find a binding. |
| */ |
| TypeUse compose( XSSimpleType t ) { |
| TypeUse e = find(t); |
| if(e!=null) return e; |
| return t.apply(composer); |
| } |
| |
| public final XSSimpleTypeFunction<TypeUse> composer = new XSSimpleTypeFunction<TypeUse>() { |
| |
| public TypeUse listSimpleType(XSListSimpleType type) { |
| // bind item type individually and then compose them into a list |
| // facets on the list shouldn't be taken account when binding item types, |
| // so weed to call build(), not compose(). |
| XSSimpleType itemType = type.getItemType(); |
| refererStack.push(itemType); |
| TypeUse tu = TypeUseFactory.makeCollection(build(type.getItemType())); |
| refererStack.pop(); |
| return tu; |
| } |
| |
| public TypeUse unionSimpleType(XSUnionSimpleType type) { |
| boolean isCollection = false; |
| for( int i=0; i<type.getMemberSize(); i++ ) |
| if(type.getMember(i).getVariety()==XSVariety.LIST || type.getMember(i).getVariety()==XSVariety.UNION) { |
| isCollection = true; |
| break; |
| } |
| |
| TypeUse r = CBuiltinLeafInfo.STRING; |
| if(isCollection) |
| r = TypeUseFactory.makeCollection(r); |
| return r; |
| } |
| |
| public TypeUse restrictionSimpleType(XSRestrictionSimpleType type) { |
| // just process the base type. |
| return compose(type.getSimpleBaseType()); |
| } |
| }; |
| |
| |
| /** |
| * Checks if there's any binding available on the given type. |
| * |
| * @return |
| * null if not (which causes the {@link #compose(XSSimpleType)} method |
| * to do ascending. |
| */ |
| private TypeUse find( XSSimpleType type ) { |
| TypeUse r; |
| boolean noAutoEnum = false; |
| |
| // check for user specified conversion |
| BindInfo info = builder.getBindInfo(type); |
| BIConversion conv = info.get(BIConversion.class); |
| |
| if( conv!=null ) { |
| // a conversion was found |
| conv.markAsAcknowledged(); |
| return conv.getTypeUse(type); |
| } |
| |
| // look for enum customization, which is another user specified conversion |
| BIEnum en = info.get(BIEnum.class); |
| if( en!=null ) { |
| en.markAsAcknowledged(); |
| |
| if(!en.isMapped()) { |
| noAutoEnum = true; |
| } else { |
| // if an enum customization is specified, make sure |
| // the type is OK |
| if( !canBeMappedToTypeSafeEnum(type) ) { |
| getErrorReporter().error( en.getLocation(), |
| Messages.ERR_CANNOT_BE_TYPE_SAFE_ENUM ); |
| getErrorReporter().error( type.getLocator(), |
| Messages.ERR_CANNOT_BE_TYPE_SAFE_ENUM_LOCATION ); |
| // recover by ignoring this customization |
| return null; |
| } |
| |
| // reference? |
| if(en.ref!=null) { |
| if(!JJavaName.isFullyQualifiedClassName(en.ref)) { |
| Ring.get(ErrorReceiver.class).error( en.getLocation(), |
| Messages.format(Messages.ERR_INCORRECT_CLASS_NAME, en.ref) ); |
| // recover by ignoring @ref |
| return null; |
| } |
| |
| return new CClassRef(model, type, en, info.toCustomizationList() ); |
| } |
| |
| // list and union cannot be mapped to a type-safe enum, |
| // so in this stage we can safely cast it to XSRestrictionSimpleType |
| return bindToTypeSafeEnum( (XSRestrictionSimpleType)type, |
| en.className, en.javadoc, en.members, |
| getEnumMemberMode().getModeWithEnum(), |
| en.getLocation() ); |
| } |
| } |
| |
| |
| // if the type is built in, look for the default binding |
| if(type.getTargetNamespace().equals(WellKnownNamespace.XML_SCHEMA)) { |
| String name = type.getName(); |
| if(name!=null) { |
| r = lookupBuiltin(name); |
| if(r!=null) |
| return r; |
| } |
| } |
| |
| // also check for swaRef |
| if(type.getTargetNamespace().equals(WellKnownNamespace.SWA_URI)) { |
| String name = type.getName(); |
| if(name!=null && name.equals("swaRef")) |
| return CBuiltinLeafInfo.STRING.makeAdapted(SwaRefAdapterMarker.class,false); |
| } |
| |
| |
| // see if this type should be mapped to a type-safe enumeration by default. |
| // if so, built a EnumXDucer from it and return it. |
| if(type.isRestriction() && !noAutoEnum) { |
| XSRestrictionSimpleType rst = type.asRestriction(); |
| if(shouldBeMappedToTypeSafeEnumByDefault(rst)) { |
| r = bindToTypeSafeEnum(rst,null,null, Collections.<String, BIEnumMember>emptyMap(), |
| getEnumMemberMode(),null); |
| if(r!=null) |
| return r; |
| } |
| } |
| |
| return (CNonElement)getClassSelector()._bindToClass(type,null,false); |
| } |
| |
| private static Set<XSRestrictionSimpleType> reportedEnumMemberSizeWarnings; |
| |
| /** |
| * Returns true if a type-safe enum should be created from |
| * the given simple type by default without an explicit <jaxb:enum> customization. |
| */ |
| private boolean shouldBeMappedToTypeSafeEnumByDefault( XSRestrictionSimpleType type ) { |
| |
| // if not, there will be a problem wrt the class name of this type safe enum type. |
| if( type.isLocal() ) return false; |
| |
| // if redefined, we should map the new definition, not the old one. |
| if( type.getRedefinedBy()!=null ) return false; |
| |
| List<XSFacet> facets = type.getDeclaredFacets(XSFacet.FACET_ENUMERATION); |
| if( facets.isEmpty() ) |
| // if the type itself doesn't have the enumeration facet, |
| // it won't be mapped to a type-safe enum. |
| return false; |
| |
| if(facets.size() > builder.getGlobalBinding().getDefaultEnumMemberSizeCap()) { |
| // if there are too many facets, it's not very useful |
| // produce warning when simple type is not mapped to enum |
| // see issue https://jaxb.dev.java.net/issues/show_bug.cgi?id=711 |
| |
| if(reportedEnumMemberSizeWarnings == null) |
| reportedEnumMemberSizeWarnings = new HashSet<XSRestrictionSimpleType>(); |
| |
| if(!reportedEnumMemberSizeWarnings.contains(type)) { |
| getErrorReporter().warning(type.getLocator(), Messages.WARN_ENUM_MEMBER_SIZE_CAP, |
| type.getName(), facets.size(), builder.getGlobalBinding().getDefaultEnumMemberSizeCap()); |
| |
| reportedEnumMemberSizeWarnings.add(type); |
| } |
| |
| return false; |
| } |
| |
| if( !canBeMappedToTypeSafeEnum(type) ) |
| // we simply can't map this to an enumeration |
| return false; |
| |
| // check for collisions among constant names. if a collision will happen, |
| // don't try to bind it to an enum. |
| |
| // return true only when this type is derived from one of the "enum base type". |
| for( XSSimpleType t = type; t!=null; t=t.getSimpleBaseType() ) |
| if( t.isGlobal() && builder.getGlobalBinding().canBeMappedToTypeSafeEnum(t) ) |
| return true; |
| |
| return false; |
| } |
| |
| |
| private static final Set<String> builtinTypeSafeEnumCapableTypes; |
| |
| static { |
| Set<String> s = new HashSet<String>(); |
| |
| // see a bullet of 6.5.1 of the spec. |
| String[] typeNames = new String[] { |
| "string", "boolean", "float", "decimal", "double", "anyURI" |
| }; |
| s.addAll(Arrays.asList(typeNames)); |
| |
| builtinTypeSafeEnumCapableTypes = Collections.unmodifiableSet(s); |
| } |
| |
| |
| /** |
| * Returns true if the given simple type can be mapped to a |
| * type-safe enum class. |
| * |
| * <p> |
| * JAXB spec places a restrictrion as to what type can be |
| * mapped to a type-safe enum. This method enforces this |
| * constraint. |
| */ |
| public static boolean canBeMappedToTypeSafeEnum( XSSimpleType type ) { |
| do { |
| if( WellKnownNamespace.XML_SCHEMA.equals(type.getTargetNamespace()) ) { |
| // type must be derived from one of these types |
| String localName = type.getName(); |
| if( localName!=null ) { |
| if( localName.equals("anySimpleType") ) |
| return false; // catch all case |
| if( localName.equals("ID") || localName.equals("IDREF") ) |
| return false; // not ID/IDREF |
| |
| // other allowed list |
| if( builtinTypeSafeEnumCapableTypes.contains(localName) ) |
| return true; |
| } |
| } |
| |
| type = type.getSimpleBaseType(); |
| } while( type!=null ); |
| |
| return false; |
| } |
| |
| |
| |
| /** |
| * Builds a type-safe enum conversion from a simple type |
| * with enumeration facets. |
| * |
| * @param className |
| * The class name of the type-safe enum. Or null to |
| * create a default name. |
| * @param javadoc |
| * Additional javadoc that will be added at the beginning of the |
| * class, or null if none is necessary. |
| * @param members |
| * A map from enumeration values (as String) to BIEnumMember objects. |
| * if some of the value names need to be overrided. |
| * Cannot be null, but the map may not contain entries |
| * for all enumeration values. |
| * @param loc |
| * The source location where the above customizations are |
| * specified, or null if none is available. |
| */ |
| private TypeUse bindToTypeSafeEnum( XSRestrictionSimpleType type, |
| String className, String javadoc, Map<String,BIEnumMember> members, |
| EnumMemberMode mode, Locator loc ) { |
| |
| if( loc==null ) // use the location of the simple type as the default |
| loc = type.getLocator(); |
| |
| if( className==null ) { |
| // infer the class name. For this to be possible, |
| // the simple type must be a global one. |
| if( !type.isGlobal() ) { |
| getErrorReporter().error( loc, Messages.ERR_NO_ENUM_NAME_AVAILABLE ); |
| // recover by returning a meaningless conversion |
| return CBuiltinLeafInfo.STRING; |
| } |
| className = type.getName(); |
| } |
| |
| // we apply name conversion in any case |
| className = builder.deriveName(className,type); |
| |
| {// compute Javadoc |
| StringWriter out = new StringWriter(); |
| SchemaWriter sw = new SchemaWriter(new JavadocEscapeWriter(out)); |
| type.visit((XSVisitor)sw); |
| |
| if(javadoc!=null) javadoc += "\n\n"; |
| else javadoc = ""; |
| |
| javadoc += Messages.format( Messages.JAVADOC_HEADING, type.getName() ) |
| +"\n<p>\n<pre>\n"+out.getBuffer()+"</pre>"; |
| |
| } |
| |
| // build base type |
| refererStack.push(type.getSimpleBaseType()); |
| TypeUse use = build(type.getSimpleBaseType()); |
| refererStack.pop(); |
| |
| if(use.isCollection()) |
| return null; // can't bind a list to enum constant |
| |
| CNonElement baseDt = use.getInfo(); // for now just ignore that case |
| |
| if(baseDt instanceof CClassInfo) |
| return null; // can't bind to an enum if the base is a class, since we don't have the value constrctor |
| |
| // if the member names collide, re-generate numbered constant names. |
| XSFacet[] errorRef = new XSFacet[1]; |
| List<CEnumConstant> memberList = buildCEnumConstants(type, false, members, errorRef); |
| if(memberList==null || checkMemberNameCollision(memberList)!=null) { |
| switch(mode) { |
| case SKIP: |
| // abort |
| return null; |
| case ERROR: |
| // error |
| if(memberList==null) { |
| getErrorReporter().error( errorRef[0].getLocator(), |
| Messages.ERR_CANNOT_GENERATE_ENUM_NAME, |
| errorRef[0].getValue() ); |
| } else { |
| CEnumConstant[] collision = checkMemberNameCollision(memberList); |
| getErrorReporter().error( collision[0].getLocator(), |
| Messages.ERR_ENUM_MEMBER_NAME_COLLISION, |
| collision[0].getName() ); |
| getErrorReporter().error( collision[1].getLocator(), |
| Messages.ERR_ENUM_MEMBER_NAME_COLLISION_RELATED ); |
| } |
| return null; // recover from error |
| case GENERATE: |
| // generate |
| memberList = buildCEnumConstants(type,true,members,null); |
| break; |
| } |
| } |
| if(memberList.isEmpty()) { |
| getErrorReporter().error( loc, Messages.ERR_NO_ENUM_FACET ); |
| return null; |
| } |
| |
| // use the name of the simple type as the name of the class. |
| CClassInfoParent scope; |
| if(type.isGlobal()) |
| scope = new CClassInfoParent.Package(getClassSelector().getPackage(type.getTargetNamespace())); |
| else |
| scope = getClassSelector().getClassScope(); |
| CEnumLeafInfo xducer = new CEnumLeafInfo( model, BGMBuilder.getName(type), scope, |
| className, baseDt, memberList, type, |
| builder.getBindInfo(type).toCustomizationList(), loc ); |
| xducer.javadoc = javadoc; |
| |
| BIConversion conv = new BIConversion.Static( type.getLocator(),xducer); |
| conv.markAsAcknowledged(); |
| |
| // attach this new conversion object to this simple type |
| // so that successive look up will use the same object. |
| builder.getOrCreateBindInfo(type).addDecl(conv); |
| |
| return conv.getTypeUse(type); |
| } |
| |
| /** |
| * |
| * @param errorRef |
| * if constant names couldn't be generated, return a reference to that enum facet. |
| * @return |
| * null if unable to generate names for some of the constants. |
| */ |
| private List<CEnumConstant> buildCEnumConstants(XSRestrictionSimpleType type, boolean needsToGenerateMemberName, Map<String, BIEnumMember> members, XSFacet[] errorRef) { |
| List<CEnumConstant> memberList = new ArrayList<CEnumConstant>(); |
| int idx=1; |
| Set<String> enums = new HashSet<String>(); // to avoid duplicates. See issue #366 |
| |
| for( XSFacet facet : type.getDeclaredFacets(XSFacet.FACET_ENUMERATION)) { |
| String name=null; |
| String mdoc=builder.getBindInfo(facet).getDocumentation(); |
| |
| if(!enums.add(facet.getValue().value)) |
| continue; // ignore the 2nd occasion |
| |
| if( needsToGenerateMemberName ) { |
| // generate names for all member names. |
| // this will even override names specified by the user. that's crazy. |
| name = "VALUE_"+(idx++); |
| } else { |
| String facetValue = facet.getValue().value; |
| BIEnumMember mem = members.get(facetValue); |
| if( mem==null ) |
| // look at the one attached to the facet object |
| mem = builder.getBindInfo(facet).get(BIEnumMember.class); |
| |
| if (mem!=null) { |
| name = mem.name; |
| if (mdoc == null) { |
| mdoc = mem.javadoc; |
| } |
| } |
| |
| if(name==null) { |
| StringBuilder sb = new StringBuilder(); |
| for( int i=0; i<facetValue.length(); i++) { |
| char ch = facetValue.charAt(i); |
| if(Character.isJavaIdentifierPart(ch)) |
| sb.append(ch); |
| else |
| sb.append('_'); |
| } |
| name = model.getNameConverter().toConstantName(sb.toString()); |
| } |
| } |
| |
| if(!JJavaName.isJavaIdentifier(name)) { |
| if(errorRef!=null) errorRef[0] = facet; |
| return null; // unable to generate a name |
| } |
| |
| memberList.add(new CEnumConstant(name,mdoc,facet.getValue().value,facet,builder.getBindInfo(facet).toCustomizationList(),facet.getLocator())); |
| } |
| return memberList; |
| } |
| |
| /** |
| * Returns non-null if {@link CEnumConstant}s have name collisions among them. |
| * |
| * @return |
| * if there's a collision, return two {@link CEnumConstant}s that collided. |
| * otherwise return null. |
| */ |
| private CEnumConstant[] checkMemberNameCollision( List<CEnumConstant> memberList ) { |
| Map<String,CEnumConstant> names = new HashMap<String,CEnumConstant>(); |
| for (CEnumConstant c : memberList) { |
| CEnumConstant old = names.put(c.getName(),c); |
| if(old!=null) |
| // collision detected |
| return new CEnumConstant[]{old,c}; |
| } |
| return null; |
| } |
| |
| |
| |
| private EnumMemberMode getEnumMemberMode() { |
| return builder.getGlobalBinding().getEnumMemberMode(); |
| } |
| |
| private TypeUse lookupBuiltin( String typeLocalName ) { |
| if(typeLocalName.equals("integer") || typeLocalName.equals("long")) { |
| /* |
| attempt an optimization so that we can |
| improve the binding for types like this: |
| |
| <simpleType> |
| <restriciton baseType="integer"> |
| <maxInclusive value="100" /> |
| </ |
| </ |
| |
| ... to int, not BigInteger. |
| */ |
| |
| BigInteger xe = readFacet(XSFacet.FACET_MAXEXCLUSIVE,-1); |
| BigInteger xi = readFacet(XSFacet.FACET_MAXINCLUSIVE,0); |
| BigInteger max = min(xe,xi); // most restrictive one takes precedence |
| |
| if(max!=null) { |
| BigInteger ne = readFacet(XSFacet.FACET_MINEXCLUSIVE,+1); |
| BigInteger ni = readFacet(XSFacet.FACET_MININCLUSIVE,0); |
| BigInteger min = max(ne,ni); |
| |
| if(min!=null) { |
| if(min.compareTo(INT_MIN )>=0 && max.compareTo(INT_MAX )<=0) |
| typeLocalName = "int"; |
| else |
| if(min.compareTo(LONG_MIN)>=0 && max.compareTo(LONG_MAX)<=0) |
| typeLocalName = "long"; |
| } |
| } |
| } else |
| if(typeLocalName.equals("boolean") && isRestrictedTo0And1()) { |
| // this is seen in the SOAP schema and too common to ignore |
| return CBuiltinLeafInfo.BOOLEAN_ZERO_OR_ONE; |
| } else |
| if(typeLocalName.equals("base64Binary")) { |
| return lookupBinaryTypeBinding(); |
| } else |
| if(typeLocalName.equals("anySimpleType")) { |
| if(getReferer() instanceof XSAttributeDecl || getReferer() instanceof XSSimpleType) |
| return CBuiltinLeafInfo.STRING; |
| else |
| return CBuiltinLeafInfo.ANYTYPE; |
| } |
| return builtinConversions.get(typeLocalName); |
| } |
| |
| /** |
| * Decides the way xs:base64Binary binds. |
| * |
| * This method checks the expected media type. |
| */ |
| private TypeUse lookupBinaryTypeBinding() { |
| XSComponent referer = getReferer(); |
| String emt = referer.getForeignAttribute(XML_MIME_URI, Const.EXPECTED_CONTENT_TYPES); |
| if(emt!=null) { |
| acknowledgedXmimeContentTypes.add(referer); |
| try { |
| // see http://www.xml.com/lpt/a/2004/07/21/dive.html |
| List<MimeTypeRange> types = MimeTypeRange.parseRanges(emt); |
| MimeTypeRange mt = MimeTypeRange.merge(types); |
| |
| // see spec table I-1 in appendix I section 2.1.1 for bindings |
| if(mt.majorType.equalsIgnoreCase("image")) |
| return CBuiltinLeafInfo.IMAGE.makeMimeTyped(mt.toMimeType()); |
| |
| if(( mt.majorType.equalsIgnoreCase("application") || mt.majorType.equalsIgnoreCase("text")) |
| && isXml(mt.subType)) |
| return CBuiltinLeafInfo.XML_SOURCE.makeMimeTyped(mt.toMimeType()); |
| |
| if((mt.majorType.equalsIgnoreCase("text") && (mt.subType.equalsIgnoreCase("plain")) )) { |
| return CBuiltinLeafInfo.STRING.makeMimeTyped(mt.toMimeType()); |
| } |
| |
| return CBuiltinLeafInfo.DATA_HANDLER.makeMimeTyped(mt.toMimeType()); |
| } catch (ParseException e) { |
| getErrorReporter().error( referer.getLocator(), |
| Messages.format(Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,emt, e.getMessage()) ); |
| // recover by using the default |
| } catch (MimeTypeParseException e) { |
| getErrorReporter().error( referer.getLocator(), |
| Messages.format(Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,emt, e.getMessage()) ); |
| } |
| } |
| // default |
| return CBuiltinLeafInfo.BASE64_BYTE_ARRAY; |
| } |
| |
| public boolean isAcknowledgedXmimeContentTypes(XSComponent c) { |
| return acknowledgedXmimeContentTypes.contains(c); |
| } |
| |
| /** |
| * Returns true if the specified sub-type is an XML type. |
| */ |
| private boolean isXml(String subType) { |
| return subType.equals("xml") || subType.endsWith("+xml"); |
| } |
| |
| /** |
| * Returns true if the {@link #initiatingType} is restricted |
| * to '0' and '1'. This logic is not complete, but it at least |
| * finds the such definition in SOAP @mustUnderstand. |
| */ |
| private boolean isRestrictedTo0And1() { |
| XSFacet pattern = initiatingType.getFacet(XSFacet.FACET_PATTERN); |
| if(pattern!=null) { |
| String v = pattern.getValue().value; |
| if(v.equals("0|1") || v.equals("1|0") || v.equals("\\d")) |
| return true; |
| } |
| XSFacet enumf = initiatingType.getFacet(XSFacet.FACET_ENUMERATION); |
| if(enumf!=null) { |
| String v = enumf.getValue().value; |
| if(v.equals("0") || v.equals("1")) |
| return true; |
| } |
| return false; |
| } |
| |
| private BigInteger readFacet(String facetName,int offset) { |
| XSFacet me = initiatingType.getFacet(facetName); |
| if(me==null) |
| return null; |
| BigInteger bi = DatatypeConverter.parseInteger(me.getValue().value); |
| if(offset!=0) |
| bi = bi.add(BigInteger.valueOf(offset)); |
| return bi; |
| } |
| |
| private BigInteger min(BigInteger a, BigInteger b) { |
| if(a==null) return b; |
| if(b==null) return a; |
| return a.min(b); |
| } |
| |
| private BigInteger max(BigInteger a, BigInteger b) { |
| if(a==null) return b; |
| if(b==null) return a; |
| return a.max(b); |
| } |
| |
| private static final BigInteger LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE); |
| private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE); |
| private static final BigInteger INT_MIN = BigInteger.valueOf(Integer.MIN_VALUE); |
| private static final BigInteger INT_MAX = BigInteger.valueOf(Integer.MAX_VALUE); |
| |
| static { |
| // list of datatypes which have built-in conversions. |
| // note that although xs:token and xs:normalizedString are not |
| // specified in the spec, they need to be here because they |
| // have different whitespace normalization semantics. |
| Map<String,TypeUse> m = builtinConversions; |
| |
| // TODO: this is so dumb |
| m.put("string", CBuiltinLeafInfo.STRING); |
| m.put("anyURI", CBuiltinLeafInfo.STRING); |
| m.put("boolean", CBuiltinLeafInfo.BOOLEAN); |
| // we'll also look at the expected media type, so don't just add this to the map |
| // m.put("base64Binary", CBuiltinLeafInfo.BASE64_BYTE_ARRAY); |
| m.put("hexBinary", CBuiltinLeafInfo.HEXBIN_BYTE_ARRAY); |
| m.put("float", CBuiltinLeafInfo.FLOAT); |
| m.put("decimal", CBuiltinLeafInfo.BIG_DECIMAL); |
| m.put("integer", CBuiltinLeafInfo.BIG_INTEGER); |
| m.put("long", CBuiltinLeafInfo.LONG); |
| m.put("unsignedInt", CBuiltinLeafInfo.LONG); |
| m.put("int", CBuiltinLeafInfo.INT); |
| m.put("unsignedShort", CBuiltinLeafInfo.INT); |
| m.put("short", CBuiltinLeafInfo.SHORT); |
| m.put("unsignedByte", CBuiltinLeafInfo.SHORT); |
| m.put("byte", CBuiltinLeafInfo.BYTE); |
| m.put("double", CBuiltinLeafInfo.DOUBLE); |
| m.put("QName", CBuiltinLeafInfo.QNAME); |
| m.put("NOTATION", CBuiltinLeafInfo.QNAME); |
| m.put("dateTime", CBuiltinLeafInfo.CALENDAR); |
| m.put("date", CBuiltinLeafInfo.CALENDAR); |
| m.put("time", CBuiltinLeafInfo.CALENDAR); |
| m.put("gYearMonth", CBuiltinLeafInfo.CALENDAR); |
| m.put("gYear", CBuiltinLeafInfo.CALENDAR); |
| m.put("gMonthDay", CBuiltinLeafInfo.CALENDAR); |
| m.put("gDay", CBuiltinLeafInfo.CALENDAR); |
| m.put("gMonth", CBuiltinLeafInfo.CALENDAR); |
| m.put("duration", CBuiltinLeafInfo.DURATION); |
| m.put("token", CBuiltinLeafInfo.TOKEN); |
| m.put("normalizedString",CBuiltinLeafInfo.NORMALIZED_STRING); |
| m.put("ID", CBuiltinLeafInfo.ID); |
| m.put("IDREF", CBuiltinLeafInfo.IDREF); |
| // TODO: handling dateTime, time, and date type |
| // String[] names = { |
| // "date", "dateTime", "time", "hexBinary" }; |
| } |
| } |