1 /* 2 * Copyright (c) 1997, 2012, 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.bindinfo; 27 28 import java.util.Collections; 29 import java.util.HashMap; 30 import java.util.Map; 31 import java.util.Set; 32 33 import javax.xml.bind.annotation.XmlAttribute; 34 import javax.xml.bind.annotation.XmlElement; 35 import javax.xml.bind.annotation.XmlEnumValue; 36 import javax.xml.bind.annotation.XmlRootElement; 37 import javax.xml.bind.annotation.XmlTransient; 38 import javax.xml.namespace.QName; 39 40 import com.sun.codemodel.internal.ClassType; 41 import com.sun.codemodel.internal.JClassAlreadyExistsException; 42 import com.sun.codemodel.internal.JCodeModel; 43 import com.sun.codemodel.internal.JDefinedClass; 44 import com.sun.tools.internal.xjc.ErrorReceiver; 45 import com.sun.tools.internal.xjc.generator.bean.ImplStructureStrategy; 46 import static com.sun.tools.internal.xjc.generator.bean.ImplStructureStrategy.BEAN_ONLY; 47 import com.sun.tools.internal.xjc.model.Model; 48 import com.sun.tools.internal.xjc.reader.Const; 49 import com.sun.tools.internal.xjc.reader.Ring; 50 import com.sun.tools.internal.xjc.reader.xmlschema.SimpleTypeBuilder; 51 import com.sun.tools.internal.xjc.util.ReadOnlyAdapter; 52 import com.sun.xml.internal.bind.api.impl.NameConverter; 53 import com.sun.xml.internal.bind.v2.WellKnownNamespace; 54 import com.sun.xml.internal.xsom.XSDeclaration; 55 import com.sun.xml.internal.xsom.XSSchemaSet; 56 import com.sun.xml.internal.xsom.XSSimpleType; 57 58 /** 59 * Global binding customization. The code is highly temporary. 60 * 61 * <p> 62 * One of the information contained in a global customization 63 * is the default binding for properties. This object contains a 64 * BIProperty object to keep this information. 65 * 66 * @author 67 * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) 68 */ 69 @XmlRootElement(name="globalBindings") 70 public final class BIGlobalBinding extends AbstractDeclarationImpl { 71 72 73 /** 74 * Gets the name converter that will govern the XML->Java 75 * name conversion process for this compilation. 76 * 77 * <p> 78 * The "underscoreBinding" customization will determine 79 * the exact object returned from this method. The rest of XJC 80 * should just use the NameConverter interface. 81 * 82 * <p> 83 * Always non-null. 84 */ 85 @XmlTransient 86 public NameConverter nameConverter = NameConverter.standard; 87 88 // JAXB will use this property to set nameConverter 89 @XmlAttribute 90 void setUnderscoreBinding( UnderscoreBinding ub ) { 91 nameConverter = ub.nc; 92 } 93 94 UnderscoreBinding getUnderscoreBinding() { 95 throw new IllegalStateException(); // no need for this 96 } 97 98 public JDefinedClass getSuperClass() { 99 if(superClass==null) return null; 100 return superClass.getClazz(ClassType.CLASS); 101 } 102 103 public JDefinedClass getSuperInterface() { 104 if(superInterface==null) return null; 105 return superInterface.getClazz(ClassType.INTERFACE); 106 } 107 108 public BIProperty getDefaultProperty() { 109 return defaultProperty; 110 } 111 112 public boolean isJavaNamingConventionEnabled() { 113 return isJavaNamingConventionEnabled; 114 } 115 116 public BISerializable getSerializable() { 117 return serializable; 118 } 119 120 public boolean isGenerateElementClass() { 121 return generateElementClass; 122 } 123 124 public boolean isGenerateMixedExtensions() { 125 return generateMixedExtensions; 126 } 127 128 public boolean isChoiceContentPropertyEnabled() { 129 return choiceContentProperty; 130 } 131 132 public int getDefaultEnumMemberSizeCap() { 133 return defaultEnumMemberSizeCap; 134 } 135 136 public boolean isSimpleMode() { 137 return simpleMode!=null; 138 } 139 140 public boolean isRestrictionFreshType() { 141 return treatRestrictionLikeNewType !=null; 142 } 143 144 public EnumMemberMode getEnumMemberMode() { 145 return generateEnumMemberName; 146 } 147 148 public boolean isSimpleTypeSubstitution() { 149 return simpleTypeSubstitution; 150 } 151 152 public ImplStructureStrategy getCodeGenerationStrategy() { 153 return codeGenerationStrategy; 154 } 155 156 public LocalScoping getFlattenClasses() { 157 return flattenClasses; 158 } 159 160 /** 161 * Performs error check 162 */ 163 public void errorCheck() { 164 ErrorReceiver er = Ring.get(ErrorReceiver.class); 165 for (QName n : enumBaseTypes) { 166 XSSchemaSet xs = Ring.get(XSSchemaSet.class); 167 XSSimpleType st = xs.getSimpleType(n.getNamespaceURI(), n.getLocalPart()); 168 if(st==null) { 169 er.error(loc,Messages.ERR_UNDEFINED_SIMPLE_TYPE.format(n)); 170 continue; 171 } 172 173 if(!SimpleTypeBuilder.canBeMappedToTypeSafeEnum(st)) { 174 er.error(loc,Messages.ERR_CANNOT_BE_BOUND_TO_SIMPLETYPE.format(n)); 175 continue; 176 } 177 } 178 } 179 180 private static enum UnderscoreBinding { 181 @XmlEnumValue("asWordSeparator") 182 WORD_SEPARATOR(NameConverter.standard), 183 @XmlEnumValue("asCharInWord") 184 CHAR_IN_WORD(NameConverter.jaxrpcCompatible); 185 186 final NameConverter nc; 187 188 UnderscoreBinding(NameConverter nc) { 189 this.nc = nc; 190 } 191 } 192 193 /** 194 * Returns true if the "isJavaNamingConventionEnabled" option is turned on. 195 * 196 * In this mode, the compiler is expected to apply XML-to-Java name 197 * conversion algorithm even to names given by customizations. 198 * 199 * This method is intended to be called by other BIXXX classes. 200 * The effect of this switch should be hidden inside this package. 201 * IOW, the reader.xmlschema package shouldn't be aware of this switch. 202 */ 203 @XmlAttribute(name="enableJavaNamingConventions") 204 /*package*/ boolean isJavaNamingConventionEnabled = true; 205 206 /** 207 * True to generate classes for every simple type. 208 */ 209 @XmlAttribute(name="mapSimpleTypeDef") 210 boolean simpleTypeSubstitution = false; 211 212 /** 213 * Gets the default defaultProperty customization. 214 */ 215 @XmlTransient 216 private BIProperty defaultProperty; 217 218 /* 219 Three properties used to construct a default property 220 */ 221 @XmlAttribute 222 private boolean fixedAttributeAsConstantProperty = false; 223 @XmlAttribute 224 private CollectionTypeAttribute collectionType = new CollectionTypeAttribute(); 225 @XmlAttribute 226 void setGenerateIsSetMethod(boolean b) { 227 optionalProperty = b ? OptionalPropertyMode.ISSET : OptionalPropertyMode.WRAPPER; 228 } 229 230 231 /** 232 * Returns true if the compiler needs to generate type-safe enum 233 * member names when enumeration values cannot be used as constant names. 234 */ 235 @XmlAttribute(name="typesafeEnumMemberName") 236 EnumMemberMode generateEnumMemberName = EnumMemberMode.SKIP; 237 238 /** 239 * The code generation strategy. 240 */ 241 @XmlAttribute(name="generateValueClass") 242 ImplStructureStrategy codeGenerationStrategy = BEAN_ONLY; 243 244 /** 245 * Set of datatype names. For a type-safe enum class 246 * to be generated, the underlying XML datatype must be derived from 247 * one of the types in this set. 248 */ 249 // default value is set in the post-init action 250 @XmlAttribute(name="typesafeEnumBase") 251 private Set<QName> enumBaseTypes; 252 253 /** 254 * Returns {@link BISerializable} if the extension is specified, 255 * or null otherwise. 256 */ 257 @XmlElement 258 private BISerializable serializable = null; 259 260 /** 261 * If <xjc:superClass> extension is specified, 262 * returns the specified root class. Otherwise null. 263 */ 264 @XmlElement(namespace=Const.XJC_EXTENSION_URI) 265 ClassNameBean superClass = null; 266 267 /** 268 * If <xjc:superInterface> extension is specified, 269 * returns the specified root class. Otherwise null. 270 */ 271 @XmlElement(namespace=Const.XJC_EXTENSION_URI) 272 ClassNameBean superInterface = null; 273 274 /** 275 * Generate the simpler optimized code, but not necessarily 276 * conforming to the spec. 277 */ 278 @XmlElement(name="simple",namespace=Const.XJC_EXTENSION_URI) 279 String simpleMode = null; 280 281 /** 282 * Handles complex type restriction as if it were a new type. 283 */ 284 @XmlElement(name="treatRestrictionLikeNewType",namespace=Const.XJC_EXTENSION_URI) 285 String treatRestrictionLikeNewType = null; 286 287 /** 288 * True to generate a class for elements by default. 289 */ 290 @XmlAttribute 291 boolean generateElementClass = false; 292 293 @XmlAttribute 294 boolean generateMixedExtensions = false; 295 296 @XmlElement(namespace=Const.XJC_EXTENSION_URI) 297 Boolean generateElementProperty = null; 298 299 @XmlAttribute(name="generateElementProperty") // for JAXB unmarshaller 300 private void setGenerateElementPropertyStd(boolean value) { 301 generateElementProperty = value; 302 } 303 304 @XmlAttribute 305 boolean choiceContentProperty = false; 306 307 @XmlAttribute 308 OptionalPropertyMode optionalProperty = OptionalPropertyMode.WRAPPER; 309 310 /** 311 * Default cap to the number of constants in the enum. 312 * We won't attempt to produce a type-safe enum by default 313 * if there are more enumeration facets than specified in this field. 314 */ 315 @XmlAttribute(name="typesafeEnumMaxMembers") 316 int defaultEnumMemberSizeCap = 256; 317 318 /** 319 * If true, interfaces/classes that are normally generated as a nested interface/class 320 * will be generated into the package, allowing the generated classes to be flat. 321 * 322 * See <a href="http://monaco.sfbay/detail.jsf?cr=4969415">Bug 4969415</a> for the motivation. 323 */ 324 @XmlAttribute(name="localScoping") 325 LocalScoping flattenClasses = LocalScoping.NESTED; 326 327 /** 328 * Globally-defined conversion customizations. 329 * 330 * @see #setGlobalConversions 331 */ 332 @XmlTransient 333 private final Map<QName,BIConversion> globalConversions = new HashMap<QName, BIConversion>(); 334 335 // method for JAXB unmarshaller 336 @XmlElement(name="javaType") 337 private void setGlobalConversions(GlobalStandardConversion[] convs) { 338 for (GlobalStandardConversion u : convs) { 339 globalConversions.put(u.xmlType,u); 340 } 341 } 342 343 @XmlElement(name="javaType",namespace=Const.XJC_EXTENSION_URI) 344 private void setGlobalConversions2(GlobalVendorConversion[] convs) { 345 for (GlobalVendorConversion u : convs) { 346 globalConversions.put(u.xmlType,u); 347 } 348 } 349 350 // 351 // these customizations were valid in 1.0, but in 2.0 we don't 352 // use them. OTOH, we don't want to issue an error for them, 353 // so we just define a mapping and ignore the value. 354 // 355 @XmlElement(namespace=Const.XJC_EXTENSION_URI) 356 String noMarshaller = null; 357 @XmlElement(namespace=Const.XJC_EXTENSION_URI) 358 String noUnmarshaller = null; 359 @XmlElement(namespace=Const.XJC_EXTENSION_URI) 360 String noValidator = null; 361 @XmlElement(namespace=Const.XJC_EXTENSION_URI) 362 String noValidatingUnmarshaller = null; 363 @XmlElement(namespace=Const.XJC_EXTENSION_URI) 364 TypeSubstitutionElement typeSubstitution = null; 365 366 /** 367 * Another 1.0 compatibility customization (but we accept it 368 * and treat it as {@link #serializable}) 369 */ 370 @XmlElement(name="serializable",namespace=Const.XJC_EXTENSION_URI) 371 void setXjcSerializable(BISerializable s) { 372 this.serializable = s; 373 } 374 375 376 377 private static final class TypeSubstitutionElement { 378 @XmlAttribute 379 String type; 380 } 381 382 public void onSetOwner() { 383 super.onSetOwner(); 384 // if one is given by options, use that 385 NameConverter nc = Ring.get(Model.class).options.getNameConverter(); 386 if(nc!=null) 387 nameConverter = nc; 388 } 389 390 /** 391 * Creates a bind info object with the default values 392 */ 393 public BIGlobalBinding() { 394 } 395 396 public void setParent(BindInfo parent) { 397 super.setParent(parent); 398 // fill in the remaining default values 399 if(enumBaseTypes==null) 400 enumBaseTypes = Collections.singleton(new QName(WellKnownNamespace.XML_SCHEMA,"string")); 401 402 this.defaultProperty = new BIProperty(getLocation(),null,null,null, 403 collectionType, fixedAttributeAsConstantProperty, optionalProperty, generateElementProperty ); 404 defaultProperty.setParent(parent); // don't forget to initialize the defaultProperty 405 } 406 407 /** 408 * Moves global BIConversion to the right object. 409 */ 410 public void dispatchGlobalConversions( XSSchemaSet schema ) { 411 // also set parent to the global conversions 412 for( Map.Entry<QName,BIConversion> e : globalConversions.entrySet() ) { 413 414 QName name = e.getKey(); 415 BIConversion conv = e.getValue(); 416 417 XSSimpleType st = schema.getSimpleType(name.getNamespaceURI(),name.getLocalPart()); 418 if(st==null) { 419 Ring.get(ErrorReceiver.class).error( 420 getLocation(), 421 Messages.ERR_UNDEFINED_SIMPLE_TYPE.format(name) 422 ); 423 continue; // abort 424 } 425 426 getBuilder().getOrCreateBindInfo(st).addDecl(conv); 427 } 428 } 429 430 431 /** 432 * Checks if the given XML Schema built-in type can be mapped to 433 * a type-safe enum class. 434 * 435 * @param typeName 436 */ 437 public boolean canBeMappedToTypeSafeEnum( QName typeName ) { 438 return enumBaseTypes.contains(typeName); 439 } 440 441 public boolean canBeMappedToTypeSafeEnum( String nsUri, String localName ) { 442 return canBeMappedToTypeSafeEnum(new QName(nsUri,localName)); 443 } 444 445 public boolean canBeMappedToTypeSafeEnum( XSDeclaration decl ) { 446 return canBeMappedToTypeSafeEnum( decl.getTargetNamespace(), decl.getName() ); 447 } 448 449 450 public QName getName() { return NAME; } 451 public static final QName NAME = new QName( 452 Const.JAXB_NSURI, "globalBindings" ); 453 454 455 /** 456 * Used to unmarshal 457 * <pre>{@code 458 * <[element] name="className" /> 459 * }</pre> 460 */ 461 static final class ClassNameBean { 462 @XmlAttribute(required=true) 463 String name; 464 465 /** 466 * Computed from {@link #name} on demand. 467 */ 468 @XmlTransient 469 JDefinedClass clazz; 470 471 JDefinedClass getClazz(ClassType t) { 472 if (clazz != null) return clazz; 473 try { 474 JCodeModel codeModel = Ring.get(JCodeModel.class); 475 clazz = codeModel._class(name, t); 476 clazz.hide(); 477 return clazz; 478 } catch (JClassAlreadyExistsException e) { 479 return e.getExistingClass(); 480 } 481 } 482 } 483 484 static final class ClassNameAdapter extends ReadOnlyAdapter<ClassNameBean,String> { 485 public String unmarshal(ClassNameBean bean) throws Exception { 486 return bean.name; 487 } 488 } 489 490 /** 491 * Global <jaxb:javaType>. 492 */ 493 static final class GlobalStandardConversion extends BIConversion.User { 494 @XmlAttribute 495 QName xmlType; 496 497 @Override 498 public boolean equals(Object obj) { 499 if(obj instanceof GlobalStandardConversion) { 500 return ((GlobalStandardConversion)obj).xmlType.equals(xmlType); 501 } 502 503 return false; 504 } 505 506 @Override 507 public int hashCode() { 508 int hash = 7; 509 hash = 73 * hash + (this.xmlType != null ? this.xmlType.hashCode() : 0); 510 return hash; 511 } 512 } 513 514 /** 515 * Global <xjc:javaType>. 516 */ 517 static final class GlobalVendorConversion extends BIConversion.UserAdapter { 518 @XmlAttribute 519 QName xmlType; 520 521 @Override 522 public boolean equals(Object obj) { 523 if(obj instanceof GlobalVendorConversion) { 524 return ((GlobalVendorConversion)obj).xmlType.equals(xmlType); 525 } 526 527 return false; 528 } 529 530 @Override 531 public int hashCode() { 532 int hash = 7; 533 hash = 73 * hash + (this.xmlType != null ? this.xmlType.hashCode() : 0); 534 return hash; 535 } 536 } 537 538 /* don't want to override equals to avoid overriding hashcode for this complex object, too */ 539 public boolean isEqual(BIGlobalBinding b) { 540 boolean equal = 541 this.isJavaNamingConventionEnabled == b.isJavaNamingConventionEnabled && 542 this.simpleTypeSubstitution == b.simpleTypeSubstitution && 543 this.fixedAttributeAsConstantProperty == b.fixedAttributeAsConstantProperty && 544 this.generateEnumMemberName == b.generateEnumMemberName && 545 this.codeGenerationStrategy == b.codeGenerationStrategy && 546 this.serializable == b.serializable && 547 this.superClass == b.superClass && 548 this.superInterface == b.superInterface && 549 this.generateElementClass == b.generateElementClass && 550 this.generateMixedExtensions == b.generateMixedExtensions && 551 this.generateElementProperty == b.generateElementProperty && 552 this.choiceContentProperty == b.choiceContentProperty && 553 this.optionalProperty == b.optionalProperty && 554 this.defaultEnumMemberSizeCap == b.defaultEnumMemberSizeCap && 555 this.flattenClasses == b.flattenClasses; 556 557 if (!equal) return false; 558 559 return isEqual(this.nameConverter, b.nameConverter) && 560 isEqual(this.noMarshaller, b.noMarshaller) && 561 isEqual(this.noUnmarshaller, b.noUnmarshaller) && 562 isEqual(this.noValidator, b.noValidator) && 563 isEqual(this.noValidatingUnmarshaller, b.noValidatingUnmarshaller) && 564 isEqual(this.typeSubstitution, b.typeSubstitution) && 565 isEqual(this.simpleMode, b.simpleMode) && 566 isEqual(this.enumBaseTypes, b.enumBaseTypes) && 567 isEqual(this.treatRestrictionLikeNewType, b.treatRestrictionLikeNewType) && 568 isEqual(this.globalConversions, b.globalConversions); 569 } 570 571 private boolean isEqual(Object a, Object b) { 572 if (a != null) { 573 return a.equals(b); 574 } 575 return (b == null); 576 } 577 }