ohair@286: /* alanb@368: * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.tools.internal.xjc.reader.xmlschema; ohair@286: ohair@286: import java.util.ArrayList; ohair@286: import java.util.HashMap; ohair@286: import java.util.List; ohair@286: import java.util.Map; ohair@286: import java.util.Set; ohair@286: ohair@286: import javax.xml.namespace.QName; ohair@286: import javax.xml.transform.Transformer; ohair@286: import javax.xml.transform.TransformerConfigurationException; ohair@286: import javax.xml.transform.TransformerFactory; ohair@286: ohair@286: import com.sun.codemodel.internal.JCodeModel; ohair@286: import com.sun.codemodel.internal.fmt.JTextFile; ohair@286: import com.sun.istack.internal.NotNull; ohair@286: import com.sun.istack.internal.Nullable; ohair@286: import com.sun.tools.internal.xjc.ErrorReceiver; ohair@286: import com.sun.tools.internal.xjc.Options; ohair@286: import com.sun.tools.internal.xjc.Plugin; ohair@286: import com.sun.tools.internal.xjc.generator.bean.field.FieldRendererFactory; ohair@286: import com.sun.tools.internal.xjc.model.CClassInfoParent; ohair@286: import com.sun.tools.internal.xjc.model.Model; ohair@286: import com.sun.tools.internal.xjc.reader.ModelChecker; ohair@286: import com.sun.tools.internal.xjc.reader.Ring; ohair@286: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDeclaration; ohair@286: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDom; ohair@286: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIGlobalBinding; ohair@286: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BISchemaBinding; ohair@286: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BISerializable; ohair@286: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo; ohair@286: import com.sun.tools.internal.xjc.util.CodeModelClassFactory; ohair@286: import com.sun.tools.internal.xjc.util.ErrorReceiverFilter; ohair@286: import com.sun.xml.internal.bind.api.impl.NameConverter; alanb@368: import com.sun.xml.internal.bind.v2.util.XmlFactory; ohair@286: import com.sun.xml.internal.xsom.XSAnnotation; ohair@286: import com.sun.xml.internal.xsom.XSAttributeUse; ohair@286: import com.sun.xml.internal.xsom.XSComponent; ohair@286: import com.sun.xml.internal.xsom.XSDeclaration; ohair@286: import com.sun.xml.internal.xsom.XSParticle; ohair@286: import com.sun.xml.internal.xsom.XSSchema; ohair@286: import com.sun.xml.internal.xsom.XSSchemaSet; ohair@286: import com.sun.xml.internal.xsom.XSSimpleType; ohair@286: import com.sun.xml.internal.xsom.XSTerm; ohair@286: import com.sun.xml.internal.xsom.XSType; ohair@286: import com.sun.xml.internal.xsom.XSWildcard; ohair@286: import com.sun.xml.internal.xsom.util.XSFinder; ohair@286: ohair@286: import org.xml.sax.Locator; ohair@286: ohair@286: /** ohair@286: * Root of the XML Schema binder. ohair@286: * ohair@286: *
ohair@286: * ohair@286: * @author Kohsuke Kawaguchi ohair@286: */ ohair@286: public class BGMBuilder extends BindingComponent { ohair@286: ohair@286: /** ohair@286: * Entry point. ohair@286: */ ohair@286: public static Model build( XSSchemaSet _schemas, JCodeModel codeModel, ohair@286: ErrorReceiver _errorReceiver, Options opts ) { ohair@286: // set up a ring ohair@286: final Ring old = Ring.begin(); ohair@286: try { ohair@286: ErrorReceiverFilter ef = new ErrorReceiverFilter(_errorReceiver); ohair@286: ohair@286: Ring.add(XSSchemaSet.class,_schemas); ohair@286: Ring.add(codeModel); ohair@286: Model model = new Model(opts, codeModel, null/*set later*/, opts.classNameAllocator, _schemas); ohair@286: Ring.add(model); ohair@286: Ring.add(ErrorReceiver.class,ef); ohair@286: Ring.add(CodeModelClassFactory.class,new CodeModelClassFactory(ef)); ohair@286: ohair@286: BGMBuilder builder = new BGMBuilder(opts.defaultPackage,opts.defaultPackage2, ohair@286: opts.isExtensionMode(),opts.getFieldRendererFactory(), opts.activePlugins); ohair@286: builder._build(); ohair@286: ohair@286: if(ef.hadError()) return null; ohair@286: else return model; ohair@286: } finally { ohair@286: Ring.end(old); ohair@286: } ohair@286: } ohair@286: ohair@286: ohair@286: /** ohair@286: * True if the compiler is running in the extension mode ohair@286: * (as opposed to the strict conformance mode.) ohair@286: */ ohair@286: public final boolean inExtensionMode; ohair@286: ohair@286: /** ohair@286: * If this is non-null, this package name takes over ohair@286: * all the schema customizations. ohair@286: */ ohair@286: public final String defaultPackage1; ohair@286: ohair@286: /** ohair@286: * If this is non-null, this package name will be ohair@286: * used when no customization is specified. ohair@286: */ ohair@286: public final String defaultPackage2; ohair@286: ohair@286: private final BindGreen green = Ring.get(BindGreen.class); ohair@286: private final BindPurple purple = Ring.get(BindPurple.class); ohair@286: ohair@286: public final Model model = Ring.get(Model.class); ohair@286: ohair@286: public final FieldRendererFactory fieldRendererFactory; ohair@286: ohair@286: /** ohair@286: * Lazily computed {@link RefererFinder}. ohair@286: * ohair@286: * @see #getReferer ohair@286: */ ohair@286: private RefererFinder refFinder; ohair@286: ohair@286: private List activePlugins; ohair@286: ohair@286: protected BGMBuilder(String defaultPackage1, String defaultPackage2, ohair@286: boolean _inExtensionMode, FieldRendererFactory fieldRendererFactory, ohair@286: List activePlugins) { ohair@286: this.inExtensionMode = _inExtensionMode; ohair@286: this.defaultPackage1 = defaultPackage1; ohair@286: this.defaultPackage2 = defaultPackage2; ohair@286: this.fieldRendererFactory = fieldRendererFactory; ohair@286: this.activePlugins = activePlugins; ohair@286: promoteGlobalBindings(); ohair@286: } ohair@286: ohair@286: private void _build() { ohair@286: // do the binding ohair@286: buildContents(); ohair@286: getClassSelector().executeTasks(); ohair@286: ohair@286: // additional error check ohair@286: // Reports unused customizations to the user as errors. ohair@286: Ring.get(UnusedCustomizationChecker.class).run(); ohair@286: ohair@286: Ring.get(ModelChecker.class).check(); ohair@286: ohair@286: for( Plugin ma : activePlugins ) ohair@286: ma.postProcessModel(model, Ring.get(ErrorReceiver.class)); ohair@286: ohair@286: } ohair@286: ohair@286: ohair@286: /** List up all the global bindings. */ ohair@286: private void promoteGlobalBindings() { ohair@286: // promote any global bindings in the schema ohair@286: XSSchemaSet schemas = Ring.get(XSSchemaSet.class); ohair@286: ohair@286: for( XSSchema s : schemas.getSchemas() ) { ohair@286: BindInfo bi = getBindInfo(s); ohair@286: ohair@286: // collect all global customizations ohair@286: model.getCustomizations().addAll(bi.toCustomizationList()); ohair@286: ohair@286: BIGlobalBinding gb = bi.get(BIGlobalBinding.class); ohair@286: if(gb==null) ohair@286: continue; ohair@286: ohair@286: gb.markAsAcknowledged(); ohair@286: ohair@286: if(globalBinding==null) { ohair@286: globalBinding = gb; ohair@286: } else { ohair@286: if (!globalBinding.isEqual(gb)) { // see Issue 687 - this may happen with syntactically imported documents ohair@286: // acknowledge this customization and report an error ohair@286: // otherwise the user will see "customization is attached to a wrong place" error, ohair@286: // which is incorrect ohair@286: getErrorReporter().error( gb.getLocation(), ohair@286: Messages.ERR_MULTIPLE_GLOBAL_BINDINGS); ohair@286: getErrorReporter().error( globalBinding.getLocation(), ohair@286: Messages.ERR_MULTIPLE_GLOBAL_BINDINGS_OTHER); ohair@286: } ohair@286: } ohair@286: } ohair@286: ohair@286: if( globalBinding==null ) { ohair@286: // no global customization is present. ohair@286: // use the default one ohair@286: globalBinding = new BIGlobalBinding(); ohair@286: BindInfo big = new BindInfo(); ohair@286: big.addDecl(globalBinding); ohair@286: big.setOwner(this,null); ohair@286: } ohair@286: ohair@286: // code generation mode ohair@286: model.strategy = globalBinding.getCodeGenerationStrategy(); ohair@286: model.rootClass = globalBinding.getSuperClass(); ohair@286: model.rootInterface = globalBinding.getSuperInterface(); ohair@286: ohair@286: particleBinder = globalBinding.isSimpleMode() ? new ExpressionParticleBinder() : new DefaultParticleBinder(); ohair@286: ohair@286: // check XJC extensions and realize them ohair@286: BISerializable serial = globalBinding.getSerializable(); ohair@286: if(serial!=null) { ohair@286: model.serializable = true; ohair@286: model.serialVersionUID = serial.uid; ohair@286: } ohair@286: ohair@286: // obtain the name conversion mode alanb@368: if (globalBinding.nameConverter!=null) ohair@286: model.setNameConverter(globalBinding.nameConverter); ohair@286: ohair@286: // attach global conversions to the appropriate simple types ohair@286: globalBinding.dispatchGlobalConversions(schemas); ohair@286: ohair@286: globalBinding.errorCheck(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Global bindings. ohair@286: * ohair@286: * The empty global binding is set as the default, so that ohair@286: * there will be no need to test if the value is null. ohair@286: */ ohair@286: private BIGlobalBinding globalBinding; ohair@286: ohair@286: /** ohair@286: * Gets the global bindings. ohair@286: */ ohair@286: public @NotNull BIGlobalBinding getGlobalBinding() { return globalBinding; } ohair@286: ohair@286: ohair@286: private ParticleBinder particleBinder; ohair@286: ohair@286: /** ohair@286: * Gets the particle binder for this binding. ohair@286: */ ohair@286: public @NotNull ParticleBinder getParticleBinder() { return particleBinder; } ohair@286: ohair@286: ohair@286: /** ohair@286: * Name converter that implements "XML->Java name conversion" ohair@286: * as specified in the spec. ohair@286: * ohair@286: * This object abstracts the detail that we use different name ohair@286: * conversion depending on the customization. ohair@286: * ohair@286: *

ohair@286: * This object should be used to perform any name conversion ohair@286: * needs, instead of the JJavaName class in CodeModel. ohair@286: */ ohair@286: public NameConverter getNameConverter() { return model.getNameConverter(); } ohair@286: ohair@286: /** Fill-in the contents of each classes. */ ohair@286: private void buildContents() { ohair@286: ClassSelector cs = getClassSelector(); ohair@286: SimpleTypeBuilder stb = Ring.get(SimpleTypeBuilder.class); ohair@286: ohair@286: for( XSSchema s : Ring.get(XSSchemaSet.class).getSchemas() ) { ohair@286: BISchemaBinding sb = getBindInfo(s).get(BISchemaBinding.class); ohair@286: ohair@286: if(sb!=null && !sb.map) { ohair@286: sb.markAsAcknowledged(); ohair@286: continue; // no mapping for this package ohair@286: } ohair@286: ohair@286: getClassSelector().pushClassScope( new CClassInfoParent.Package( ohair@286: getClassSelector().getPackage(s.getTargetNamespace())) ); ohair@286: ohair@286: checkMultipleSchemaBindings(s); ohair@286: processPackageJavadoc(s); ohair@286: populate(s.getAttGroupDecls(),s); ohair@286: populate(s.getAttributeDecls(),s); ohair@286: populate(s.getElementDecls(),s); ohair@286: populate(s.getModelGroupDecls(),s); ohair@286: ohair@286: // fill in typeUses ohair@286: for (XSType t : s.getTypes().values()) { ohair@286: stb.refererStack.push(t); ohair@286: model.typeUses().put( getName(t), cs.bindToType(t,s) ); ohair@286: stb.refererStack.pop(); ohair@286: } ohair@286: ohair@286: getClassSelector().popClassScope(); ohair@286: } ohair@286: } ohair@286: ohair@286: /** Reports an error if there are more than one jaxb:schemaBindings customization. */ ohair@286: private void checkMultipleSchemaBindings( XSSchema schema ) { ohair@286: ArrayList locations = new ArrayList(); ohair@286: ohair@286: BindInfo bi = getBindInfo(schema); ohair@286: for( BIDeclaration bid : bi ) { ohair@286: if( bid.getName()==BISchemaBinding.NAME ) ohair@286: locations.add( bid.getLocation() ); ohair@286: } ohair@286: if(locations.size()<=1) return; // OK ohair@286: ohair@286: // error ohair@286: getErrorReporter().error( locations.get(0), ohair@286: Messages.ERR_MULTIPLE_SCHEMA_BINDINGS, ohair@286: schema.getTargetNamespace() ); ohair@286: for( int i=1; i col, XSSchema schema ) { ohair@286: ClassSelector cs = getClassSelector(); ohair@286: for( XSComponent sc : col.values() ) ohair@286: cs.bindToType(sc,schema); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Generates package.html if the customization ohair@286: * says so. ohair@286: */ ohair@286: private void processPackageJavadoc( XSSchema s ) { ohair@286: // look for the schema-wide customization ohair@286: BISchemaBinding cust = getBindInfo(s).get(BISchemaBinding.class); ohair@286: if(cust==null) return; // not present ohair@286: ohair@286: cust.markAsAcknowledged(); ohair@286: if( cust.getJavadoc()==null ) return; // no javadoc customization ohair@286: ohair@286: // produce a HTML file ohair@286: JTextFile html = new JTextFile("package.html"); ohair@286: html.setContents(cust.getJavadoc()); ohair@286: getClassSelector().getPackage(s.getTargetNamespace()).addResourceFile(html); ohair@286: } ohair@286: ohair@286: ohair@286: ohair@286: ohair@286: ohair@286: ohair@286: /** ohair@286: * Gets or creates the BindInfo object associated to a schema component. ohair@286: * ohair@286: * @return ohair@286: * Always return a non-null valid BindInfo object. ohair@286: * Even if no declaration was specified, this method creates ohair@286: * a new BindInfo so that new decls can be added. ohair@286: */ ohair@286: public BindInfo getOrCreateBindInfo( XSComponent schemaComponent ) { ohair@286: ohair@286: BindInfo bi = _getBindInfoReadOnly(schemaComponent); ohair@286: if(bi!=null) return bi; ohair@286: ohair@286: // XSOM is read-only, so we cannot add new annotations. ohair@286: // for components that didn't have annotations, ohair@286: // we maintain an external map. ohair@286: bi = new BindInfo(); ohair@286: bi.setOwner(this,schemaComponent); ohair@286: externalBindInfos.put(schemaComponent,bi); ohair@286: return bi; ohair@286: } ohair@286: ohair@286: ohair@286: /** ohair@286: * Used as a constant instance to represent the empty {@link BindInfo}. ohair@286: */ ohair@286: private final BindInfo emptyBindInfo = new BindInfo(); ohair@286: ohair@286: /** ohair@286: * Gets the BindInfo object associated to a schema component. ohair@286: * ohair@286: * @return ohair@286: * always return a valid {@link BindInfo} object. If none ohair@286: * is specified for the given component, a dummy empty BindInfo ohair@286: * will be returned. ohair@286: */ ohair@286: public BindInfo getBindInfo( XSComponent schemaComponent ) { ohair@286: BindInfo bi = _getBindInfoReadOnly(schemaComponent); ohair@286: if(bi!=null) return bi; ohair@286: else return emptyBindInfo; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the BindInfo object associated to a schema component. ohair@286: * ohair@286: * @return ohair@286: * null if no bind info is associated to this schema component. ohair@286: */ ohair@286: private BindInfo _getBindInfoReadOnly( XSComponent schemaComponent ) { ohair@286: ohair@286: BindInfo bi = externalBindInfos.get(schemaComponent); ohair@286: if(bi!=null) return bi; ohair@286: ohair@286: XSAnnotation annon = schemaComponent.getAnnotation(); ohair@286: if(annon!=null) { ohair@286: bi = (BindInfo)annon.getAnnotation(); ohair@286: if(bi!=null) { ohair@286: if(bi.getOwner()==null) ohair@286: bi.setOwner(this,schemaComponent); ohair@286: return bi; ohair@286: } ohair@286: } ohair@286: ohair@286: return null; ohair@286: } ohair@286: ohair@286: /** ohair@286: * A map that stores binding declarations augmented by XJC. ohair@286: */ ohair@286: private final Map externalBindInfos = new HashMap(); ohair@286: ohair@286: /** ohair@286: * Gets the {@link BIDom} object that applies to the given particle. ohair@286: */ ohair@286: protected final BIDom getLocalDomCustomization( XSParticle p ) { ohair@286: if (p == null) { ohair@286: return null; ohair@286: } ohair@286: BIDom dom = getBindInfo(p).get(BIDom.class); ohair@286: if(dom!=null) return dom; ohair@286: ohair@286: // if not, the term might have one. ohair@286: dom = getBindInfo(p.getTerm()).get(BIDom.class); ohair@286: if(dom!=null) return dom; ohair@286: ohair@286: XSTerm t = p.getTerm(); ohair@286: // type could also have one, in case of the dom customization ohair@286: if(t.isElementDecl()) ohair@286: return getBindInfo(t.asElementDecl().getType()).get(BIDom.class); ohair@286: // similarly the model group in a model group definition may have one. ohair@286: if(t.isModelGroupDecl()) ohair@286: return getBindInfo(t.asModelGroupDecl().getModelGroup()).get(BIDom.class); ohair@286: ohair@286: return null; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns true if the component should be processed by purple. ohair@286: */ ohair@286: private final XSFinder toPurple = new XSFinder() { ohair@286: @Override ohair@286: public Boolean attributeUse(XSAttributeUse use) { ohair@286: // attribute use always maps to a property ohair@286: return true; ohair@286: } ohair@286: ohair@286: @Override ohair@286: public Boolean simpleType(XSSimpleType xsSimpleType) { ohair@286: // simple type always maps to a type, hence we should take purple ohair@286: return true; ohair@286: } ohair@286: ohair@286: @Override ohair@286: public Boolean wildcard(XSWildcard xsWildcard) { ohair@286: // attribute wildcards always maps to a property. ohair@286: // element wildcards should have been processed with particle binders ohair@286: return true; ohair@286: } ohair@286: }; ohair@286: /** ohair@286: * If the component maps to a property, forwards to purple, otherwise to green. ohair@286: * ohair@286: * If the component is mapped to a type, this method needs to return true. ohair@286: * See the chart at the class javadoc. ohair@286: */ ohair@286: public void ying( XSComponent sc, @Nullable XSComponent referer ) { ohair@286: if(sc.apply(toPurple)==true || getClassSelector().bindToType(sc,referer)!=null) ohair@286: sc.visit(purple); ohair@286: else ohair@286: sc.visit(green); ohair@286: } ohair@286: ohair@286: private Transformer identityTransformer; ohair@286: ohair@286: /** ohair@286: * Gets the shared instance of the identity transformer. ohair@286: */ ohair@286: public Transformer getIdentityTransformer() { ohair@286: try { alanb@368: if(identityTransformer==null) { alanb@368: TransformerFactory tf = XmlFactory.createTransformerFactory(model.options.disableXmlSecurity); alanb@368: identityTransformer = tf.newTransformer(); alanb@368: } ohair@286: return identityTransformer; ohair@286: } catch (TransformerConfigurationException e) { ohair@286: throw new Error(e); // impossible ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Find all types that refer to the given complex type. ohair@286: */ ohair@286: public Set getReferer(XSType c) { ohair@286: if(refFinder==null) { ohair@286: refFinder = new RefererFinder(); ohair@286: refFinder.schemaSet(Ring.get(XSSchemaSet.class)); ohair@286: } ohair@286: return refFinder.getReferer(c); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the QName of the declaration. ohair@286: * @return null ohair@286: * if the declaration is anonymous. ohair@286: */ ohair@286: public static QName getName(XSDeclaration decl) { ohair@286: String local = decl.getName(); ohair@286: if(local==null) return null; ohair@286: return new QName(decl.getTargetNamespace(),local); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Derives a name from a schema component. ohair@286: * ohair@286: * This method handles prefix/suffix modification and ohair@286: * XML-to-Java name conversion. ohair@286: * ohair@286: * @param name ohair@286: * The base name. This should be things like element names ohair@286: * or type names. ohair@286: * @param comp ohair@286: * The component from which the base name was taken. ohair@286: * Used to determine how names are modified. ohair@286: */ ohair@286: public String deriveName( String name, XSComponent comp ) { ohair@286: XSSchema owner = comp.getOwnerSchema(); ohair@286: ohair@286: name = getNameConverter().toClassName(name); ohair@286: ohair@286: if( owner!=null ) { ohair@286: BISchemaBinding sb = getBindInfo(owner).get(BISchemaBinding.class); ohair@286: ohair@286: if(sb!=null) name = sb.mangleClassName(name,comp); ohair@286: } ohair@286: ohair@286: return name; ohair@286: } ohair@286: ohair@286: public boolean isGenerateMixedExtensions() { ohair@286: if (globalBinding != null) { ohair@286: return globalBinding.isGenerateMixedExtensions(); ohair@286: } ohair@286: return false; ohair@286: } ohair@286: ohair@286: }