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