1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/sun/tools/internal/xjc/reader/xmlschema/SimpleTypeBuilder.java Wed Apr 27 01:27:09 2016 +0800 1.3 @@ -0,0 +1,918 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.sun.tools.internal.xjc.reader.xmlschema; 1.30 + 1.31 +import java.io.StringWriter; 1.32 +import java.math.BigInteger; 1.33 +import java.text.ParseException; 1.34 +import java.util.ArrayList; 1.35 +import java.util.Arrays; 1.36 +import java.util.Collections; 1.37 +import java.util.HashMap; 1.38 +import java.util.HashSet; 1.39 +import java.util.List; 1.40 +import java.util.Map; 1.41 +import java.util.Set; 1.42 +import java.util.Stack; 1.43 + 1.44 +import javax.activation.MimeTypeParseException; 1.45 +import javax.xml.bind.DatatypeConverter; 1.46 + 1.47 +import com.sun.codemodel.internal.JJavaName; 1.48 +import com.sun.codemodel.internal.util.JavadocEscapeWriter; 1.49 +import com.sun.xml.internal.bind.v2.WellKnownNamespace; 1.50 +import com.sun.tools.internal.xjc.ErrorReceiver; 1.51 +import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo; 1.52 +import com.sun.tools.internal.xjc.model.CClassInfo; 1.53 +import com.sun.tools.internal.xjc.model.CClassInfoParent; 1.54 +import com.sun.tools.internal.xjc.model.CClassRef; 1.55 +import com.sun.tools.internal.xjc.model.CEnumConstant; 1.56 +import com.sun.tools.internal.xjc.model.CEnumLeafInfo; 1.57 +import com.sun.tools.internal.xjc.model.CNonElement; 1.58 +import com.sun.tools.internal.xjc.model.Model; 1.59 +import com.sun.tools.internal.xjc.model.TypeUse; 1.60 +import com.sun.tools.internal.xjc.model.TypeUseFactory; 1.61 +import com.sun.tools.internal.xjc.reader.Const; 1.62 +import com.sun.tools.internal.xjc.reader.Ring; 1.63 +import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIConversion; 1.64 +import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnum; 1.65 +import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnumMember; 1.66 +import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIProperty; 1.67 +import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo; 1.68 +import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.EnumMemberMode; 1.69 +import com.sun.tools.internal.xjc.util.MimeTypeRange; 1.70 + 1.71 +import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_MIME_URI; 1.72 + 1.73 +import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapterMarker; 1.74 +import com.sun.xml.internal.xsom.XSAttributeDecl; 1.75 +import com.sun.xml.internal.xsom.XSComplexType; 1.76 +import com.sun.xml.internal.xsom.XSComponent; 1.77 +import com.sun.xml.internal.xsom.XSElementDecl; 1.78 +import com.sun.xml.internal.xsom.XSFacet; 1.79 +import com.sun.xml.internal.xsom.XSListSimpleType; 1.80 +import com.sun.xml.internal.xsom.XSRestrictionSimpleType; 1.81 +import com.sun.xml.internal.xsom.XSSimpleType; 1.82 +import com.sun.xml.internal.xsom.XSUnionSimpleType; 1.83 +import com.sun.xml.internal.xsom.XSVariety; 1.84 +import com.sun.xml.internal.xsom.impl.util.SchemaWriter; 1.85 +import com.sun.xml.internal.xsom.visitor.XSSimpleTypeFunction; 1.86 +import com.sun.xml.internal.xsom.visitor.XSVisitor; 1.87 + 1.88 +import org.xml.sax.Locator; 1.89 + 1.90 +/** 1.91 + * Builds {@link TypeUse} from simple types. 1.92 + * 1.93 + * <p> 1.94 + * This code consists of two main portions. The {@link #compose(XSSimpleType)} method 1.95 + * and {@link #composer} forms an outer cycle, which gradually ascends the type 1.96 + * inheritance chain until it finds the suitable binding. When it does this 1.97 + * {@link #initiatingType} is set to the type which started binding, so that we can refer 1.98 + * to the actual constraint facets and such that are applicable on the type. 1.99 + * 1.100 + * <p> 1.101 + * For each intermediate type in the chain, the {@link #find(XSSimpleType)} method 1.102 + * is used to find the binding on that type, sine the outer loop is doing the ascending, 1.103 + * this method only sees if the current type has some binding available. 1.104 + * 1.105 + * <p> 1.106 + * There is at least one ugly code that you need to aware of 1.107 + * when you are modifying the code. See the documentation 1.108 + * about <a href="package.html#stref_cust"> 1.109 + * "simple type customization at the point of reference."</a> 1.110 + * 1.111 + * 1.112 + * @author 1.113 + * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) 1.114 + */ 1.115 +public final class SimpleTypeBuilder extends BindingComponent { 1.116 + 1.117 + protected final BGMBuilder builder = Ring.get(BGMBuilder.class); 1.118 + 1.119 + private final Model model = Ring.get(Model.class); 1.120 + 1.121 + /** 1.122 + * The component that is refering to the simple type 1.123 + * which we are building. This is ugly but necessary 1.124 + * to support the customization of simple types at 1.125 + * its point of reference. See my comment at the header 1.126 + * of this class for details. 1.127 + * 1.128 + * UGLY: Implemented as a Stack of XSComponent to fix a bug 1.129 + */ 1.130 + public final Stack<XSComponent> refererStack = new Stack<XSComponent>(); 1.131 + 1.132 + /** 1.133 + * Records what xmime:expectedContentTypes annotations we honored and processed, 1.134 + * so that we can later check if the user had these annotations in the places 1.135 + * where we didn't anticipate them. 1.136 + */ 1.137 + private final Set<XSComponent> acknowledgedXmimeContentTypes = new HashSet<XSComponent>(); 1.138 + 1.139 + /** 1.140 + * The type that was originally passed to this {@link SimpleTypeBuilder#build(XSSimpleType)}. 1.141 + * Never null. 1.142 + */ 1.143 + private XSSimpleType initiatingType; 1.144 + 1.145 + /** {@link TypeUse}s for the built-in types. Read-only. */ 1.146 + public static final Map<String,TypeUse> builtinConversions = new HashMap<String,TypeUse>(); 1.147 + 1.148 + 1.149 + /** 1.150 + * Entry point from outside. Builds a BGM type expression 1.151 + * from a simple type schema component. 1.152 + * 1.153 + * @param type 1.154 + * the simple type to be bound. 1.155 + */ 1.156 + public TypeUse build( XSSimpleType type ) { 1.157 + XSSimpleType oldi = initiatingType; 1.158 + this.initiatingType = type; 1.159 + 1.160 + TypeUse e = checkRefererCustomization(type); 1.161 + if(e==null) 1.162 + e = compose(type); 1.163 + 1.164 + initiatingType = oldi; 1.165 + 1.166 + return e; 1.167 + } 1.168 + 1.169 + /** 1.170 + * A version of the {@link #build(XSSimpleType)} method 1.171 + * used to bind the definition of a class generated from 1.172 + * the given simple type. 1.173 + */ 1.174 + public TypeUse buildDef( XSSimpleType type ) { 1.175 + XSSimpleType oldi = initiatingType; 1.176 + this.initiatingType = type; 1.177 + 1.178 + TypeUse e = type.apply(composer); 1.179 + 1.180 + initiatingType = oldi; 1.181 + 1.182 + return e; 1.183 + } 1.184 + 1.185 + 1.186 + /** 1.187 + * Returns a javaType customization specified to the referer, if present. 1.188 + * @return can be null. 1.189 + */ 1.190 + private BIConversion getRefererCustomization() { 1.191 + BindInfo info = builder.getBindInfo(getReferer()); 1.192 + BIProperty prop = info.get(BIProperty.class); 1.193 + if(prop==null) return null; 1.194 + return prop.getConv(); 1.195 + } 1.196 + 1.197 + public XSComponent getReferer() { 1.198 + return refererStack.peek(); 1.199 + } 1.200 + 1.201 + /** 1.202 + * Checks if the referer has a conversion customization or not. 1.203 + * If it does, use it to bind this simple type. Otherwise 1.204 + * return null; 1.205 + */ 1.206 + private TypeUse checkRefererCustomization( XSSimpleType type ) { 1.207 + 1.208 + // assertion check. referer must be set properly 1.209 + // before the build method is called. 1.210 + // since the handling of the simple type point-of-reference 1.211 + // customization is very error prone, it deserves a strict 1.212 + // assertion check. 1.213 + // UGLY CODE WARNING 1.214 + XSComponent top = getReferer(); 1.215 + 1.216 + if( top instanceof XSElementDecl ) { 1.217 + // if the parent is element type, its content type must be us. 1.218 + XSElementDecl eref = (XSElementDecl)top; 1.219 + assert eref.getType()==type; 1.220 + 1.221 + // for elements, you can't use <property>, 1.222 + // so we allow javaType to appear directly. 1.223 + BindInfo info = builder.getBindInfo(top); 1.224 + BIConversion conv = info.get(BIConversion.class); 1.225 + if(conv!=null) { 1.226 + conv.markAsAcknowledged(); 1.227 + // the conversion is given. 1.228 + return conv.getTypeUse(type); 1.229 + } 1.230 + detectJavaTypeCustomization(); 1.231 + } else 1.232 + if( top instanceof XSAttributeDecl ) { 1.233 + XSAttributeDecl aref = (XSAttributeDecl)top; 1.234 + assert aref.getType()==type; 1.235 + detectJavaTypeCustomization(); 1.236 + } else 1.237 + if( top instanceof XSComplexType ) { 1.238 + XSComplexType tref = (XSComplexType)top; 1.239 + assert tref.getBaseType()==type || tref.getContentType()==type; 1.240 + detectJavaTypeCustomization(); 1.241 + } else 1.242 + if( top == type ) { 1.243 + // this means the simple type is built by itself and 1.244 + // not because it's referenced by something. 1.245 + } else 1.246 + // unexpected referer type. 1.247 + assert false; 1.248 + 1.249 + // now we are certain that the referer is OK. 1.250 + // see if it has a conversion customization. 1.251 + BIConversion conv = getRefererCustomization(); 1.252 + if(conv!=null) { 1.253 + conv.markAsAcknowledged(); 1.254 + // the conversion is given. 1.255 + return conv.getTypeUse(type); 1.256 + } else 1.257 + // not found 1.258 + return null; 1.259 + } 1.260 + 1.261 + /** 1.262 + * Detect "javaType" customizations placed directly on simple types, rather 1.263 + * than being enclosed by "property" and "baseType" customizations (see 1.264 + * sec 6.8.1 of the spec). 1.265 + * 1.266 + * Report an error if any exist. 1.267 + */ 1.268 + private void detectJavaTypeCustomization() { 1.269 + BindInfo info = builder.getBindInfo(getReferer()); 1.270 + BIConversion conv = info.get(BIConversion.class); 1.271 + 1.272 + if( conv != null ) { 1.273 + // ack this conversion to prevent further error messages 1.274 + conv.markAsAcknowledged(); 1.275 + 1.276 + // report the error 1.277 + getErrorReporter().error( conv.getLocation(), 1.278 + Messages.ERR_UNNESTED_JAVATYPE_CUSTOMIZATION_ON_SIMPLETYPE ); 1.279 + } 1.280 + } 1.281 + 1.282 + /** 1.283 + * Recursively decend the type inheritance chain to find a binding. 1.284 + */ 1.285 + TypeUse compose( XSSimpleType t ) { 1.286 + TypeUse e = find(t); 1.287 + if(e!=null) return e; 1.288 + return t.apply(composer); 1.289 + } 1.290 + 1.291 + public final XSSimpleTypeFunction<TypeUse> composer = new XSSimpleTypeFunction<TypeUse>() { 1.292 + 1.293 + public TypeUse listSimpleType(XSListSimpleType type) { 1.294 + // bind item type individually and then compose them into a list 1.295 + // facets on the list shouldn't be taken account when binding item types, 1.296 + // so weed to call build(), not compose(). 1.297 + XSSimpleType itemType = type.getItemType(); 1.298 + refererStack.push(itemType); 1.299 + TypeUse tu = TypeUseFactory.makeCollection(build(type.getItemType())); 1.300 + refererStack.pop(); 1.301 + return tu; 1.302 + } 1.303 + 1.304 + public TypeUse unionSimpleType(XSUnionSimpleType type) { 1.305 + boolean isCollection = false; 1.306 + for( int i=0; i<type.getMemberSize(); i++ ) 1.307 + if(type.getMember(i).getVariety()==XSVariety.LIST || type.getMember(i).getVariety()==XSVariety.UNION) { 1.308 + isCollection = true; 1.309 + break; 1.310 + } 1.311 + 1.312 + TypeUse r = CBuiltinLeafInfo.STRING; 1.313 + if(isCollection) 1.314 + r = TypeUseFactory.makeCollection(r); 1.315 + return r; 1.316 + } 1.317 + 1.318 + public TypeUse restrictionSimpleType(XSRestrictionSimpleType type) { 1.319 + // just process the base type. 1.320 + return compose(type.getSimpleBaseType()); 1.321 + } 1.322 + }; 1.323 + 1.324 + 1.325 + /** 1.326 + * Checks if there's any binding available on the given type. 1.327 + * 1.328 + * @return 1.329 + * null if not (which causes the {@link #compose(XSSimpleType)} method 1.330 + * to do ascending. 1.331 + */ 1.332 + private TypeUse find( XSSimpleType type ) { 1.333 + TypeUse r; 1.334 + boolean noAutoEnum = false; 1.335 + 1.336 + // check for user specified conversion 1.337 + BindInfo info = builder.getBindInfo(type); 1.338 + BIConversion conv = info.get(BIConversion.class); 1.339 + 1.340 + if( conv!=null ) { 1.341 + // a conversion was found 1.342 + conv.markAsAcknowledged(); 1.343 + return conv.getTypeUse(type); 1.344 + } 1.345 + 1.346 + // look for enum customization, which is another user specified conversion 1.347 + BIEnum en = info.get(BIEnum.class); 1.348 + if( en!=null ) { 1.349 + en.markAsAcknowledged(); 1.350 + 1.351 + if(!en.isMapped()) { 1.352 + noAutoEnum = true; 1.353 + } else { 1.354 + // if an enum customization is specified, make sure 1.355 + // the type is OK 1.356 + if( !canBeMappedToTypeSafeEnum(type) ) { 1.357 + getErrorReporter().error( en.getLocation(), 1.358 + Messages.ERR_CANNOT_BE_TYPE_SAFE_ENUM ); 1.359 + getErrorReporter().error( type.getLocator(), 1.360 + Messages.ERR_CANNOT_BE_TYPE_SAFE_ENUM_LOCATION ); 1.361 + // recover by ignoring this customization 1.362 + return null; 1.363 + } 1.364 + 1.365 + // reference? 1.366 + if(en.ref!=null) { 1.367 + if(!JJavaName.isFullyQualifiedClassName(en.ref)) { 1.368 + Ring.get(ErrorReceiver.class).error( en.getLocation(), 1.369 + Messages.format(Messages.ERR_INCORRECT_CLASS_NAME, en.ref) ); 1.370 + // recover by ignoring @ref 1.371 + return null; 1.372 + } 1.373 + 1.374 + return new CClassRef(model, type, en, info.toCustomizationList() ); 1.375 + } 1.376 + 1.377 + // list and union cannot be mapped to a type-safe enum, 1.378 + // so in this stage we can safely cast it to XSRestrictionSimpleType 1.379 + return bindToTypeSafeEnum( (XSRestrictionSimpleType)type, 1.380 + en.className, en.javadoc, en.members, 1.381 + getEnumMemberMode().getModeWithEnum(), 1.382 + en.getLocation() ); 1.383 + } 1.384 + } 1.385 + 1.386 + 1.387 + // if the type is built in, look for the default binding 1.388 + if(type.getTargetNamespace().equals(WellKnownNamespace.XML_SCHEMA)) { 1.389 + String name = type.getName(); 1.390 + if(name!=null) { 1.391 + r = lookupBuiltin(name); 1.392 + if(r!=null) 1.393 + return r; 1.394 + } 1.395 + } 1.396 + 1.397 + // also check for swaRef 1.398 + if(type.getTargetNamespace().equals(WellKnownNamespace.SWA_URI)) { 1.399 + String name = type.getName(); 1.400 + if(name!=null && name.equals("swaRef")) 1.401 + return CBuiltinLeafInfo.STRING.makeAdapted(SwaRefAdapterMarker.class,false); 1.402 + } 1.403 + 1.404 + 1.405 + // see if this type should be mapped to a type-safe enumeration by default. 1.406 + // if so, built a EnumXDucer from it and return it. 1.407 + if(type.isRestriction() && !noAutoEnum) { 1.408 + XSRestrictionSimpleType rst = type.asRestriction(); 1.409 + if(shouldBeMappedToTypeSafeEnumByDefault(rst)) { 1.410 + r = bindToTypeSafeEnum(rst,null,null, Collections.<String, BIEnumMember>emptyMap(), 1.411 + getEnumMemberMode(),null); 1.412 + if(r!=null) 1.413 + return r; 1.414 + } 1.415 + } 1.416 + 1.417 + return (CNonElement)getClassSelector()._bindToClass(type,null,false); 1.418 + } 1.419 + 1.420 + private static Set<XSRestrictionSimpleType> reportedEnumMemberSizeWarnings; 1.421 + 1.422 + /** 1.423 + * Returns true if a type-safe enum should be created from 1.424 + * the given simple type by default without an explicit <jaxb:enum> customization. 1.425 + */ 1.426 + private boolean shouldBeMappedToTypeSafeEnumByDefault( XSRestrictionSimpleType type ) { 1.427 + 1.428 + // if not, there will be a problem wrt the class name of this type safe enum type. 1.429 + if( type.isLocal() ) return false; 1.430 + 1.431 + // if redefined, we should map the new definition, not the old one. 1.432 + if( type.getRedefinedBy()!=null ) return false; 1.433 + 1.434 + List<XSFacet> facets = type.getDeclaredFacets(XSFacet.FACET_ENUMERATION); 1.435 + if( facets.isEmpty() ) 1.436 + // if the type itself doesn't have the enumeration facet, 1.437 + // it won't be mapped to a type-safe enum. 1.438 + return false; 1.439 + 1.440 + if(facets.size() > builder.getGlobalBinding().getDefaultEnumMemberSizeCap()) { 1.441 + // if there are too many facets, it's not very useful 1.442 + // produce warning when simple type is not mapped to enum 1.443 + // see issue https://jaxb.dev.java.net/issues/show_bug.cgi?id=711 1.444 + 1.445 + if(reportedEnumMemberSizeWarnings == null) 1.446 + reportedEnumMemberSizeWarnings = new HashSet<XSRestrictionSimpleType>(); 1.447 + 1.448 + if(!reportedEnumMemberSizeWarnings.contains(type)) { 1.449 + getErrorReporter().warning(type.getLocator(), Messages.WARN_ENUM_MEMBER_SIZE_CAP, 1.450 + type.getName(), facets.size(), builder.getGlobalBinding().getDefaultEnumMemberSizeCap()); 1.451 + 1.452 + reportedEnumMemberSizeWarnings.add(type); 1.453 + } 1.454 + 1.455 + return false; 1.456 + } 1.457 + 1.458 + if( !canBeMappedToTypeSafeEnum(type) ) 1.459 + // we simply can't map this to an enumeration 1.460 + return false; 1.461 + 1.462 + // check for collisions among constant names. if a collision will happen, 1.463 + // don't try to bind it to an enum. 1.464 + 1.465 + // return true only when this type is derived from one of the "enum base type". 1.466 + for( XSSimpleType t = type; t!=null; t=t.getSimpleBaseType() ) 1.467 + if( t.isGlobal() && builder.getGlobalBinding().canBeMappedToTypeSafeEnum(t) ) 1.468 + return true; 1.469 + 1.470 + return false; 1.471 + } 1.472 + 1.473 + 1.474 + private static final Set<String> builtinTypeSafeEnumCapableTypes; 1.475 + 1.476 + static { 1.477 + Set<String> s = new HashSet<String>(); 1.478 + 1.479 + // see a bullet of 6.5.1 of the spec. 1.480 + String[] typeNames = new String[] { 1.481 + "string", "boolean", "float", "decimal", "double", "anyURI" 1.482 + }; 1.483 + s.addAll(Arrays.asList(typeNames)); 1.484 + 1.485 + builtinTypeSafeEnumCapableTypes = Collections.unmodifiableSet(s); 1.486 + } 1.487 + 1.488 + 1.489 + /** 1.490 + * Returns true if the given simple type can be mapped to a 1.491 + * type-safe enum class. 1.492 + * 1.493 + * <p> 1.494 + * JAXB spec places a restrictrion as to what type can be 1.495 + * mapped to a type-safe enum. This method enforces this 1.496 + * constraint. 1.497 + */ 1.498 + public static boolean canBeMappedToTypeSafeEnum( XSSimpleType type ) { 1.499 + do { 1.500 + if( WellKnownNamespace.XML_SCHEMA.equals(type.getTargetNamespace()) ) { 1.501 + // type must be derived from one of these types 1.502 + String localName = type.getName(); 1.503 + if( localName!=null ) { 1.504 + if( localName.equals("anySimpleType") ) 1.505 + return false; // catch all case 1.506 + if( localName.equals("ID") || localName.equals("IDREF") ) 1.507 + return false; // not ID/IDREF 1.508 + 1.509 + // other allowed list 1.510 + if( builtinTypeSafeEnumCapableTypes.contains(localName) ) 1.511 + return true; 1.512 + } 1.513 + } 1.514 + 1.515 + type = type.getSimpleBaseType(); 1.516 + } while( type!=null ); 1.517 + 1.518 + return false; 1.519 + } 1.520 + 1.521 + 1.522 + 1.523 + /** 1.524 + * Builds a type-safe enum conversion from a simple type 1.525 + * with enumeration facets. 1.526 + * 1.527 + * @param className 1.528 + * The class name of the type-safe enum. Or null to 1.529 + * create a default name. 1.530 + * @param javadoc 1.531 + * Additional javadoc that will be added at the beginning of the 1.532 + * class, or null if none is necessary. 1.533 + * @param members 1.534 + * A map from enumeration values (as String) to BIEnumMember objects. 1.535 + * if some of the value names need to be overrided. 1.536 + * Cannot be null, but the map may not contain entries 1.537 + * for all enumeration values. 1.538 + * @param loc 1.539 + * The source location where the above customizations are 1.540 + * specified, or null if none is available. 1.541 + */ 1.542 + private TypeUse bindToTypeSafeEnum( XSRestrictionSimpleType type, 1.543 + String className, String javadoc, Map<String,BIEnumMember> members, 1.544 + EnumMemberMode mode, Locator loc ) { 1.545 + 1.546 + if( loc==null ) // use the location of the simple type as the default 1.547 + loc = type.getLocator(); 1.548 + 1.549 + if( className==null ) { 1.550 + // infer the class name. For this to be possible, 1.551 + // the simple type must be a global one. 1.552 + if( !type.isGlobal() ) { 1.553 + getErrorReporter().error( loc, Messages.ERR_NO_ENUM_NAME_AVAILABLE ); 1.554 + // recover by returning a meaningless conversion 1.555 + return CBuiltinLeafInfo.STRING; 1.556 + } 1.557 + className = type.getName(); 1.558 + } 1.559 + 1.560 + // we apply name conversion in any case 1.561 + className = builder.deriveName(className,type); 1.562 + 1.563 + {// compute Javadoc 1.564 + StringWriter out = new StringWriter(); 1.565 + SchemaWriter sw = new SchemaWriter(new JavadocEscapeWriter(out)); 1.566 + type.visit((XSVisitor)sw); 1.567 + 1.568 + if(javadoc!=null) javadoc += "\n\n"; 1.569 + else javadoc = ""; 1.570 + 1.571 + javadoc += Messages.format( Messages.JAVADOC_HEADING, type.getName() ) 1.572 + +"\n<p>\n<pre>\n"+out.getBuffer()+"</pre>"; 1.573 + 1.574 + } 1.575 + 1.576 + // build base type 1.577 + refererStack.push(type.getSimpleBaseType()); 1.578 + TypeUse use = build(type.getSimpleBaseType()); 1.579 + refererStack.pop(); 1.580 + 1.581 + if(use.isCollection()) 1.582 + return null; // can't bind a list to enum constant 1.583 + 1.584 + CNonElement baseDt = use.getInfo(); // for now just ignore that case 1.585 + 1.586 + if(baseDt instanceof CClassInfo) 1.587 + return null; // can't bind to an enum if the base is a class, since we don't have the value constrctor 1.588 + 1.589 + // if the member names collide, re-generate numbered constant names. 1.590 + XSFacet[] errorRef = new XSFacet[1]; 1.591 + List<CEnumConstant> memberList = buildCEnumConstants(type, false, members, errorRef); 1.592 + if(memberList==null || checkMemberNameCollision(memberList)!=null) { 1.593 + switch(mode) { 1.594 + case SKIP: 1.595 + // abort 1.596 + return null; 1.597 + case ERROR: 1.598 + // error 1.599 + if(memberList==null) { 1.600 + getErrorReporter().error( errorRef[0].getLocator(), 1.601 + Messages.ERR_CANNOT_GENERATE_ENUM_NAME, 1.602 + errorRef[0].getValue() ); 1.603 + } else { 1.604 + CEnumConstant[] collision = checkMemberNameCollision(memberList); 1.605 + getErrorReporter().error( collision[0].getLocator(), 1.606 + Messages.ERR_ENUM_MEMBER_NAME_COLLISION, 1.607 + collision[0].getName() ); 1.608 + getErrorReporter().error( collision[1].getLocator(), 1.609 + Messages.ERR_ENUM_MEMBER_NAME_COLLISION_RELATED ); 1.610 + } 1.611 + return null; // recover from error 1.612 + case GENERATE: 1.613 + // generate 1.614 + memberList = buildCEnumConstants(type,true,members,null); 1.615 + break; 1.616 + } 1.617 + } 1.618 + if(memberList.isEmpty()) { 1.619 + getErrorReporter().error( loc, Messages.ERR_NO_ENUM_FACET ); 1.620 + return null; 1.621 + } 1.622 + 1.623 + // use the name of the simple type as the name of the class. 1.624 + CClassInfoParent scope; 1.625 + if(type.isGlobal()) 1.626 + scope = new CClassInfoParent.Package(getClassSelector().getPackage(type.getTargetNamespace())); 1.627 + else 1.628 + scope = getClassSelector().getClassScope(); 1.629 + CEnumLeafInfo xducer = new CEnumLeafInfo( model, BGMBuilder.getName(type), scope, 1.630 + className, baseDt, memberList, type, 1.631 + builder.getBindInfo(type).toCustomizationList(), loc ); 1.632 + xducer.javadoc = javadoc; 1.633 + 1.634 + BIConversion conv = new BIConversion.Static( type.getLocator(),xducer); 1.635 + conv.markAsAcknowledged(); 1.636 + 1.637 + // attach this new conversion object to this simple type 1.638 + // so that successive look up will use the same object. 1.639 + builder.getOrCreateBindInfo(type).addDecl(conv); 1.640 + 1.641 + return conv.getTypeUse(type); 1.642 + } 1.643 + 1.644 + /** 1.645 + * 1.646 + * @param errorRef 1.647 + * if constant names couldn't be generated, return a reference to that enum facet. 1.648 + * @return 1.649 + * null if unable to generate names for some of the constants. 1.650 + */ 1.651 + private List<CEnumConstant> buildCEnumConstants(XSRestrictionSimpleType type, boolean needsToGenerateMemberName, Map<String, BIEnumMember> members, XSFacet[] errorRef) { 1.652 + List<CEnumConstant> memberList = new ArrayList<CEnumConstant>(); 1.653 + int idx=1; 1.654 + Set<String> enums = new HashSet<String>(); // to avoid duplicates. See issue #366 1.655 + 1.656 + for( XSFacet facet : type.getDeclaredFacets(XSFacet.FACET_ENUMERATION)) { 1.657 + String name=null; 1.658 + String mdoc=builder.getBindInfo(facet).getDocumentation(); 1.659 + 1.660 + if(!enums.add(facet.getValue().value)) 1.661 + continue; // ignore the 2nd occasion 1.662 + 1.663 + if( needsToGenerateMemberName ) { 1.664 + // generate names for all member names. 1.665 + // this will even override names specified by the user. that's crazy. 1.666 + name = "VALUE_"+(idx++); 1.667 + } else { 1.668 + String facetValue = facet.getValue().value; 1.669 + BIEnumMember mem = members.get(facetValue); 1.670 + if( mem==null ) 1.671 + // look at the one attached to the facet object 1.672 + mem = builder.getBindInfo(facet).get(BIEnumMember.class); 1.673 + 1.674 + if (mem!=null) { 1.675 + name = mem.name; 1.676 + if (mdoc == null) { 1.677 + mdoc = mem.javadoc; 1.678 + } 1.679 + } 1.680 + 1.681 + if(name==null) { 1.682 + StringBuilder sb = new StringBuilder(); 1.683 + for( int i=0; i<facetValue.length(); i++) { 1.684 + char ch = facetValue.charAt(i); 1.685 + if(Character.isJavaIdentifierPart(ch)) 1.686 + sb.append(ch); 1.687 + else 1.688 + sb.append('_'); 1.689 + } 1.690 + name = model.getNameConverter().toConstantName(sb.toString()); 1.691 + } 1.692 + } 1.693 + 1.694 + if(!JJavaName.isJavaIdentifier(name)) { 1.695 + if(errorRef!=null) errorRef[0] = facet; 1.696 + return null; // unable to generate a name 1.697 + } 1.698 + 1.699 + memberList.add(new CEnumConstant(name,mdoc,facet.getValue().value,facet,builder.getBindInfo(facet).toCustomizationList(),facet.getLocator())); 1.700 + } 1.701 + return memberList; 1.702 + } 1.703 + 1.704 + /** 1.705 + * Returns non-null if {@link CEnumConstant}s have name collisions among them. 1.706 + * 1.707 + * @return 1.708 + * if there's a collision, return two {@link CEnumConstant}s that collided. 1.709 + * otherwise return null. 1.710 + */ 1.711 + private CEnumConstant[] checkMemberNameCollision( List<CEnumConstant> memberList ) { 1.712 + Map<String,CEnumConstant> names = new HashMap<String,CEnumConstant>(); 1.713 + for (CEnumConstant c : memberList) { 1.714 + CEnumConstant old = names.put(c.getName(),c); 1.715 + if(old!=null) 1.716 + // collision detected 1.717 + return new CEnumConstant[]{old,c}; 1.718 + } 1.719 + return null; 1.720 + } 1.721 + 1.722 + 1.723 + 1.724 + private EnumMemberMode getEnumMemberMode() { 1.725 + return builder.getGlobalBinding().getEnumMemberMode(); 1.726 + } 1.727 + 1.728 + private TypeUse lookupBuiltin( String typeLocalName ) { 1.729 + if(typeLocalName.equals("integer") || typeLocalName.equals("long")) { 1.730 + /* 1.731 + attempt an optimization so that we can 1.732 + improve the binding for types like this: 1.733 + 1.734 + <simpleType> 1.735 + <restriciton baseType="integer"> 1.736 + <maxInclusive value="100" /> 1.737 + </ 1.738 + </ 1.739 + 1.740 + ... to int, not BigInteger. 1.741 + */ 1.742 + 1.743 + BigInteger xe = readFacet(XSFacet.FACET_MAXEXCLUSIVE,-1); 1.744 + BigInteger xi = readFacet(XSFacet.FACET_MAXINCLUSIVE,0); 1.745 + BigInteger max = min(xe,xi); // most restrictive one takes precedence 1.746 + 1.747 + if(max!=null) { 1.748 + BigInteger ne = readFacet(XSFacet.FACET_MINEXCLUSIVE,+1); 1.749 + BigInteger ni = readFacet(XSFacet.FACET_MININCLUSIVE,0); 1.750 + BigInteger min = max(ne,ni); 1.751 + 1.752 + if(min!=null) { 1.753 + if(min.compareTo(INT_MIN )>=0 && max.compareTo(INT_MAX )<=0) 1.754 + typeLocalName = "int"; 1.755 + else 1.756 + if(min.compareTo(LONG_MIN)>=0 && max.compareTo(LONG_MAX)<=0) 1.757 + typeLocalName = "long"; 1.758 + } 1.759 + } 1.760 + } else 1.761 + if(typeLocalName.equals("boolean") && isRestrictedTo0And1()) { 1.762 + // this is seen in the SOAP schema and too common to ignore 1.763 + return CBuiltinLeafInfo.BOOLEAN_ZERO_OR_ONE; 1.764 + } else 1.765 + if(typeLocalName.equals("base64Binary")) { 1.766 + return lookupBinaryTypeBinding(); 1.767 + } else 1.768 + if(typeLocalName.equals("anySimpleType")) { 1.769 + if(getReferer() instanceof XSAttributeDecl || getReferer() instanceof XSSimpleType) 1.770 + return CBuiltinLeafInfo.STRING; 1.771 + else 1.772 + return CBuiltinLeafInfo.ANYTYPE; 1.773 + } 1.774 + return builtinConversions.get(typeLocalName); 1.775 + } 1.776 + 1.777 + /** 1.778 + * Decides the way xs:base64Binary binds. 1.779 + * 1.780 + * This method checks the expected media type. 1.781 + */ 1.782 + private TypeUse lookupBinaryTypeBinding() { 1.783 + XSComponent referer = getReferer(); 1.784 + String emt = referer.getForeignAttribute(XML_MIME_URI, Const.EXPECTED_CONTENT_TYPES); 1.785 + if(emt!=null) { 1.786 + acknowledgedXmimeContentTypes.add(referer); 1.787 + try { 1.788 + // see http://www.xml.com/lpt/a/2004/07/21/dive.html 1.789 + List<MimeTypeRange> types = MimeTypeRange.parseRanges(emt); 1.790 + MimeTypeRange mt = MimeTypeRange.merge(types); 1.791 + 1.792 + // see spec table I-1 in appendix I section 2.1.1 for bindings 1.793 + if(mt.majorType.equalsIgnoreCase("image")) 1.794 + return CBuiltinLeafInfo.IMAGE.makeMimeTyped(mt.toMimeType()); 1.795 + 1.796 + if(( mt.majorType.equalsIgnoreCase("application") || mt.majorType.equalsIgnoreCase("text")) 1.797 + && isXml(mt.subType)) 1.798 + return CBuiltinLeafInfo.XML_SOURCE.makeMimeTyped(mt.toMimeType()); 1.799 + 1.800 + if((mt.majorType.equalsIgnoreCase("text") && (mt.subType.equalsIgnoreCase("plain")) )) { 1.801 + return CBuiltinLeafInfo.STRING.makeMimeTyped(mt.toMimeType()); 1.802 + } 1.803 + 1.804 + return CBuiltinLeafInfo.DATA_HANDLER.makeMimeTyped(mt.toMimeType()); 1.805 + } catch (ParseException e) { 1.806 + getErrorReporter().error( referer.getLocator(), 1.807 + Messages.format(Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,emt, e.getMessage()) ); 1.808 + // recover by using the default 1.809 + } catch (MimeTypeParseException e) { 1.810 + getErrorReporter().error( referer.getLocator(), 1.811 + Messages.format(Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,emt, e.getMessage()) ); 1.812 + } 1.813 + } 1.814 + // default 1.815 + return CBuiltinLeafInfo.BASE64_BYTE_ARRAY; 1.816 + } 1.817 + 1.818 + public boolean isAcknowledgedXmimeContentTypes(XSComponent c) { 1.819 + return acknowledgedXmimeContentTypes.contains(c); 1.820 + } 1.821 + 1.822 + /** 1.823 + * Returns true if the specified sub-type is an XML type. 1.824 + */ 1.825 + private boolean isXml(String subType) { 1.826 + return subType.equals("xml") || subType.endsWith("+xml"); 1.827 + } 1.828 + 1.829 + /** 1.830 + * Returns true if the {@link #initiatingType} is restricted 1.831 + * to '0' and '1'. This logic is not complete, but it at least 1.832 + * finds the such definition in SOAP @mustUnderstand. 1.833 + */ 1.834 + private boolean isRestrictedTo0And1() { 1.835 + XSFacet pattern = initiatingType.getFacet(XSFacet.FACET_PATTERN); 1.836 + if(pattern!=null) { 1.837 + String v = pattern.getValue().value; 1.838 + if(v.equals("0|1") || v.equals("1|0") || v.equals("\\d")) 1.839 + return true; 1.840 + } 1.841 + XSFacet enumf = initiatingType.getFacet(XSFacet.FACET_ENUMERATION); 1.842 + if(enumf!=null) { 1.843 + String v = enumf.getValue().value; 1.844 + if(v.equals("0") || v.equals("1")) 1.845 + return true; 1.846 + } 1.847 + return false; 1.848 + } 1.849 + 1.850 + private BigInteger readFacet(String facetName,int offset) { 1.851 + XSFacet me = initiatingType.getFacet(facetName); 1.852 + if(me==null) 1.853 + return null; 1.854 + BigInteger bi = DatatypeConverter.parseInteger(me.getValue().value); 1.855 + if(offset!=0) 1.856 + bi = bi.add(BigInteger.valueOf(offset)); 1.857 + return bi; 1.858 + } 1.859 + 1.860 + private BigInteger min(BigInteger a, BigInteger b) { 1.861 + if(a==null) return b; 1.862 + if(b==null) return a; 1.863 + return a.min(b); 1.864 + } 1.865 + 1.866 + private BigInteger max(BigInteger a, BigInteger b) { 1.867 + if(a==null) return b; 1.868 + if(b==null) return a; 1.869 + return a.max(b); 1.870 + } 1.871 + 1.872 + private static final BigInteger LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE); 1.873 + private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE); 1.874 + private static final BigInteger INT_MIN = BigInteger.valueOf(Integer.MIN_VALUE); 1.875 + private static final BigInteger INT_MAX = BigInteger.valueOf(Integer.MAX_VALUE); 1.876 + 1.877 + static { 1.878 + // list of datatypes which have built-in conversions. 1.879 + // note that although xs:token and xs:normalizedString are not 1.880 + // specified in the spec, they need to be here because they 1.881 + // have different whitespace normalization semantics. 1.882 + Map<String,TypeUse> m = builtinConversions; 1.883 + 1.884 + // TODO: this is so dumb 1.885 + m.put("string", CBuiltinLeafInfo.STRING); 1.886 + m.put("anyURI", CBuiltinLeafInfo.STRING); 1.887 + m.put("boolean", CBuiltinLeafInfo.BOOLEAN); 1.888 + // we'll also look at the expected media type, so don't just add this to the map 1.889 + // m.put("base64Binary", CBuiltinLeafInfo.BASE64_BYTE_ARRAY); 1.890 + m.put("hexBinary", CBuiltinLeafInfo.HEXBIN_BYTE_ARRAY); 1.891 + m.put("float", CBuiltinLeafInfo.FLOAT); 1.892 + m.put("decimal", CBuiltinLeafInfo.BIG_DECIMAL); 1.893 + m.put("integer", CBuiltinLeafInfo.BIG_INTEGER); 1.894 + m.put("long", CBuiltinLeafInfo.LONG); 1.895 + m.put("unsignedInt", CBuiltinLeafInfo.LONG); 1.896 + m.put("int", CBuiltinLeafInfo.INT); 1.897 + m.put("unsignedShort", CBuiltinLeafInfo.INT); 1.898 + m.put("short", CBuiltinLeafInfo.SHORT); 1.899 + m.put("unsignedByte", CBuiltinLeafInfo.SHORT); 1.900 + m.put("byte", CBuiltinLeafInfo.BYTE); 1.901 + m.put("double", CBuiltinLeafInfo.DOUBLE); 1.902 + m.put("QName", CBuiltinLeafInfo.QNAME); 1.903 + m.put("NOTATION", CBuiltinLeafInfo.QNAME); 1.904 + m.put("dateTime", CBuiltinLeafInfo.CALENDAR); 1.905 + m.put("date", CBuiltinLeafInfo.CALENDAR); 1.906 + m.put("time", CBuiltinLeafInfo.CALENDAR); 1.907 + m.put("gYearMonth", CBuiltinLeafInfo.CALENDAR); 1.908 + m.put("gYear", CBuiltinLeafInfo.CALENDAR); 1.909 + m.put("gMonthDay", CBuiltinLeafInfo.CALENDAR); 1.910 + m.put("gDay", CBuiltinLeafInfo.CALENDAR); 1.911 + m.put("gMonth", CBuiltinLeafInfo.CALENDAR); 1.912 + m.put("duration", CBuiltinLeafInfo.DURATION); 1.913 + m.put("token", CBuiltinLeafInfo.TOKEN); 1.914 + m.put("normalizedString",CBuiltinLeafInfo.NORMALIZED_STRING); 1.915 + m.put("ID", CBuiltinLeafInfo.ID); 1.916 + m.put("IDREF", CBuiltinLeafInfo.IDREF); 1.917 + // TODO: handling dateTime, time, and date type 1.918 +// String[] names = { 1.919 +// "date", "dateTime", "time", "hexBinary" }; 1.920 + } 1.921 +}