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 import static com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder.getName; 28 29 import java.util.Set; 30 31 import javax.xml.namespace.QName; 32 33 import com.sun.codemodel.internal.JJavaName; 34 import com.sun.codemodel.internal.JPackage; 35 import com.sun.istack.internal.NotNull; 36 import com.sun.istack.internal.Nullable; 37 import com.sun.tools.internal.xjc.ErrorReceiver; 38 import com.sun.tools.internal.xjc.model.CClassInfo; 39 import com.sun.tools.internal.xjc.model.CClassInfoParent; 40 import com.sun.tools.internal.xjc.model.CClassRef; 41 import com.sun.tools.internal.xjc.model.CCustomizations; 42 import com.sun.tools.internal.xjc.model.CElement; 43 import com.sun.tools.internal.xjc.model.CElementInfo; 44 import com.sun.tools.internal.xjc.model.Model; 45 import com.sun.tools.internal.xjc.reader.Ring; 46 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIClass; 47 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIGlobalBinding; 48 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BISchemaBinding; 49 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo; 50 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIXSubstitutable; 51 import com.sun.tools.internal.xjc.reader.xmlschema.ct.ComplexTypeFieldBuilder; 52 import com.sun.tools.internal.xjc.reader.xmlschema.ct.ComplexTypeBindingMode; 53 import com.sun.xml.internal.xsom.XSAnnotation; 54 import com.sun.xml.internal.xsom.XSAttGroupDecl; 55 import com.sun.xml.internal.xsom.XSAttributeDecl; 56 import com.sun.xml.internal.xsom.XSAttributeUse; 57 import com.sun.xml.internal.xsom.XSComplexType; 58 import com.sun.xml.internal.xsom.XSComponent; 59 import com.sun.xml.internal.xsom.XSContentType; 60 import com.sun.xml.internal.xsom.XSDeclaration; 61 import com.sun.xml.internal.xsom.XSElementDecl; 62 import com.sun.xml.internal.xsom.XSFacet; 63 import com.sun.xml.internal.xsom.XSIdentityConstraint; 64 import com.sun.xml.internal.xsom.XSModelGroup; 65 import com.sun.xml.internal.xsom.XSModelGroupDecl; 66 import com.sun.xml.internal.xsom.XSNotation; 67 import com.sun.xml.internal.xsom.XSParticle; 68 import com.sun.xml.internal.xsom.XSSchema; 69 import com.sun.xml.internal.xsom.XSSchemaSet; 70 import com.sun.xml.internal.xsom.XSSimpleType; 71 import com.sun.xml.internal.xsom.XSType; 72 import com.sun.xml.internal.xsom.XSWildcard; 73 import com.sun.xml.internal.xsom.XSXPath; 74 75 import org.xml.sax.Locator; 76 77 /** 78 * Default classBinder implementation. Honors {@code <jaxb:class>} customizations 79 * and default bindings. 80 */ 81 final class DefaultClassBinder implements ClassBinder 82 { 83 private final SimpleTypeBuilder stb = Ring.get(SimpleTypeBuilder.class); 84 private final Model model = Ring.get(Model.class); 85 86 protected final BGMBuilder builder = Ring.get(BGMBuilder.class); 87 protected final ClassSelector selector = Ring.get(ClassSelector.class); 88 89 protected final XSSchemaSet schemas = Ring.get(XSSchemaSet.class); 90 91 public CElement attGroupDecl(XSAttGroupDecl decl) { 92 return allow(decl,decl.getName()); 93 } 94 95 public CElement attributeDecl(XSAttributeDecl decl) { 96 return allow(decl,decl.getName()); 97 } 98 99 public CElement modelGroup(XSModelGroup mgroup) { 100 return never(); 101 } 102 103 public CElement modelGroupDecl(XSModelGroupDecl decl) { 104 return never(); 105 } 106 107 108 public CElement complexType(XSComplexType type) { 109 CElement ci = allow(type,type.getName()); 110 if(ci!=null) return ci; 111 112 // no customization is given -- do as the default binding. 113 114 BindInfo bi = builder.getBindInfo(type); 115 116 if(type.isGlobal()) { 117 QName tagName = null; 118 String className = deriveName(type); 119 Locator loc = type.getLocator(); 120 121 if(getGlobalBinding().isSimpleMode()) { 122 // in the simple mode, we may optimize it away 123 XSElementDecl referer = getSoleElementReferer(type); 124 if(referer!=null && isCollapsable(referer)) { 125 // if a global element contains 126 // a collpsable complex type, we bind this element to a named one 127 // and collapses element and complex type. 128 tagName = getName(referer); 129 className = deriveName(referer); 130 loc = referer.getLocator(); 131 } 132 } 133 134 // by default, global ones get their own classes. 135 136 JPackage pkg = selector.getPackage(type.getTargetNamespace()); 137 138 return new CClassInfo(model,pkg,className, loc,getTypeName(type),tagName,type,bi.toCustomizationList()); 139 } else { 140 XSElementDecl element = type.getScope(); 141 142 if( element.isGlobal() && isCollapsable(element)) { 143 if(builder.getBindInfo(element).get(BIClass.class)!=null) 144 // the parent element was bound to a class. Don't bind this again to 145 // cause unnecessary wrapping 146 return null; 147 148 // generate one class from element and complex type together. 149 // this needs to be done before selector.isBound to avoid infinite recursion. 150 151 // but avoid doing so when the element is mapped to a class, 152 // which creates unnecessary classes 153 return new CClassInfo( model, selector.getClassScope(), 154 deriveName(element), element.getLocator(), null, 155 getName(element), element, bi.toCustomizationList() ); 156 } 157 158 159 CElement parentType = selector.isBound(element,type); 160 161 String className; 162 CClassInfoParent scope; 163 164 165 if( parentType!=null 166 && parentType instanceof CElementInfo 167 && ((CElementInfo)parentType).hasClass() ) { 168 // special case where we put a nested 'Type' element 169 scope = (CElementInfo)parentType; 170 className = "Type"; 171 } else { 172 // since the parent element isn't bound to a type, merge the customizations associated to it, too. 173 // custs = CCustomizations.merge( custs, builder.getBindInfo(type.getScope()).toCustomizationList()); 174 className = builder.getNameConverter().toClassName(element.getName()); 175 176 BISchemaBinding sb = builder.getBindInfo( 177 type.getOwnerSchema() ).get(BISchemaBinding.class); 178 if(sb!=null) className = sb.mangleAnonymousTypeClassName(className); 179 scope = selector.getClassScope(); 180 } 181 182 return new CClassInfo(model, scope, className, type.getLocator(), null, null, type, bi.toCustomizationList() ); 183 } 184 } 185 186 private QName getTypeName(XSComplexType type) { 187 if(type.getRedefinedBy()!=null) 188 return null; 189 else 190 return getName(type); 191 } 192 193 /** 194 * Returns true if the complex type of the given element can be "optimized away" 195 * and unified with its parent element decl to form a single class. 196 */ 197 private boolean isCollapsable(XSElementDecl decl) { 198 XSType type = decl.getType(); 199 200 if(!type.isComplexType()) 201 return false; // not a complex type 202 203 if(decl.getSubstitutables().size()>1 || decl.getSubstAffiliation()!=null) 204 // because element substitution calls for a proper JAXBElement hierarchy 205 return false; 206 207 if(decl.isNillable()) 208 // because nillable needs JAXBElement to represent correctly 209 return false; 210 211 BIXSubstitutable bixSubstitutable = builder.getBindInfo(decl).get(BIXSubstitutable.class); 212 if(bixSubstitutable !=null) { 213 // see https://jaxb.dev.java.net/issues/show_bug.cgi?id=289 214 // this customization forces non-collapsing behavior. 215 bixSubstitutable.markAsAcknowledged(); 216 return false; 217 } 218 219 if( getGlobalBinding().isSimpleMode() && decl.isGlobal()) { 220 // in the simple mode, we do more aggressive optimization, and get rid of 221 // a complex type class if it's only used once from a global element 222 XSElementDecl referer = getSoleElementReferer(decl.getType()); 223 if(referer!=null) { 224 assert referer==decl; // I must be the sole referer 225 return true; 226 } 227 } 228 229 if(!type.isLocal() || !type.isComplexType()) 230 return false; 231 232 return true; 233 } 234 235 /** 236 * If only one global {@link XSElementDecl} is refering to {@link XSType}, 237 * return that element, otherwise null. 238 */ 239 private @Nullable XSElementDecl getSoleElementReferer(@NotNull XSType t) { 240 Set<XSComponent> referer = builder.getReferer(t); 241 242 XSElementDecl sole = null; 243 for (XSComponent r : referer) { 244 if(r instanceof XSElementDecl) { 245 XSElementDecl x = (XSElementDecl) r; 246 if(!x.isGlobal()) 247 // local element references can be ignored, as their names are either given 248 // by the property, or by the JAXBElement (for things like mixed contents) 249 continue; 250 if(sole==null) sole=x; 251 else return null; // more than one 252 } else { 253 // if another type refers to this type, that means 254 // this type has a sub-type, so type substitution is possible now. 255 return null; 256 } 257 } 258 259 return sole; 260 } 261 262 public CElement elementDecl(XSElementDecl decl) { 263 CElement r = allow(decl,decl.getName()); 264 265 if(r==null) { 266 QName tagName = getName(decl); 267 CCustomizations custs = builder.getBindInfo(decl).toCustomizationList(); 268 269 if(decl.isGlobal()) { 270 if(isCollapsable(decl)) { 271 // we want the returned type to be built as a complex type, 272 // so the binding cannot be delayed. 273 return selector.bindToType(decl.getType().asComplexType(),decl,true); 274 } else { 275 String className = null; 276 if(getGlobalBinding().isGenerateElementClass()) 277 className = deriveName(decl); 278 279 // otherwise map global elements to JAXBElement 280 CElementInfo cei = new CElementInfo( 281 model, tagName, selector.getClassScope(), className, custs, decl.getLocator()); 282 selector.boundElements.put(decl,cei); 283 284 stb.refererStack.push(decl); // referer is element 285 cei.initContentType( selector.bindToType(decl.getType(),decl), decl, decl.getDefaultValue() ); 286 stb.refererStack.pop(); 287 r = cei; 288 } 289 } 290 } 291 292 // have the substitution member derive from the substitution head 293 XSElementDecl top = decl.getSubstAffiliation(); 294 if(top!=null) { 295 CElement topci = selector.bindToType(top,decl); 296 297 if(r instanceof CClassInfo && topci instanceof CClassInfo) 298 ((CClassInfo)r).setBaseClass((CClassInfo)topci); 299 if (r instanceof CElementInfo && topci instanceof CElementInfo) 300 ((CElementInfo)r).setSubstitutionHead((CElementInfo)topci); 301 } 302 303 return r; 304 } 305 306 public CClassInfo empty( XSContentType ct ) { return null; } 307 308 public CClassInfo identityConstraint(XSIdentityConstraint xsIdentityConstraint) { 309 return never(); 310 } 311 312 public CClassInfo xpath(XSXPath xsxPath) { 313 return never(); 314 } 315 316 public CClassInfo attributeUse(XSAttributeUse use) { 317 return never(); 318 } 319 320 public CElement simpleType(XSSimpleType type) { 321 CElement c = allow(type,type.getName()); 322 if(c!=null) return c; 323 324 if(getGlobalBinding().isSimpleTypeSubstitution() && type.isGlobal()) { 325 return new CClassInfo(model,selector.getClassScope(), 326 deriveName(type), type.getLocator(), getName(type), null, type, null ); 327 } 328 329 return never(); 330 } 331 332 public CClassInfo particle(XSParticle particle) { 333 return never(); 334 } 335 336 public CClassInfo wildcard(XSWildcard wc) { 337 return never(); 338 } 339 340 341 // these methods won't be used 342 public CClassInfo annotation(XSAnnotation annon) { 343 assert false; 344 return null; 345 } 346 347 public CClassInfo notation(XSNotation not) { 348 assert false; 349 return null; 350 } 351 352 public CClassInfo facet(XSFacet decl) { 353 assert false; 354 return null; 355 } 356 public CClassInfo schema(XSSchema schema) { 357 assert false; 358 return null; 359 } 360 361 362 363 364 365 /** 366 * Makes sure that the component doesn't carry a {@link BIClass} 367 * customization. 368 * 369 * @return 370 * return value is unused. Since most of the caller needs to 371 * return null, to make the code a little bit shorter, this 372 * method always return null (so that the caller can always 373 * say <code>return never(sc);</code>. 374 */ 375 private CClassInfo never() { 376 // all we need to do here is just not to acknowledge 377 // any class customization. Then this class customization 378 // will be reported as an error later when we check all 379 // unacknowledged customizations. 380 381 382 // BIDeclaration cust=owner.getBindInfo(component).get(BIClass.NAME); 383 // if(cust!=null) { 384 // // error 385 // owner.errorReporter.error( 386 // cust.getLocation(), 387 // "test {0}", NameGetter.get(component) ); 388 // } 389 return null; 390 } 391 392 /** 393 * Checks if a component carries a customization to map it to a class. 394 * If so, make it a class. 395 * 396 * @param defaultBaseName 397 * The token which will be used as the basis of the class name 398 * if the class name is not specified in the customization. 399 * This is usually the name of an element declaration, and so on. 400 * 401 * This parameter can be null, in that case it would be an error 402 * if a name is not given by the customization. 403 */ 404 private CElement allow( XSComponent component, String defaultBaseName ) { 405 406 BIClass decl = null; 407 408 if(component instanceof XSComplexType) { 409 XSType complexType = (XSType)component; 410 411 BIClass lastFoundRecursiveBiClass = null; 412 413 if(complexType.getName() != null) { 414 while( ! schemas.getAnyType().equals(complexType)) { 415 BindInfo bindInfo = builder.getBindInfo(complexType); 416 BIClass biClass = bindInfo.get(BIClass.class); 417 418 if(biClass != null && "true".equals(biClass.getRecursive())) 419 lastFoundRecursiveBiClass = biClass; 420 421 complexType = complexType.getBaseType(); 422 } 423 } 424 425 // use this as biclass for current component 426 decl = lastFoundRecursiveBiClass; 427 428 } 429 430 BindInfo bindInfo = builder.getBindInfo(component); 431 if(decl == null) { 432 decl = bindInfo.get(BIClass.class); 433 if(decl==null) return null; 434 } 435 436 decl.markAsAcknowledged(); 437 438 // first consider binding to the class reference. 439 String ref = decl.getExistingClassRef(); 440 if(ref!=null) { 441 if(!JJavaName.isFullyQualifiedClassName(ref)) { 442 Ring.get(ErrorReceiver.class).error( decl.getLocation(), 443 Messages.format(Messages.ERR_INCORRECT_CLASS_NAME,ref) ); 444 // recover by ignoring @ref 445 } else { 446 if(component instanceof XSComplexType) { 447 // UGLY UGLY UGLY 448 // since we are not going to bind this complex type, we need to figure out 449 // its binding mode without actually binding it (and also expose this otherwise 450 // hidden mechanism into this part of the code.) 451 // 452 // this code is potentially dangerous as the base class might have been bound 453 // in different ways. To be correct, we need to figure out how the content type 454 // would have been bound, from the schema. 455 Ring.get(ComplexTypeFieldBuilder.class).recordBindingMode( 456 (XSComplexType)component, ComplexTypeBindingMode.NORMAL 457 ); 458 } 459 return new CClassRef(model, component, decl, bindInfo.toCustomizationList() ); 460 } 461 } 462 463 String clsName = decl.getClassName(); 464 if(clsName==null) { 465 // if the customiztion doesn't give us a name, derive one 466 // from the current component. 467 if( defaultBaseName==null ) { 468 Ring.get(ErrorReceiver.class).error( decl.getLocation(), 469 Messages.format(Messages.ERR_CLASS_NAME_IS_REQUIRED) ); 470 471 // recover by generating a pseudo-random name 472 defaultBaseName = "undefined"+component.hashCode(); 473 } 474 clsName = builder.deriveName( defaultBaseName, component ); 475 } else { 476 if( !JJavaName.isJavaIdentifier(clsName) ) { 477 // not a valid Java class name 478 Ring.get(ErrorReceiver.class).error( decl.getLocation(), 479 Messages.format( Messages.ERR_INCORRECT_CLASS_NAME, clsName )); 480 // recover by a dummy name 481 clsName = "Undefined"+component.hashCode(); 482 } 483 } 484 485 QName typeName = null; 486 QName elementName = null; 487 488 if(component instanceof XSType) { 489 XSType t = (XSType) component; 490 typeName = getName(t); 491 } 492 493 if (component instanceof XSElementDecl) { 494 XSElementDecl e = (XSElementDecl) component; 495 elementName = getName(e); 496 } 497 498 if (component instanceof XSElementDecl && !isCollapsable((XSElementDecl)component)) { 499 XSElementDecl e = ((XSElementDecl)component); 500 501 CElementInfo cei = new CElementInfo(model, elementName, 502 selector.getClassScope(), clsName, 503 bindInfo.toCustomizationList(), decl.getLocation() ); 504 selector.boundElements.put(e,cei); 505 506 stb.refererStack.push(component); // referer is element 507 cei.initContentType( 508 selector.bindToType(e.getType(),e), 509 e,e.getDefaultValue()); 510 stb.refererStack.pop(); 511 return cei; 512 // TODO: support javadoc and userSpecifiedImplClass 513 } else { 514 CClassInfo bt = new CClassInfo(model,selector.getClassScope(), 515 clsName, decl.getLocation(), typeName, elementName, component, bindInfo.toCustomizationList() ); 516 517 // set javadoc class comment. 518 if(decl.getJavadoc()!=null ) 519 bt.javadoc = decl.getJavadoc()+"\n\n"; 520 // add extra blank lines so that the schema fragment 521 // and user-specified javadoc would be separated 522 523 524 // if the implClass is given, set it to ClassItem 525 String implClass = decl.getUserSpecifiedImplClass(); 526 if( implClass!=null ) 527 bt.setUserSpecifiedImplClass( implClass ); 528 529 return bt; 530 } 531 } 532 533 private BIGlobalBinding getGlobalBinding() { 534 return builder.getGlobalBinding(); 535 } 536 537 /** 538 * Derives a name from a schema component. 539 * Use the name of the schema component as the default name. 540 */ 541 private String deriveName( XSDeclaration comp ) { 542 return builder.deriveName( comp.getName(), comp ); 543 } 544 545 /** 546 * Derives a name from a schema component. 547 * For complex types, we take redefinition into account when 548 * deriving a default name. 549 */ 550 private String deriveName( XSComplexType comp ) { 551 String seed = builder.deriveName( comp.getName(), comp ); 552 int cnt = comp.getRedefinedCount(); 553 for( ; cnt>0; cnt-- ) 554 seed = "Original"+seed; 555 return seed; 556 } 557 558 }