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