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 }