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