325N/A/*
325N/A * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
325N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
325N/A *
325N/A * This code is free software; you can redistribute it and/or modify it
325N/A * under the terms of the GNU General Public License version 2 only, as
325N/A * published by the Free Software Foundation. Oracle designates this
325N/A * particular file as subject to the "Classpath" exception as provided
325N/A * by Oracle in the LICENSE file that accompanied this code.
325N/A *
325N/A * This code is distributed in the hope that it will be useful, but WITHOUT
325N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
325N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
325N/A * version 2 for more details (a copy is included in the LICENSE file that
325N/A * accompanied this code).
325N/A *
325N/A * You should have received a copy of the GNU General Public License version
325N/A * 2 along with this work; if not, write to the Free Software Foundation,
325N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
325N/A *
325N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
325N/A * or visit www.oracle.com if you need additional information or have any
325N/A * questions.
325N/A */
325N/A
325N/Apackage com.sun.tools.internal.xjc.reader.xmlschema;
325N/Aimport static com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder.getName;
325N/A
325N/Aimport java.util.Set;
325N/A
325N/Aimport javax.xml.namespace.QName;
325N/A
325N/Aimport com.sun.codemodel.internal.JJavaName;
325N/Aimport com.sun.codemodel.internal.JPackage;
325N/Aimport com.sun.istack.internal.NotNull;
325N/Aimport com.sun.istack.internal.Nullable;
325N/Aimport com.sun.tools.internal.xjc.ErrorReceiver;
325N/Aimport com.sun.tools.internal.xjc.model.CClassInfo;
325N/Aimport com.sun.tools.internal.xjc.model.CClassInfoParent;
325N/Aimport com.sun.tools.internal.xjc.model.CClassRef;
325N/Aimport com.sun.tools.internal.xjc.model.CCustomizations;
325N/Aimport com.sun.tools.internal.xjc.model.CElement;
325N/Aimport com.sun.tools.internal.xjc.model.CElementInfo;
325N/Aimport com.sun.tools.internal.xjc.model.Model;
325N/Aimport com.sun.tools.internal.xjc.reader.Ring;
325N/Aimport com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIClass;
325N/Aimport com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIGlobalBinding;
325N/Aimport com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BISchemaBinding;
325N/Aimport com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo;
325N/Aimport com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIXSubstitutable;
325N/Aimport com.sun.tools.internal.xjc.reader.xmlschema.ct.ComplexTypeFieldBuilder;
325N/Aimport com.sun.tools.internal.xjc.reader.xmlschema.ct.ComplexTypeBindingMode;
325N/Aimport com.sun.xml.internal.xsom.XSAnnotation;
325N/Aimport com.sun.xml.internal.xsom.XSAttGroupDecl;
325N/Aimport com.sun.xml.internal.xsom.XSAttributeDecl;
325N/Aimport com.sun.xml.internal.xsom.XSAttributeUse;
325N/Aimport com.sun.xml.internal.xsom.XSComplexType;
325N/Aimport com.sun.xml.internal.xsom.XSComponent;
325N/Aimport com.sun.xml.internal.xsom.XSContentType;
325N/Aimport com.sun.xml.internal.xsom.XSDeclaration;
325N/Aimport com.sun.xml.internal.xsom.XSElementDecl;
325N/Aimport com.sun.xml.internal.xsom.XSFacet;
325N/Aimport com.sun.xml.internal.xsom.XSIdentityConstraint;
325N/Aimport com.sun.xml.internal.xsom.XSModelGroup;
325N/Aimport com.sun.xml.internal.xsom.XSModelGroupDecl;
325N/Aimport com.sun.xml.internal.xsom.XSNotation;
325N/Aimport com.sun.xml.internal.xsom.XSParticle;
325N/Aimport com.sun.xml.internal.xsom.XSSchema;
325N/Aimport com.sun.xml.internal.xsom.XSSchemaSet;
325N/Aimport com.sun.xml.internal.xsom.XSSimpleType;
325N/Aimport com.sun.xml.internal.xsom.XSType;
325N/Aimport com.sun.xml.internal.xsom.XSWildcard;
325N/Aimport com.sun.xml.internal.xsom.XSXPath;
325N/A
325N/Aimport org.xml.sax.Locator;
325N/A
325N/A/**
325N/A * Default classBinder implementation. Honors <jaxb:class> customizations
325N/A * and default bindings.
325N/A */
325N/Afinal class DefaultClassBinder implements ClassBinder
325N/A{
325N/A private final SimpleTypeBuilder stb = Ring.get(SimpleTypeBuilder.class);
325N/A private final Model model = Ring.get(Model.class);
325N/A
325N/A protected final BGMBuilder builder = Ring.get(BGMBuilder.class);
325N/A protected final ClassSelector selector = Ring.get(ClassSelector.class);
325N/A
325N/A protected final XSSchemaSet schemas = Ring.get(XSSchemaSet.class);
325N/A
325N/A public CElement attGroupDecl(XSAttGroupDecl decl) {
325N/A return allow(decl,decl.getName());
325N/A }
325N/A
325N/A public CElement attributeDecl(XSAttributeDecl decl) {
325N/A return allow(decl,decl.getName());
325N/A }
325N/A
325N/A public CElement modelGroup(XSModelGroup mgroup) {
325N/A return never();
325N/A }
325N/A
325N/A public CElement modelGroupDecl(XSModelGroupDecl decl) {
325N/A return never();
325N/A }
325N/A
325N/A
325N/A public CElement complexType(XSComplexType type) {
325N/A CElement ci = allow(type,type.getName());
325N/A if(ci!=null) return ci;
325N/A
325N/A // no customization is given -- do as the default binding.
325N/A
325N/A BindInfo bi = builder.getBindInfo(type);
325N/A
325N/A if(type.isGlobal()) {
325N/A QName tagName = null;
325N/A String className = deriveName(type);
325N/A Locator loc = type.getLocator();
325N/A
325N/A if(getGlobalBinding().isSimpleMode()) {
325N/A // in the simple mode, we may optimize it away
325N/A XSElementDecl referer = getSoleElementReferer(type);
325N/A if(referer!=null && isCollapsable(referer)) {
325N/A // if a global element contains
325N/A // a collpsable complex type, we bind this element to a named one
325N/A // and collapses element and complex type.
325N/A tagName = getName(referer);
325N/A className = deriveName(referer);
325N/A loc = referer.getLocator();
325N/A }
325N/A }
325N/A
325N/A // by default, global ones get their own classes.
325N/A
325N/A JPackage pkg = selector.getPackage(type.getTargetNamespace());
325N/A
325N/A return new CClassInfo(model,pkg,className, loc,getTypeName(type),tagName,type,bi.toCustomizationList());
325N/A } else {
325N/A XSElementDecl element = type.getScope();
325N/A
325N/A if( element.isGlobal() && isCollapsable(element)) {
325N/A if(builder.getBindInfo(element).get(BIClass.class)!=null)
325N/A // the parent element was bound to a class. Don't bind this again to
325N/A // cause unnecessary wrapping
325N/A return null;
325N/A
325N/A // generate one class from element and complex type together.
325N/A // this needs to be done before selector.isBound to avoid infinite recursion.
325N/A
325N/A // but avoid doing so when the element is mapped to a class,
325N/A // which creates unnecessary classes
325N/A return new CClassInfo( model, selector.getClassScope(),
325N/A deriveName(element), element.getLocator(), null,
325N/A getName(element), element, bi.toCustomizationList() );
325N/A }
325N/A
325N/A
325N/A CElement parentType = selector.isBound(element,type);
325N/A
325N/A String className;
325N/A CClassInfoParent scope;
325N/A
325N/A
325N/A if( parentType!=null
325N/A && parentType instanceof CElementInfo
325N/A && ((CElementInfo)parentType).hasClass() ) {
325N/A // special case where we put a nested 'Type' element
325N/A scope = (CElementInfo)parentType;
325N/A className = "Type";
325N/A } else {
325N/A // since the parent element isn't bound to a type, merge the customizations associated to it, too.
325N/A// custs = CCustomizations.merge( custs, builder.getBindInfo(type.getScope()).toCustomizationList());
325N/A className = builder.getNameConverter().toClassName(element.getName());
325N/A
325N/A BISchemaBinding sb = builder.getBindInfo(
325N/A type.getOwnerSchema() ).get(BISchemaBinding.class);
325N/A if(sb!=null) className = sb.mangleAnonymousTypeClassName(className);
325N/A scope = selector.getClassScope();
325N/A }
325N/A
325N/A return new CClassInfo(model, scope, className, type.getLocator(), null, null, type, bi.toCustomizationList() );
325N/A }
325N/A }
325N/A
325N/A private QName getTypeName(XSComplexType type) {
325N/A if(type.getRedefinedBy()!=null)
325N/A return null;
325N/A else
325N/A return getName(type);
325N/A }
325N/A
325N/A /**
325N/A * Returns true if the complex type of the given element can be "optimized away"
325N/A * and unified with its parent element decl to form a single class.
325N/A */
325N/A private boolean isCollapsable(XSElementDecl decl) {
325N/A XSType type = decl.getType();
325N/A
325N/A if(!type.isComplexType())
325N/A return false; // not a complex type
325N/A
325N/A if(decl.getSubstitutables().size()>1 || decl.getSubstAffiliation()!=null)
325N/A // because element substitution calls for a proper JAXBElement hierarchy
325N/A return false;
325N/A
325N/A if(decl.isNillable())
325N/A // because nillable needs JAXBElement to represent correctly
325N/A return false;
325N/A
325N/A BIXSubstitutable bixSubstitutable = builder.getBindInfo(decl).get(BIXSubstitutable.class);
325N/A if(bixSubstitutable !=null) {
325N/A // see https://jaxb.dev.java.net/issues/show_bug.cgi?id=289
325N/A // this customization forces non-collapsing behavior.
325N/A bixSubstitutable.markAsAcknowledged();
325N/A return false;
325N/A }
325N/A
325N/A if( getGlobalBinding().isSimpleMode() && decl.isGlobal()) {
325N/A // in the simple mode, we do more aggressive optimization, and get rid of
325N/A // a complex type class if it's only used once from a global element
325N/A XSElementDecl referer = getSoleElementReferer(decl.getType());
325N/A if(referer!=null) {
325N/A assert referer==decl; // I must be the sole referer
325N/A return true;
325N/A }
325N/A }
325N/A
325N/A if(!type.isLocal() || !type.isComplexType())
325N/A return false;
325N/A
325N/A return true;
325N/A }
325N/A
325N/A /**
325N/A * If only one global {@link XSElementDecl} is refering to {@link XSType},
325N/A * return that element, otherwise null.
325N/A */
325N/A private @Nullable XSElementDecl getSoleElementReferer(@NotNull XSType t) {
325N/A Set<XSComponent> referer = builder.getReferer(t);
325N/A
325N/A XSElementDecl sole = null;
325N/A for (XSComponent r : referer) {
325N/A if(r instanceof XSElementDecl) {
325N/A XSElementDecl x = (XSElementDecl) r;
325N/A if(!x.isGlobal())
325N/A // local element references can be ignored, as their names are either given
325N/A // by the property, or by the JAXBElement (for things like mixed contents)
325N/A continue;
325N/A if(sole==null) sole=x;
325N/A else return null; // more than one
325N/A } else {
325N/A // if another type refers to this type, that means
325N/A // this type has a sub-type, so type substitution is possible now.
325N/A return null;
325N/A }
325N/A }
325N/A
325N/A return sole;
325N/A }
325N/A
325N/A public CElement elementDecl(XSElementDecl decl) {
325N/A CElement r = allow(decl,decl.getName());
325N/A
325N/A if(r==null) {
325N/A QName tagName = getName(decl);
325N/A CCustomizations custs = builder.getBindInfo(decl).toCustomizationList();
325N/A
325N/A if(decl.isGlobal()) {
325N/A if(isCollapsable(decl)) {
325N/A // we want the returned type to be built as a complex type,
325N/A // so the binding cannot be delayed.
325N/A return selector.bindToType(decl.getType().asComplexType(),decl,true);
325N/A } else {
325N/A String className = null;
325N/A if(getGlobalBinding().isGenerateElementClass())
325N/A className = deriveName(decl);
325N/A
325N/A // otherwise map global elements to JAXBElement
325N/A CElementInfo cei = new CElementInfo(
325N/A model, tagName, selector.getClassScope(), className, custs, decl.getLocator());
325N/A selector.boundElements.put(decl,cei);
325N/A
325N/A stb.refererStack.push(decl); // referer is element
325N/A cei.initContentType( selector.bindToType(decl.getType(),decl), decl, decl.getDefaultValue() );
325N/A stb.refererStack.pop();
325N/A r = cei;
325N/A }
325N/A }
325N/A }
325N/A
325N/A // have the substitution member derive from the substitution head
325N/A XSElementDecl top = decl.getSubstAffiliation();
325N/A if(top!=null) {
325N/A CElement topci = selector.bindToType(top,decl);
325N/A
325N/A if(r instanceof CClassInfo && topci instanceof CClassInfo)
325N/A ((CClassInfo)r).setBaseClass((CClassInfo)topci);
325N/A if (r instanceof CElementInfo && topci instanceof CElementInfo)
325N/A ((CElementInfo)r).setSubstitutionHead((CElementInfo)topci);
325N/A }
325N/A
325N/A return r;
325N/A }
325N/A
325N/A public CClassInfo empty( XSContentType ct ) { return null; }
325N/A
325N/A public CClassInfo identityConstraint(XSIdentityConstraint xsIdentityConstraint) {
325N/A return never();
325N/A }
325N/A
325N/A public CClassInfo xpath(XSXPath xsxPath) {
325N/A return never();
325N/A }
325N/A
325N/A public CClassInfo attributeUse(XSAttributeUse use) {
325N/A return never();
325N/A }
325N/A
325N/A public CElement simpleType(XSSimpleType type) {
325N/A CElement c = allow(type,type.getName());
325N/A if(c!=null) return c;
325N/A
325N/A if(getGlobalBinding().isSimpleTypeSubstitution() && type.isGlobal()) {
325N/A return new CClassInfo(model,selector.getClassScope(),
325N/A deriveName(type), type.getLocator(), getName(type), null, type, null );
325N/A }
325N/A
325N/A return never();
325N/A }
325N/A
325N/A public CClassInfo particle(XSParticle particle) {
325N/A return never();
325N/A }
325N/A
325N/A public CClassInfo wildcard(XSWildcard wc) {
325N/A return never();
325N/A }
325N/A
325N/A
325N/A // these methods won't be used
325N/A public CClassInfo annotation(XSAnnotation annon) {
325N/A assert false;
325N/A return null;
325N/A }
325N/A
325N/A public CClassInfo notation(XSNotation not) {
325N/A assert false;
325N/A return null;
325N/A }
325N/A
325N/A public CClassInfo facet(XSFacet decl) {
325N/A assert false;
325N/A return null;
325N/A }
325N/A public CClassInfo schema(XSSchema schema) {
325N/A assert false;
325N/A return null;
325N/A }
325N/A
325N/A
325N/A
325N/A
325N/A
325N/A /**
325N/A * Makes sure that the component doesn't carry a {@link BIClass}
325N/A * customization.
325N/A *
325N/A * @return
325N/A * return value is unused. Since most of the caller needs to
325N/A * return null, to make the code a little bit shorter, this
325N/A * method always return null (so that the caller can always
325N/A * say <code>return never(sc);</code>.
325N/A */
325N/A private CClassInfo never() {
325N/A // all we need to do here is just not to acknowledge
325N/A // any class customization. Then this class customization
325N/A // will be reported as an error later when we check all
325N/A // unacknowledged customizations.
325N/A
325N/A
325N/A// BIDeclaration cust=owner.getBindInfo(component).get(BIClass.NAME);
325N/A// if(cust!=null) {
325N/A// // error
325N/A// owner.errorReporter.error(
325N/A// cust.getLocation(),
325N/A// "test {0}", NameGetter.get(component) );
325N/A// }
325N/A return null;
325N/A }
325N/A
325N/A /**
325N/A * Checks if a component carries a customization to map it to a class.
325N/A * If so, make it a class.
325N/A *
325N/A * @param defaultBaseName
325N/A * The token which will be used as the basis of the class name
325N/A * if the class name is not specified in the customization.
325N/A * This is usually the name of an element declaration, and so on.
325N/A *
325N/A * This parameter can be null, in that case it would be an error
325N/A * if a name is not given by the customization.
325N/A */
325N/A private CElement allow( XSComponent component, String defaultBaseName ) {
325N/A
325N/A BIClass decl = null;
325N/A
325N/A if(component instanceof XSComplexType) {
325N/A XSType complexType = (XSType)component;
325N/A
325N/A BIClass lastFoundRecursiveBiClass = null;
325N/A
325N/A if(complexType.getName() != null) {
325N/A while( ! schemas.getAnyType().equals(complexType)) {
325N/A BindInfo bindInfo = builder.getBindInfo(complexType);
325N/A BIClass biClass = bindInfo.get(BIClass.class);
325N/A
325N/A if(biClass != null && "true".equals(biClass.getRecursive()))
325N/A lastFoundRecursiveBiClass = biClass;
325N/A
325N/A complexType = complexType.getBaseType();
325N/A }
325N/A }
325N/A
325N/A // use this as biclass for current component
325N/A decl = lastFoundRecursiveBiClass;
325N/A
325N/A }
325N/A
325N/A BindInfo bindInfo = builder.getBindInfo(component);
325N/A if(decl == null) {
325N/A decl = bindInfo.get(BIClass.class);
325N/A if(decl==null) return null;
325N/A }
325N/A
325N/A decl.markAsAcknowledged();
325N/A
325N/A // first consider binding to the class reference.
325N/A String ref = decl.getExistingClassRef();
325N/A if(ref!=null) {
325N/A if(!JJavaName.isFullyQualifiedClassName(ref)) {
325N/A Ring.get(ErrorReceiver.class).error( decl.getLocation(),
325N/A Messages.format(Messages.ERR_INCORRECT_CLASS_NAME,ref) );
325N/A // recover by ignoring @ref
325N/A } else {
325N/A if(component instanceof XSComplexType) {
325N/A // UGLY UGLY UGLY
325N/A // since we are not going to bind this complex type, we need to figure out
325N/A // its binding mode without actually binding it (and also expose this otherwise
325N/A // hidden mechanism into this part of the code.)
325N/A //
325N/A // this code is potentially dangerous as the base class might have been bound
325N/A // in different ways. To be correct, we need to figure out how the content type
325N/A // would have been bound, from the schema.
325N/A Ring.get(ComplexTypeFieldBuilder.class).recordBindingMode(
325N/A (XSComplexType)component, ComplexTypeBindingMode.NORMAL
325N/A );
325N/A }
325N/A return new CClassRef(model, component, decl, bindInfo.toCustomizationList() );
325N/A }
325N/A }
325N/A
325N/A String clsName = decl.getClassName();
325N/A if(clsName==null) {
325N/A // if the customiztion doesn't give us a name, derive one
325N/A // from the current component.
325N/A if( defaultBaseName==null ) {
325N/A Ring.get(ErrorReceiver.class).error( decl.getLocation(),
325N/A Messages.format(Messages.ERR_CLASS_NAME_IS_REQUIRED) );
325N/A
325N/A // recover by generating a pseudo-random name
325N/A defaultBaseName = "undefined"+component.hashCode();
325N/A }
325N/A clsName = builder.deriveName( defaultBaseName, component );
325N/A } else {
325N/A if( !JJavaName.isJavaIdentifier(clsName) ) {
325N/A // not a valid Java class name
325N/A Ring.get(ErrorReceiver.class).error( decl.getLocation(),
325N/A Messages.format( Messages.ERR_INCORRECT_CLASS_NAME, clsName ));
325N/A // recover by a dummy name
325N/A clsName = "Undefined"+component.hashCode();
325N/A }
325N/A }
325N/A
325N/A QName typeName = null;
325N/A QName elementName = null;
325N/A
325N/A if(component instanceof XSType) {
325N/A XSType t = (XSType) component;
325N/A typeName = getName(t);
325N/A }
325N/A
325N/A if (component instanceof XSElementDecl) {
325N/A XSElementDecl e = (XSElementDecl) component;
325N/A elementName = getName(e);
325N/A }
325N/A
325N/A if (component instanceof XSElementDecl && !isCollapsable((XSElementDecl)component)) {
325N/A XSElementDecl e = ((XSElementDecl)component);
325N/A
325N/A CElementInfo cei = new CElementInfo(model, elementName,
325N/A selector.getClassScope(), clsName,
325N/A bindInfo.toCustomizationList(), decl.getLocation() );
325N/A selector.boundElements.put(e,cei);
325N/A
325N/A stb.refererStack.push(component); // referer is element
325N/A cei.initContentType(
325N/A selector.bindToType(e.getType(),e),
325N/A e,e.getDefaultValue());
325N/A stb.refererStack.pop();
325N/A return cei;
325N/A // TODO: support javadoc and userSpecifiedImplClass
325N/A } else {
325N/A CClassInfo bt = new CClassInfo(model,selector.getClassScope(),
325N/A clsName, decl.getLocation(), typeName, elementName, component, bindInfo.toCustomizationList() );
325N/A
325N/A // set javadoc class comment.
325N/A if(decl.getJavadoc()!=null )
325N/A bt.javadoc = decl.getJavadoc()+"\n\n";
325N/A // add extra blank lines so that the schema fragment
325N/A // and user-specified javadoc would be separated
325N/A
325N/A
325N/A // if the implClass is given, set it to ClassItem
325N/A String implClass = decl.getUserSpecifiedImplClass();
325N/A if( implClass!=null )
325N/A bt.setUserSpecifiedImplClass( implClass );
325N/A
325N/A return bt;
325N/A }
325N/A }
325N/A
325N/A private BIGlobalBinding getGlobalBinding() {
325N/A return builder.getGlobalBinding();
325N/A }
325N/A
325N/A /**
325N/A * Derives a name from a schema component.
325N/A * Use the name of the schema component as the default name.
325N/A */
325N/A private String deriveName( XSDeclaration comp ) {
325N/A return builder.deriveName( comp.getName(), comp );
325N/A }
325N/A
325N/A /**
325N/A * Derives a name from a schema component.
325N/A * For complex types, we take redefinition into account when
325N/A * deriving a default name.
325N/A */
325N/A private String deriveName( XSComplexType comp ) {
325N/A String seed = builder.deriveName( comp.getName(), comp );
325N/A int cnt = comp.getRedefinedCount();
325N/A for( ; cnt>0; cnt-- )
325N/A seed = "Original"+seed;
325N/A return seed;
325N/A }
325N/A
325N/A}