1 /* 2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.internal.xjc.reader.xmlschema; 27 28 import java.io.StringWriter; 29 import java.math.BigInteger; 30 import java.text.ParseException; 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Set; 39 import java.util.Stack; 40 41 import javax.activation.MimeTypeParseException; 42 import javax.xml.bind.DatatypeConverter; 43 44 import com.sun.codemodel.internal.JJavaName; 45 import com.sun.codemodel.internal.util.JavadocEscapeWriter; 46 import com.sun.xml.internal.bind.v2.WellKnownNamespace; 47 import com.sun.tools.internal.xjc.ErrorReceiver; 48 import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo; 49 import com.sun.tools.internal.xjc.model.CClassInfo; 50 import com.sun.tools.internal.xjc.model.CClassInfoParent; 51 import com.sun.tools.internal.xjc.model.CClassRef; 52 import com.sun.tools.internal.xjc.model.CEnumConstant; 53 import com.sun.tools.internal.xjc.model.CEnumLeafInfo; 54 import com.sun.tools.internal.xjc.model.CNonElement; 55 import com.sun.tools.internal.xjc.model.Model; 56 import com.sun.tools.internal.xjc.model.TypeUse; 57 import com.sun.tools.internal.xjc.model.TypeUseFactory; 58 import com.sun.tools.internal.xjc.reader.Const; 59 import com.sun.tools.internal.xjc.reader.Ring; 60 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIConversion; 61 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnum; 62 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnumMember; 63 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIProperty; 64 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo; 65 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.EnumMemberMode; 66 import com.sun.tools.internal.xjc.util.MimeTypeRange; 67 68 import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_MIME_URI; 69 70 import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapterMarker; 71 import com.sun.xml.internal.xsom.XSAttributeDecl; 72 import com.sun.xml.internal.xsom.XSComplexType; 73 import com.sun.xml.internal.xsom.XSComponent; 74 import com.sun.xml.internal.xsom.XSElementDecl; 75 import com.sun.xml.internal.xsom.XSFacet; 76 import com.sun.xml.internal.xsom.XSListSimpleType; 77 import com.sun.xml.internal.xsom.XSRestrictionSimpleType; 78 import com.sun.xml.internal.xsom.XSSimpleType; 79 import com.sun.xml.internal.xsom.XSUnionSimpleType; 80 import com.sun.xml.internal.xsom.XSVariety; 81 import com.sun.xml.internal.xsom.impl.util.SchemaWriter; 82 import com.sun.xml.internal.xsom.visitor.XSSimpleTypeFunction; 83 import com.sun.xml.internal.xsom.visitor.XSVisitor; 84 85 import org.xml.sax.Locator; 86 87 /** 88 * Builds {@link TypeUse} from simple types. 89 * 90 * <p> 91 * This code consists of two main portions. The {@link #compose(XSSimpleType)} method 92 * and {@link #composer} forms an outer cycle, which gradually ascends the type 93 * inheritance chain until it finds the suitable binding. When it does this 94 * {@link #initiatingType} is set to the type which started binding, so that we can refer 95 * to the actual constraint facets and such that are applicable on the type. 96 * 97 * <p> 98 * For each intermediate type in the chain, the {@link #find(XSSimpleType)} method 99 * is used to find the binding on that type, sine the outer loop is doing the ascending, 100 * this method only sees if the current type has some binding available. 101 * 102 * <p> 103 * There is at least one ugly code that you need to aware of 104 * when you are modifying the code. See the documentation 105 * about <a href="package.html#stref_cust"> 106 * "simple type customization at the point of reference."</a> 107 * 108 * 109 * @author 110 * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) 111 */ 112 public final class SimpleTypeBuilder extends BindingComponent { 113 114 protected final BGMBuilder builder = Ring.get(BGMBuilder.class); 115 116 private final Model model = Ring.get(Model.class); 117 118 /** 119 * The component that is refering to the simple type 120 * which we are building. This is ugly but necessary 121 * to support the customization of simple types at 122 * its point of reference. See my comment at the header 123 * of this class for details. 124 * 125 * UGLY: Implemented as a Stack of XSComponent to fix a bug 126 */ 127 public final Stack<XSComponent> refererStack = new Stack<XSComponent>(); 128 129 /** 130 * Records what xmime:expectedContentTypes annotations we honored and processed, 131 * so that we can later check if the user had these annotations in the places 132 * where we didn't anticipate them. 133 */ 134 private final Set<XSComponent> acknowledgedXmimeContentTypes = new HashSet<XSComponent>(); 135 136 /** 137 * The type that was originally passed to this {@link SimpleTypeBuilder#build(XSSimpleType)}. 138 * Never null. 139 */ 140 private XSSimpleType initiatingType; 141 142 /** {@link TypeUse}s for the built-in types. Read-only. */ 143 public static final Map<String,TypeUse> builtinConversions; 144 145 146 /** 147 * Entry point from outside. Builds a BGM type expression 148 * from a simple type schema component. 149 * 150 * @param type 151 * the simple type to be bound. 152 */ 153 public TypeUse build( XSSimpleType type ) { 154 XSSimpleType oldi = initiatingType; 155 this.initiatingType = type; 156 157 TypeUse e = checkRefererCustomization(type); 158 if(e==null) 159 e = compose(type); 160 161 initiatingType = oldi; 162 163 return e; 164 } 165 166 /** 167 * A version of the {@link #build(XSSimpleType)} method 168 * used to bind the definition of a class generated from 169 * the given simple type. 170 */ 171 public TypeUse buildDef( XSSimpleType type ) { 172 XSSimpleType oldi = initiatingType; 173 this.initiatingType = type; 174 175 TypeUse e = type.apply(composer); 176 177 initiatingType = oldi; 178 179 return e; 180 } 181 182 183 /** 184 * Returns a javaType customization specified to the referer, if present. 185 * @return can be null. 186 */ 187 private BIConversion getRefererCustomization() { 188 BindInfo info = builder.getBindInfo(getReferer()); 189 BIProperty prop = info.get(BIProperty.class); 190 if(prop==null) return null; 191 return prop.getConv(); 192 } 193 194 public XSComponent getReferer() { 195 return refererStack.peek(); 196 } 197 198 /** 199 * Checks if the referer has a conversion customization or not. 200 * If it does, use it to bind this simple type. Otherwise 201 * return null; 202 */ 203 private TypeUse checkRefererCustomization( XSSimpleType type ) { 204 205 // assertion check. referer must be set properly 206 // before the build method is called. 207 // since the handling of the simple type point-of-reference 208 // customization is very error prone, it deserves a strict 209 // assertion check. 210 // UGLY CODE WARNING 211 XSComponent top = getReferer(); 212 213 if( top instanceof XSElementDecl ) { 214 // if the parent is element type, its content type must be us. 215 XSElementDecl eref = (XSElementDecl)top; 216 assert eref.getType()==type; 217 218 // for elements, you can't use <property>, 219 // so we allow javaType to appear directly. 220 BindInfo info = builder.getBindInfo(top); 221 BIConversion conv = info.get(BIConversion.class); 222 if(conv!=null) { 223 conv.markAsAcknowledged(); 224 // the conversion is given. 225 return conv.getTypeUse(type); 226 } 227 detectJavaTypeCustomization(); 228 } else 229 if( top instanceof XSAttributeDecl ) { 230 XSAttributeDecl aref = (XSAttributeDecl)top; 231 assert aref.getType()==type; 232 detectJavaTypeCustomization(); 233 } else 234 if( top instanceof XSComplexType ) { 235 XSComplexType tref = (XSComplexType)top; 236 assert tref.getBaseType()==type || tref.getContentType()==type; 237 detectJavaTypeCustomization(); 238 } else 239 if( top == type ) { 240 // this means the simple type is built by itself and 241 // not because it's referenced by something. 242 } else 243 // unexpected referer type. 244 assert false; 245 246 // now we are certain that the referer is OK. 247 // see if it has a conversion customization. 248 BIConversion conv = getRefererCustomization(); 249 if(conv!=null) { 250 conv.markAsAcknowledged(); 251 // the conversion is given. 252 return conv.getTypeUse(type); 253 } else 254 // not found 255 return null; 256 } 257 258 /** 259 * Detect "javaType" customizations placed directly on simple types, rather 260 * than being enclosed by "property" and "baseType" customizations (see 261 * sec 6.8.1 of the spec). 262 * 263 * Report an error if any exist. 264 */ 265 private void detectJavaTypeCustomization() { 266 BindInfo info = builder.getBindInfo(getReferer()); 267 BIConversion conv = info.get(BIConversion.class); 268 269 if( conv != null ) { 270 // ack this conversion to prevent further error messages 271 conv.markAsAcknowledged(); 272 273 // report the error 274 getErrorReporter().error( conv.getLocation(), 275 Messages.ERR_UNNESTED_JAVATYPE_CUSTOMIZATION_ON_SIMPLETYPE ); 276 } 277 } 278 279 /** 280 * Recursively decend the type inheritance chain to find a binding. 281 */ 282 TypeUse compose( XSSimpleType t ) { 283 TypeUse e = find(t); 284 if(e!=null) return e; 285 return t.apply(composer); 286 } 287 288 public final XSSimpleTypeFunction<TypeUse> composer = new XSSimpleTypeFunction<TypeUse>() { 289 290 public TypeUse listSimpleType(XSListSimpleType type) { 291 // bind item type individually and then compose them into a list 292 // facets on the list shouldn't be taken account when binding item types, 293 // so weed to call build(), not compose(). 294 XSSimpleType itemType = type.getItemType(); 295 refererStack.push(itemType); 296 TypeUse tu = TypeUseFactory.makeCollection(build(type.getItemType())); 297 refererStack.pop(); 298 return tu; 299 } 300 301 public TypeUse unionSimpleType(XSUnionSimpleType type) { 302 boolean isCollection = false; 303 for( int i=0; i<type.getMemberSize(); i++ ) 304 if(type.getMember(i).getVariety()==XSVariety.LIST || type.getMember(i).getVariety()==XSVariety.UNION) { 305 isCollection = true; 306 break; 307 } 308 309 TypeUse r = CBuiltinLeafInfo.STRING; 310 if(isCollection) 311 r = TypeUseFactory.makeCollection(r); 312 return r; 313 } 314 315 public TypeUse restrictionSimpleType(XSRestrictionSimpleType type) { 316 // just process the base type. 317 return compose(type.getSimpleBaseType()); 318 } 319 }; 320 321 322 /** 323 * Checks if there's any binding available on the given type. 324 * 325 * @return 326 * null if not (which causes the {@link #compose(XSSimpleType)} method 327 * to do ascending. 328 */ 329 private TypeUse find( XSSimpleType type ) { 330 TypeUse r; 331 boolean noAutoEnum = false; 332 333 // check for user specified conversion 334 BindInfo info = builder.getBindInfo(type); 335 BIConversion conv = info.get(BIConversion.class); 336 337 if( conv!=null ) { 338 // a conversion was found 339 conv.markAsAcknowledged(); 340 return conv.getTypeUse(type); 341 } 342 343 // look for enum customization, which is another user specified conversion 344 BIEnum en = info.get(BIEnum.class); 345 if( en!=null ) { 346 en.markAsAcknowledged(); 347 348 if(!en.isMapped()) { 349 noAutoEnum = true; 350 } else { 351 // if an enum customization is specified, make sure 352 // the type is OK 353 if( !canBeMappedToTypeSafeEnum(type) ) { 354 getErrorReporter().error( en.getLocation(), 355 Messages.ERR_CANNOT_BE_TYPE_SAFE_ENUM ); 356 getErrorReporter().error( type.getLocator(), 357 Messages.ERR_CANNOT_BE_TYPE_SAFE_ENUM_LOCATION ); 358 // recover by ignoring this customization 359 return null; 360 } 361 362 // reference? 363 if(en.ref!=null) { 364 if(!JJavaName.isFullyQualifiedClassName(en.ref)) { 365 Ring.get(ErrorReceiver.class).error( en.getLocation(), 366 Messages.format(Messages.ERR_INCORRECT_CLASS_NAME, en.ref) ); 367 // recover by ignoring @ref 368 return null; 369 } 370 371 return new CClassRef(model, type, en, info.toCustomizationList() ); 372 } 373 374 // list and union cannot be mapped to a type-safe enum, 375 // so in this stage we can safely cast it to XSRestrictionSimpleType 376 return bindToTypeSafeEnum( (XSRestrictionSimpleType)type, 377 en.className, en.javadoc, en.members, 378 getEnumMemberMode().getModeWithEnum(), 379 en.getLocation() ); 380 } 381 } 382 383 384 // if the type is built in, look for the default binding 385 if(type.getTargetNamespace().equals(WellKnownNamespace.XML_SCHEMA)) { 386 String name = type.getName(); 387 if(name!=null) { 388 r = lookupBuiltin(name); 389 if(r!=null) 390 return r; 391 } 392 } 393 394 // also check for swaRef 395 if(type.getTargetNamespace().equals(WellKnownNamespace.SWA_URI)) { 396 String name = type.getName(); 397 if(name!=null && name.equals("swaRef")) 398 return CBuiltinLeafInfo.STRING.makeAdapted(SwaRefAdapterMarker.class,false); 399 } 400 401 402 // see if this type should be mapped to a type-safe enumeration by default. 403 // if so, built a EnumXDucer from it and return it. 404 if(type.isRestriction() && !noAutoEnum) { 405 XSRestrictionSimpleType rst = type.asRestriction(); 406 if(shouldBeMappedToTypeSafeEnumByDefault(rst)) { 407 r = bindToTypeSafeEnum(rst,null,null, Collections.<String, BIEnumMember>emptyMap(), 408 getEnumMemberMode(),null); 409 if(r!=null) 410 return r; 411 } 412 } 413 414 return (CNonElement)getClassSelector()._bindToClass(type,null,false); 415 } 416 417 private static Set<XSRestrictionSimpleType> reportedEnumMemberSizeWarnings; 418 419 /** 420 * Returns true if a type-safe enum should be created from 421 * the given simple type by default without an explicit {@code <jaxb:enum>} customization. 422 */ 423 private boolean shouldBeMappedToTypeSafeEnumByDefault( XSRestrictionSimpleType type ) { 424 425 // if not, there will be a problem wrt the class name of this type safe enum type. 426 if( type.isLocal() ) return false; 427 428 // if redefined, we should map the new definition, not the old one. 429 if( type.getRedefinedBy()!=null ) return false; 430 431 List<XSFacet> facets = type.getDeclaredFacets(XSFacet.FACET_ENUMERATION); 432 if( facets.isEmpty() ) 433 // if the type itself doesn't have the enumeration facet, 434 // it won't be mapped to a type-safe enum. 435 return false; 436 437 if(facets.size() > builder.getGlobalBinding().getDefaultEnumMemberSizeCap()) { 438 // if there are too many facets, it's not very useful 439 // produce warning when simple type is not mapped to enum 440 // see issue https://jaxb.dev.java.net/issues/show_bug.cgi?id=711 441 442 if(reportedEnumMemberSizeWarnings == null) 443 reportedEnumMemberSizeWarnings = new HashSet<XSRestrictionSimpleType>(); 444 445 if(!reportedEnumMemberSizeWarnings.contains(type)) { 446 getErrorReporter().warning(type.getLocator(), Messages.WARN_ENUM_MEMBER_SIZE_CAP, 447 type.getName(), facets.size(), builder.getGlobalBinding().getDefaultEnumMemberSizeCap()); 448 449 reportedEnumMemberSizeWarnings.add(type); 450 } 451 452 return false; 453 } 454 455 if( !canBeMappedToTypeSafeEnum(type) ) 456 // we simply can't map this to an enumeration 457 return false; 458 459 // check for collisions among constant names. if a collision will happen, 460 // don't try to bind it to an enum. 461 462 // return true only when this type is derived from one of the "enum base type". 463 for( XSSimpleType t = type; t!=null; t=t.getSimpleBaseType() ) 464 if( t.isGlobal() && builder.getGlobalBinding().canBeMappedToTypeSafeEnum(t) ) 465 return true; 466 467 return false; 468 } 469 470 471 private static final Set<String> builtinTypeSafeEnumCapableTypes; 472 473 static { 474 Set<String> s = new HashSet<String>(); 475 476 // see a bullet of 6.5.1 of the spec. 477 String[] typeNames = new String[] { 478 "string", "boolean", "float", "decimal", "double", "anyURI" 479 }; 480 s.addAll(Arrays.asList(typeNames)); 481 482 builtinTypeSafeEnumCapableTypes = Collections.unmodifiableSet(s); 483 } 484 485 486 /** 487 * Returns true if the given simple type can be mapped to a 488 * type-safe enum class. 489 * 490 * <p> 491 * JAXB spec places a restrictrion as to what type can be 492 * mapped to a type-safe enum. This method enforces this 493 * constraint. 494 */ 495 public static boolean canBeMappedToTypeSafeEnum( XSSimpleType type ) { 496 do { 497 if( WellKnownNamespace.XML_SCHEMA.equals(type.getTargetNamespace()) ) { 498 // type must be derived from one of these types 499 String localName = type.getName(); 500 if( localName!=null ) { 501 if( localName.equals("anySimpleType") ) 502 return false; // catch all case 503 if( localName.equals("ID") || localName.equals("IDREF") ) 504 return false; // not ID/IDREF 505 506 // other allowed list 507 if( builtinTypeSafeEnumCapableTypes.contains(localName) ) 508 return true; 509 } 510 } 511 512 type = type.getSimpleBaseType(); 513 } while( type!=null ); 514 515 return false; 516 } 517 518 519 520 /** 521 * Builds a type-safe enum conversion from a simple type 522 * with enumeration facets. 523 * 524 * @param className 525 * The class name of the type-safe enum. Or null to 526 * create a default name. 527 * @param javadoc 528 * Additional javadoc that will be added at the beginning of the 529 * class, or null if none is necessary. 530 * @param members 531 * A map from enumeration values (as String) to BIEnumMember objects. 532 * if some of the value names need to be overrided. 533 * Cannot be null, but the map may not contain entries 534 * for all enumeration values. 535 * @param loc 536 * The source location where the above customizations are 537 * specified, or null if none is available. 538 */ 539 private TypeUse bindToTypeSafeEnum( XSRestrictionSimpleType type, 540 String className, String javadoc, Map<String,BIEnumMember> members, 541 EnumMemberMode mode, Locator loc ) { 542 543 if( loc==null ) // use the location of the simple type as the default 544 loc = type.getLocator(); 545 546 if( className==null ) { 547 // infer the class name. For this to be possible, 548 // the simple type must be a global one. 549 if( !type.isGlobal() ) { 550 getErrorReporter().error( loc, Messages.ERR_NO_ENUM_NAME_AVAILABLE ); 551 // recover by returning a meaningless conversion 552 return CBuiltinLeafInfo.STRING; 553 } 554 className = type.getName(); 555 } 556 557 // we apply name conversion in any case 558 className = builder.deriveName(className,type); 559 560 {// compute Javadoc 561 StringWriter out = new StringWriter(); 562 SchemaWriter sw = new SchemaWriter(new JavadocEscapeWriter(out)); 563 type.visit((XSVisitor)sw); 564 565 if(javadoc!=null) javadoc += "\n\n"; 566 else javadoc = ""; 567 568 javadoc += Messages.format( Messages.JAVADOC_HEADING, type.getName() ) 569 +"\n<p>\n<pre>\n"+out.getBuffer()+"</pre>"; 570 571 } 572 573 // build base type 574 refererStack.push(type.getSimpleBaseType()); 575 TypeUse use = build(type.getSimpleBaseType()); 576 refererStack.pop(); 577 578 if(use.isCollection()) 579 return null; // can't bind a list to enum constant 580 581 CNonElement baseDt = use.getInfo(); // for now just ignore that case 582 583 if(baseDt instanceof CClassInfo) 584 return null; // can't bind to an enum if the base is a class, since we don't have the value constrctor 585 586 // if the member names collide, re-generate numbered constant names. 587 XSFacet[] errorRef = new XSFacet[1]; 588 List<CEnumConstant> memberList = buildCEnumConstants(type, false, members, errorRef); 589 if(memberList==null || checkMemberNameCollision(memberList)!=null) { 590 switch(mode) { 591 case SKIP: 592 // abort 593 return null; 594 case ERROR: 595 // error 596 if(memberList==null) { 597 getErrorReporter().error( errorRef[0].getLocator(), 598 Messages.ERR_CANNOT_GENERATE_ENUM_NAME, 599 errorRef[0].getValue() ); 600 } else { 601 CEnumConstant[] collision = checkMemberNameCollision(memberList); 602 getErrorReporter().error( collision[0].getLocator(), 603 Messages.ERR_ENUM_MEMBER_NAME_COLLISION, 604 collision[0].getName() ); 605 getErrorReporter().error( collision[1].getLocator(), 606 Messages.ERR_ENUM_MEMBER_NAME_COLLISION_RELATED ); 607 } 608 return null; // recover from error 609 case GENERATE: 610 // generate 611 memberList = buildCEnumConstants(type,true,members,null); 612 break; 613 } 614 } 615 if(memberList.isEmpty()) { 616 getErrorReporter().error( loc, Messages.ERR_NO_ENUM_FACET ); 617 return null; 618 } 619 620 // use the name of the simple type as the name of the class. 621 CClassInfoParent scope; 622 if(type.isGlobal()) 623 scope = new CClassInfoParent.Package(getClassSelector().getPackage(type.getTargetNamespace())); 624 else 625 scope = getClassSelector().getClassScope(); 626 CEnumLeafInfo xducer = new CEnumLeafInfo( model, BGMBuilder.getName(type), scope, 627 className, baseDt, memberList, type, 628 builder.getBindInfo(type).toCustomizationList(), loc ); 629 xducer.javadoc = javadoc; 630 631 BIConversion conv = new BIConversion.Static( type.getLocator(),xducer); 632 conv.markAsAcknowledged(); 633 634 // attach this new conversion object to this simple type 635 // so that successive look up will use the same object. 636 builder.getOrCreateBindInfo(type).addDecl(conv); 637 638 return conv.getTypeUse(type); 639 } 640 641 /** 642 * 643 * @param errorRef 644 * if constant names couldn't be generated, return a reference to that enum facet. 645 * @return 646 * null if unable to generate names for some of the constants. 647 */ 648 private List<CEnumConstant> buildCEnumConstants(XSRestrictionSimpleType type, boolean needsToGenerateMemberName, Map<String, BIEnumMember> members, XSFacet[] errorRef) { 649 List<CEnumConstant> memberList = new ArrayList<CEnumConstant>(); 650 int idx=1; 651 Set<String> enums = new HashSet<String>(); // to avoid duplicates. See issue #366 652 653 for( XSFacet facet : type.getDeclaredFacets(XSFacet.FACET_ENUMERATION)) { 654 String name=null; 655 String mdoc=builder.getBindInfo(facet).getDocumentation(); 656 657 if(!enums.add(facet.getValue().value)) 658 continue; // ignore the 2nd occasion 659 660 if( needsToGenerateMemberName ) { 661 // generate names for all member names. 662 // this will even override names specified by the user. that's crazy. 663 name = "VALUE_"+(idx++); 664 } else { 665 String facetValue = facet.getValue().value; 666 BIEnumMember mem = members.get(facetValue); 667 if( mem==null ) 668 // look at the one attached to the facet object 669 mem = builder.getBindInfo(facet).get(BIEnumMember.class); 670 671 if (mem!=null) { 672 name = mem.name; 673 if (mdoc == null) { 674 mdoc = mem.javadoc; 675 } 676 } 677 678 if(name==null) { 679 StringBuilder sb = new StringBuilder(); 680 for( int i=0; i<facetValue.length(); i++) { 681 char ch = facetValue.charAt(i); 682 if(Character.isJavaIdentifierPart(ch)) 683 sb.append(ch); 684 else 685 sb.append('_'); 686 } 687 name = model.getNameConverter().toConstantName(sb.toString()); 688 } 689 } 690 691 if(!JJavaName.isJavaIdentifier(name)) { 692 if(errorRef!=null) errorRef[0] = facet; 693 return null; // unable to generate a name 694 } 695 696 memberList.add(new CEnumConstant(name,mdoc,facet.getValue().value,facet,builder.getBindInfo(facet).toCustomizationList(),facet.getLocator())); 697 } 698 return memberList; 699 } 700 701 /** 702 * Returns non-null if {@link CEnumConstant}s have name collisions among them. 703 * 704 * @return 705 * if there's a collision, return two {@link CEnumConstant}s that collided. 706 * otherwise return null. 707 */ 708 private CEnumConstant[] checkMemberNameCollision( List<CEnumConstant> memberList ) { 709 Map<String,CEnumConstant> names = new HashMap<String,CEnumConstant>(); 710 for (CEnumConstant c : memberList) { 711 CEnumConstant old = names.put(c.getName(),c); 712 if(old!=null) 713 // collision detected 714 return new CEnumConstant[]{old,c}; 715 } 716 return null; 717 } 718 719 720 721 private EnumMemberMode getEnumMemberMode() { 722 return builder.getGlobalBinding().getEnumMemberMode(); 723 } 724 725 private TypeUse lookupBuiltin( String typeLocalName ) { 726 if(typeLocalName.equals("integer") || typeLocalName.equals("long")) { 727 /* 728 attempt an optimization so that we can 729 improve the binding for types like this: 730 731 <simpleType> 732 <restriciton baseType="integer"> 733 <maxInclusive value="100" /> 734 </ 735 </ 736 737 ... to int, not BigInteger. 738 */ 739 740 BigInteger xe = readFacet(XSFacet.FACET_MAXEXCLUSIVE,-1); 741 BigInteger xi = readFacet(XSFacet.FACET_MAXINCLUSIVE,0); 742 BigInteger max = min(xe,xi); // most restrictive one takes precedence 743 744 if(max!=null) { 745 BigInteger ne = readFacet(XSFacet.FACET_MINEXCLUSIVE,+1); 746 BigInteger ni = readFacet(XSFacet.FACET_MININCLUSIVE,0); 747 BigInteger min = max(ne,ni); 748 749 if(min!=null) { 750 if(min.compareTo(INT_MIN )>=0 && max.compareTo(INT_MAX )<=0) 751 typeLocalName = "int"; 752 else 753 if(min.compareTo(LONG_MIN)>=0 && max.compareTo(LONG_MAX)<=0) 754 typeLocalName = "long"; 755 } 756 } 757 } else 758 if(typeLocalName.equals("boolean") && isRestrictedTo0And1()) { 759 // this is seen in the SOAP schema and too common to ignore 760 return CBuiltinLeafInfo.BOOLEAN_ZERO_OR_ONE; 761 } else 762 if(typeLocalName.equals("base64Binary")) { 763 return lookupBinaryTypeBinding(); 764 } else 765 if(typeLocalName.equals("anySimpleType")) { 766 if(getReferer() instanceof XSAttributeDecl || getReferer() instanceof XSSimpleType) 767 return CBuiltinLeafInfo.STRING; 768 else 769 return CBuiltinLeafInfo.ANYTYPE; 770 } 771 return builtinConversions.get(typeLocalName); 772 } 773 774 /** 775 * Decides the way xs:base64Binary binds. 776 * 777 * This method checks the expected media type. 778 */ 779 private TypeUse lookupBinaryTypeBinding() { 780 XSComponent referer = getReferer(); 781 String emt = referer.getForeignAttribute(XML_MIME_URI, Const.EXPECTED_CONTENT_TYPES); 782 if(emt!=null) { 783 acknowledgedXmimeContentTypes.add(referer); 784 try { 785 // see http://www.xml.com/lpt/a/2004/07/21/dive.html 786 List<MimeTypeRange> types = MimeTypeRange.parseRanges(emt); 787 MimeTypeRange mt = MimeTypeRange.merge(types); 788 789 // see spec table I-1 in appendix I section 2.1.1 for bindings 790 if(mt.majorType.equalsIgnoreCase("image")) 791 return CBuiltinLeafInfo.IMAGE.makeMimeTyped(mt.toMimeType()); 792 793 if(( mt.majorType.equalsIgnoreCase("application") || mt.majorType.equalsIgnoreCase("text")) 794 && isXml(mt.subType)) 795 return CBuiltinLeafInfo.XML_SOURCE.makeMimeTyped(mt.toMimeType()); 796 797 if((mt.majorType.equalsIgnoreCase("text") && (mt.subType.equalsIgnoreCase("plain")) )) { 798 return CBuiltinLeafInfo.STRING.makeMimeTyped(mt.toMimeType()); 799 } 800 801 return CBuiltinLeafInfo.DATA_HANDLER.makeMimeTyped(mt.toMimeType()); 802 } catch (ParseException e) { 803 getErrorReporter().error( referer.getLocator(), 804 Messages.format(Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,emt, e.getMessage()) ); 805 // recover by using the default 806 } catch (MimeTypeParseException e) { 807 getErrorReporter().error( referer.getLocator(), 808 Messages.format(Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,emt, e.getMessage()) ); 809 } 810 } 811 // default 812 return CBuiltinLeafInfo.BASE64_BYTE_ARRAY; 813 } 814 815 public boolean isAcknowledgedXmimeContentTypes(XSComponent c) { 816 return acknowledgedXmimeContentTypes.contains(c); 817 } 818 819 /** 820 * Returns true if the specified sub-type is an XML type. 821 */ 822 private boolean isXml(String subType) { 823 return subType.equals("xml") || subType.endsWith("+xml"); 824 } 825 826 /** 827 * Returns true if the {@link #initiatingType} is restricted 828 * to '0' and '1'. This logic is not complete, but it at least 829 * finds the such definition in SOAP @mustUnderstand. 830 */ 831 private boolean isRestrictedTo0And1() { 832 XSFacet pattern = initiatingType.getFacet(XSFacet.FACET_PATTERN); 833 if(pattern!=null) { 834 String v = pattern.getValue().value; 835 if(v.equals("0|1") || v.equals("1|0") || v.equals("\\d")) 836 return true; 837 } 838 XSFacet enumf = initiatingType.getFacet(XSFacet.FACET_ENUMERATION); 839 if(enumf!=null) { 840 String v = enumf.getValue().value; 841 if(v.equals("0") || v.equals("1")) 842 return true; 843 } 844 return false; 845 } 846 847 private BigInteger readFacet(String facetName,int offset) { 848 XSFacet me = initiatingType.getFacet(facetName); 849 if(me==null) 850 return null; 851 BigInteger bi = DatatypeConverter.parseInteger(me.getValue().value); 852 if(offset!=0) 853 bi = bi.add(BigInteger.valueOf(offset)); 854 return bi; 855 } 856 857 private BigInteger min(BigInteger a, BigInteger b) { 858 if(a==null) return b; 859 if(b==null) return a; 860 return a.min(b); 861 } 862 863 private BigInteger max(BigInteger a, BigInteger b) { 864 if(a==null) return b; 865 if(b==null) return a; 866 return a.max(b); 867 } 868 869 private static final BigInteger LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE); 870 private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE); 871 private static final BigInteger INT_MIN = BigInteger.valueOf(Integer.MIN_VALUE); 872 private static final BigInteger INT_MAX = BigInteger.valueOf(Integer.MAX_VALUE); 873 874 static { 875 // list of datatypes which have built-in conversions. 876 // note that although xs:token and xs:normalizedString are not 877 // specified in the spec, they need to be here because they 878 // have different whitespace normalization semantics. 879 Map<String,TypeUse> m = new HashMap<String,TypeUse>(); 880 881 // TODO: this is so dumb 882 m.put("string", CBuiltinLeafInfo.STRING); 883 m.put("anyURI", CBuiltinLeafInfo.STRING); 884 m.put("boolean", CBuiltinLeafInfo.BOOLEAN); 885 // we'll also look at the expected media type, so don't just add this to the map 886 // m.put("base64Binary", CBuiltinLeafInfo.BASE64_BYTE_ARRAY); 887 m.put("hexBinary", CBuiltinLeafInfo.HEXBIN_BYTE_ARRAY); 888 m.put("float", CBuiltinLeafInfo.FLOAT); 889 m.put("decimal", CBuiltinLeafInfo.BIG_DECIMAL); 890 m.put("integer", CBuiltinLeafInfo.BIG_INTEGER); 891 m.put("long", CBuiltinLeafInfo.LONG); 892 m.put("unsignedInt", CBuiltinLeafInfo.LONG); 893 m.put("int", CBuiltinLeafInfo.INT); 894 m.put("unsignedShort", CBuiltinLeafInfo.INT); 895 m.put("short", CBuiltinLeafInfo.SHORT); 896 m.put("unsignedByte", CBuiltinLeafInfo.SHORT); 897 m.put("byte", CBuiltinLeafInfo.BYTE); 898 m.put("double", CBuiltinLeafInfo.DOUBLE); 899 m.put("QName", CBuiltinLeafInfo.QNAME); 900 m.put("NOTATION", CBuiltinLeafInfo.QNAME); 901 m.put("dateTime", CBuiltinLeafInfo.CALENDAR); 902 m.put("date", CBuiltinLeafInfo.CALENDAR); 903 m.put("time", CBuiltinLeafInfo.CALENDAR); 904 m.put("gYearMonth", CBuiltinLeafInfo.CALENDAR); 905 m.put("gYear", CBuiltinLeafInfo.CALENDAR); 906 m.put("gMonthDay", CBuiltinLeafInfo.CALENDAR); 907 m.put("gDay", CBuiltinLeafInfo.CALENDAR); 908 m.put("gMonth", CBuiltinLeafInfo.CALENDAR); 909 m.put("duration", CBuiltinLeafInfo.DURATION); 910 m.put("token", CBuiltinLeafInfo.TOKEN); 911 m.put("normalizedString",CBuiltinLeafInfo.NORMALIZED_STRING); 912 m.put("ID", CBuiltinLeafInfo.ID); 913 m.put("IDREF", CBuiltinLeafInfo.IDREF); 914 915 builtinConversions = Collections.unmodifiableMap(m); 916 // TODO: handling dateTime, time, and date type 917 // String[] names = { 918 // "date", "dateTime", "time", "hexBinary" }; 919 } 920 }