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/A
325N/Aimport java.util.ArrayList;
325N/Aimport java.util.HashMap;
325N/Aimport java.util.List;
325N/Aimport java.util.Map;
325N/Aimport java.util.Set;
325N/A
325N/Aimport javax.xml.namespace.QName;
325N/Aimport javax.xml.transform.Transformer;
325N/Aimport javax.xml.transform.TransformerConfigurationException;
325N/Aimport javax.xml.transform.TransformerFactory;
325N/A
325N/Aimport com.sun.codemodel.internal.JCodeModel;
325N/Aimport com.sun.codemodel.internal.fmt.JTextFile;
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.Options;
325N/Aimport com.sun.tools.internal.xjc.Plugin;
325N/Aimport com.sun.tools.internal.xjc.generator.bean.field.FieldRendererFactory;
325N/Aimport com.sun.tools.internal.xjc.model.CClassInfoParent;
325N/Aimport com.sun.tools.internal.xjc.model.Model;
325N/Aimport com.sun.tools.internal.xjc.reader.ModelChecker;
325N/Aimport com.sun.tools.internal.xjc.reader.Ring;
325N/Aimport com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDeclaration;
325N/Aimport com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDom;
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.BISerializable;
325N/Aimport com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo;
325N/Aimport com.sun.tools.internal.xjc.util.CodeModelClassFactory;
325N/Aimport com.sun.tools.internal.xjc.util.ErrorReceiverFilter;
325N/Aimport com.sun.xml.internal.bind.api.impl.NameConverter;
325N/Aimport com.sun.xml.internal.xsom.XSAnnotation;
325N/Aimport com.sun.xml.internal.xsom.XSAttributeUse;
325N/Aimport com.sun.xml.internal.xsom.XSComponent;
325N/Aimport com.sun.xml.internal.xsom.XSDeclaration;
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.XSTerm;
325N/Aimport com.sun.xml.internal.xsom.XSType;
325N/Aimport com.sun.xml.internal.xsom.XSWildcard;
325N/Aimport com.sun.xml.internal.xsom.util.XSFinder;
325N/A
325N/Aimport org.xml.sax.Locator;
325N/A
325N/A/**
325N/A * Root of the XML Schema binder.
325N/A *
325N/A * <div><img src="doc-files/binding_chart.png"/></div>
325N/A *
325N/A * @author Kohsuke Kawaguchi
325N/A */
325N/Apublic class BGMBuilder extends BindingComponent {
325N/A
325N/A /**
325N/A * Entry point.
325N/A */
325N/A public static Model build( XSSchemaSet _schemas, JCodeModel codeModel,
325N/A ErrorReceiver _errorReceiver, Options opts ) {
325N/A // set up a ring
325N/A final Ring old = Ring.begin();
325N/A try {
325N/A ErrorReceiverFilter ef = new ErrorReceiverFilter(_errorReceiver);
325N/A
325N/A Ring.add(XSSchemaSet.class,_schemas);
325N/A Ring.add(codeModel);
325N/A Model model = new Model(opts, codeModel, null/*set later*/, opts.classNameAllocator, _schemas);
325N/A Ring.add(model);
325N/A Ring.add(ErrorReceiver.class,ef);
325N/A Ring.add(CodeModelClassFactory.class,new CodeModelClassFactory(ef));
325N/A
325N/A BGMBuilder builder = new BGMBuilder(opts.defaultPackage,opts.defaultPackage2,
325N/A opts.isExtensionMode(),opts.getFieldRendererFactory(), opts.activePlugins);
325N/A builder._build();
325N/A
325N/A if(ef.hadError()) return null;
325N/A else return model;
325N/A } finally {
325N/A Ring.end(old);
325N/A }
325N/A }
325N/A
325N/A
325N/A /**
325N/A * True if the compiler is running in the extension mode
325N/A * (as opposed to the strict conformance mode.)
325N/A */
325N/A public final boolean inExtensionMode;
325N/A
325N/A /**
325N/A * If this is non-null, this package name takes over
325N/A * all the schema customizations.
325N/A */
325N/A public final String defaultPackage1;
325N/A
325N/A /**
325N/A * If this is non-null, this package name will be
325N/A * used when no customization is specified.
325N/A */
325N/A public final String defaultPackage2;
325N/A
325N/A private final BindGreen green = Ring.get(BindGreen.class);
325N/A private final BindPurple purple = Ring.get(BindPurple.class);
325N/A
325N/A public final Model model = Ring.get(Model.class);
325N/A
325N/A public final FieldRendererFactory fieldRendererFactory;
325N/A
325N/A /**
325N/A * Lazily computed {@link RefererFinder}.
325N/A *
325N/A * @see #getReferer
325N/A */
325N/A private RefererFinder refFinder;
325N/A
325N/A private List<Plugin> activePlugins;
325N/A
325N/A protected BGMBuilder(String defaultPackage1, String defaultPackage2,
325N/A boolean _inExtensionMode, FieldRendererFactory fieldRendererFactory,
325N/A List<Plugin> activePlugins) {
325N/A this.inExtensionMode = _inExtensionMode;
325N/A this.defaultPackage1 = defaultPackage1;
325N/A this.defaultPackage2 = defaultPackage2;
325N/A this.fieldRendererFactory = fieldRendererFactory;
325N/A this.activePlugins = activePlugins;
325N/A promoteGlobalBindings();
325N/A }
325N/A
325N/A private void _build() {
325N/A // do the binding
325N/A buildContents();
325N/A getClassSelector().executeTasks();
325N/A
325N/A // additional error check
325N/A // Reports unused customizations to the user as errors.
325N/A Ring.get(UnusedCustomizationChecker.class).run();
325N/A
325N/A Ring.get(ModelChecker.class).check();
325N/A
325N/A for( Plugin ma : activePlugins )
325N/A ma.postProcessModel(model, Ring.get(ErrorReceiver.class));
325N/A
325N/A }
325N/A
325N/A
325N/A /** List up all the global bindings. */
325N/A private void promoteGlobalBindings() {
325N/A // promote any global bindings in the schema
325N/A XSSchemaSet schemas = Ring.get(XSSchemaSet.class);
325N/A
325N/A for( XSSchema s : schemas.getSchemas() ) {
325N/A BindInfo bi = getBindInfo(s);
325N/A
325N/A // collect all global customizations
325N/A model.getCustomizations().addAll(bi.toCustomizationList());
325N/A
325N/A BIGlobalBinding gb = bi.get(BIGlobalBinding.class);
325N/A if(gb==null)
325N/A continue;
325N/A
325N/A gb.markAsAcknowledged();
325N/A
325N/A if(globalBinding==null) {
325N/A globalBinding = gb;
325N/A } else {
325N/A if (!globalBinding.isEqual(gb)) { // see Issue 687 - this may happen with syntactically imported documents
325N/A // acknowledge this customization and report an error
325N/A // otherwise the user will see "customization is attached to a wrong place" error,
325N/A // which is incorrect
325N/A getErrorReporter().error( gb.getLocation(),
325N/A Messages.ERR_MULTIPLE_GLOBAL_BINDINGS);
325N/A getErrorReporter().error( globalBinding.getLocation(),
325N/A Messages.ERR_MULTIPLE_GLOBAL_BINDINGS_OTHER);
325N/A }
325N/A }
325N/A }
325N/A
325N/A if( globalBinding==null ) {
325N/A // no global customization is present.
325N/A // use the default one
325N/A globalBinding = new BIGlobalBinding();
325N/A BindInfo big = new BindInfo();
325N/A big.addDecl(globalBinding);
325N/A big.setOwner(this,null);
325N/A }
325N/A
325N/A // code generation mode
325N/A model.strategy = globalBinding.getCodeGenerationStrategy();
325N/A model.rootClass = globalBinding.getSuperClass();
325N/A model.rootInterface = globalBinding.getSuperInterface();
325N/A
325N/A particleBinder = globalBinding.isSimpleMode() ? new ExpressionParticleBinder() : new DefaultParticleBinder();
325N/A
325N/A // check XJC extensions and realize them
325N/A BISerializable serial = globalBinding.getSerializable();
325N/A if(serial!=null) {
325N/A model.serializable = true;
325N/A model.serialVersionUID = serial.uid;
325N/A }
325N/A
325N/A // obtain the name conversion mode
325N/A if(globalBinding.nameConverter!=null)
325N/A model.setNameConverter(globalBinding.nameConverter);
325N/A
325N/A // attach global conversions to the appropriate simple types
325N/A globalBinding.dispatchGlobalConversions(schemas);
325N/A
325N/A globalBinding.errorCheck();
325N/A }
325N/A
325N/A /**
325N/A * Global bindings.
325N/A *
325N/A * The empty global binding is set as the default, so that
325N/A * there will be no need to test if the value is null.
325N/A */
325N/A private BIGlobalBinding globalBinding;
325N/A
325N/A /**
325N/A * Gets the global bindings.
325N/A */
325N/A public @NotNull BIGlobalBinding getGlobalBinding() { return globalBinding; }
325N/A
325N/A
325N/A private ParticleBinder particleBinder;
325N/A
325N/A /**
325N/A * Gets the particle binder for this binding.
325N/A */
325N/A public @NotNull ParticleBinder getParticleBinder() { return particleBinder; }
325N/A
325N/A
325N/A /**
325N/A * Name converter that implements "XML->Java name conversion"
325N/A * as specified in the spec.
325N/A *
325N/A * This object abstracts the detail that we use different name
325N/A * conversion depending on the customization.
325N/A *
325N/A * <p>
325N/A * This object should be used to perform any name conversion
325N/A * needs, instead of the JJavaName class in CodeModel.
325N/A */
325N/A public NameConverter getNameConverter() { return model.getNameConverter(); }
325N/A
325N/A /** Fill-in the contents of each classes. */
325N/A private void buildContents() {
325N/A ClassSelector cs = getClassSelector();
325N/A SimpleTypeBuilder stb = Ring.get(SimpleTypeBuilder.class);
325N/A
325N/A for( XSSchema s : Ring.get(XSSchemaSet.class).getSchemas() ) {
325N/A BISchemaBinding sb = getBindInfo(s).get(BISchemaBinding.class);
325N/A
325N/A if(sb!=null && !sb.map) {
325N/A sb.markAsAcknowledged();
325N/A continue; // no mapping for this package
325N/A }
325N/A
325N/A getClassSelector().pushClassScope( new CClassInfoParent.Package(
325N/A getClassSelector().getPackage(s.getTargetNamespace())) );
325N/A
325N/A checkMultipleSchemaBindings(s);
325N/A processPackageJavadoc(s);
325N/A populate(s.getAttGroupDecls(),s);
325N/A populate(s.getAttributeDecls(),s);
325N/A populate(s.getElementDecls(),s);
325N/A populate(s.getModelGroupDecls(),s);
325N/A
325N/A // fill in typeUses
325N/A for (XSType t : s.getTypes().values()) {
325N/A stb.refererStack.push(t);
325N/A model.typeUses().put( getName(t), cs.bindToType(t,s) );
325N/A stb.refererStack.pop();
325N/A }
325N/A
325N/A getClassSelector().popClassScope();
325N/A }
325N/A }
325N/A
325N/A /** Reports an error if there are more than one jaxb:schemaBindings customization. */
325N/A private void checkMultipleSchemaBindings( XSSchema schema ) {
325N/A ArrayList<Locator> locations = new ArrayList<Locator>();
325N/A
325N/A BindInfo bi = getBindInfo(schema);
325N/A for( BIDeclaration bid : bi ) {
325N/A if( bid.getName()==BISchemaBinding.NAME )
325N/A locations.add( bid.getLocation() );
325N/A }
325N/A if(locations.size()<=1) return; // OK
325N/A
325N/A // error
325N/A getErrorReporter().error( locations.get(0),
325N/A Messages.ERR_MULTIPLE_SCHEMA_BINDINGS,
325N/A schema.getTargetNamespace() );
325N/A for( int i=1; i<locations.size(); i++ )
325N/A getErrorReporter().error( (Locator)locations.get(i),
325N/A Messages.ERR_MULTIPLE_SCHEMA_BINDINGS_LOCATION);
325N/A }
325N/A
325N/A /**
325N/A * Calls {@link ClassSelector} for each item in the iterator
325N/A * to populate class items if there is any.
325N/A */
325N/A private void populate( Map<String,? extends XSComponent> col, XSSchema schema ) {
325N/A ClassSelector cs = getClassSelector();
325N/A for( XSComponent sc : col.values() )
325N/A cs.bindToType(sc,schema);
325N/A }
325N/A
325N/A /**
325N/A * Generates <code>package.html</code> if the customization
325N/A * says so.
325N/A */
325N/A private void processPackageJavadoc( XSSchema s ) {
325N/A // look for the schema-wide customization
325N/A BISchemaBinding cust = getBindInfo(s).get(BISchemaBinding.class);
325N/A if(cust==null) return; // not present
325N/A
325N/A cust.markAsAcknowledged();
325N/A if( cust.getJavadoc()==null ) return; // no javadoc customization
325N/A
325N/A // produce a HTML file
325N/A JTextFile html = new JTextFile("package.html");
325N/A html.setContents(cust.getJavadoc());
325N/A getClassSelector().getPackage(s.getTargetNamespace()).addResourceFile(html);
325N/A }
325N/A
325N/A
325N/A
325N/A
325N/A
325N/A
325N/A /**
325N/A * Gets or creates the BindInfo object associated to a schema component.
325N/A *
325N/A * @return
325N/A * Always return a non-null valid BindInfo object.
325N/A * Even if no declaration was specified, this method creates
325N/A * a new BindInfo so that new decls can be added.
325N/A */
325N/A public BindInfo getOrCreateBindInfo( XSComponent schemaComponent ) {
325N/A
325N/A BindInfo bi = _getBindInfoReadOnly(schemaComponent);
325N/A if(bi!=null) return bi;
325N/A
325N/A // XSOM is read-only, so we cannot add new annotations.
325N/A // for components that didn't have annotations,
325N/A // we maintain an external map.
325N/A bi = new BindInfo();
325N/A bi.setOwner(this,schemaComponent);
325N/A externalBindInfos.put(schemaComponent,bi);
325N/A return bi;
325N/A }
325N/A
325N/A
325N/A /**
325N/A * Used as a constant instance to represent the empty {@link BindInfo}.
325N/A */
325N/A private final BindInfo emptyBindInfo = new BindInfo();
325N/A
325N/A /**
325N/A * Gets the BindInfo object associated to a schema component.
325N/A *
325N/A * @return
325N/A * always return a valid {@link BindInfo} object. If none
325N/A * is specified for the given component, a dummy empty BindInfo
325N/A * will be returned.
325N/A */
325N/A public BindInfo getBindInfo( XSComponent schemaComponent ) {
325N/A BindInfo bi = _getBindInfoReadOnly(schemaComponent);
325N/A if(bi!=null) return bi;
325N/A else return emptyBindInfo;
325N/A }
325N/A
325N/A /**
325N/A * Gets the BindInfo object associated to a schema component.
325N/A *
325N/A * @return
325N/A * null if no bind info is associated to this schema component.
325N/A */
325N/A private BindInfo _getBindInfoReadOnly( XSComponent schemaComponent ) {
325N/A
325N/A BindInfo bi = externalBindInfos.get(schemaComponent);
325N/A if(bi!=null) return bi;
325N/A
325N/A XSAnnotation annon = schemaComponent.getAnnotation();
325N/A if(annon!=null) {
325N/A bi = (BindInfo)annon.getAnnotation();
325N/A if(bi!=null) {
325N/A if(bi.getOwner()==null)
325N/A bi.setOwner(this,schemaComponent);
325N/A return bi;
325N/A }
325N/A }
325N/A
325N/A return null;
325N/A }
325N/A
325N/A /**
325N/A * A map that stores binding declarations augmented by XJC.
325N/A */
325N/A private final Map<XSComponent,BindInfo> externalBindInfos = new HashMap<XSComponent,BindInfo>();
325N/A
325N/A /**
325N/A * Gets the {@link BIDom} object that applies to the given particle.
325N/A */
325N/A protected final BIDom getLocalDomCustomization( XSParticle p ) {
325N/A if (p == null) {
325N/A return null;
325N/A }
325N/A BIDom dom = getBindInfo(p).get(BIDom.class);
325N/A if(dom!=null) return dom;
325N/A
325N/A // if not, the term might have one.
325N/A dom = getBindInfo(p.getTerm()).get(BIDom.class);
325N/A if(dom!=null) return dom;
325N/A
325N/A XSTerm t = p.getTerm();
325N/A // type could also have one, in case of the dom customization
325N/A if(t.isElementDecl())
325N/A return getBindInfo(t.asElementDecl().getType()).get(BIDom.class);
325N/A // similarly the model group in a model group definition may have one.
325N/A if(t.isModelGroupDecl())
325N/A return getBindInfo(t.asModelGroupDecl().getModelGroup()).get(BIDom.class);
325N/A
325N/A return null;
325N/A }
325N/A
325N/A /**
325N/A * Returns true if the component should be processed by purple.
325N/A */
325N/A private final XSFinder toPurple = new XSFinder() {
325N/A @Override
325N/A public Boolean attributeUse(XSAttributeUse use) {
325N/A // attribute use always maps to a property
325N/A return true;
325N/A }
325N/A
325N/A @Override
325N/A public Boolean simpleType(XSSimpleType xsSimpleType) {
325N/A // simple type always maps to a type, hence we should take purple
325N/A return true;
325N/A }
325N/A
325N/A @Override
325N/A public Boolean wildcard(XSWildcard xsWildcard) {
325N/A // attribute wildcards always maps to a property.
325N/A // element wildcards should have been processed with particle binders
325N/A return true;
325N/A }
325N/A };
325N/A /**
325N/A * If the component maps to a property, forwards to purple, otherwise to green.
325N/A *
325N/A * If the component is mapped to a type, this method needs to return true.
325N/A * See the chart at the class javadoc.
325N/A */
325N/A public void ying( XSComponent sc, @Nullable XSComponent referer ) {
325N/A if(sc.apply(toPurple)==true || getClassSelector().bindToType(sc,referer)!=null)
325N/A sc.visit(purple);
325N/A else
325N/A sc.visit(green);
325N/A }
325N/A
325N/A private Transformer identityTransformer;
325N/A
325N/A /**
325N/A * Gets the shared instance of the identity transformer.
325N/A */
325N/A public Transformer getIdentityTransformer() {
325N/A try {
325N/A if(identityTransformer==null)
325N/A identityTransformer = TransformerFactory.newInstance().newTransformer();
325N/A return identityTransformer;
325N/A } catch (TransformerConfigurationException e) {
325N/A throw new Error(e); // impossible
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Find all types that refer to the given complex type.
325N/A */
325N/A public Set<XSComponent> getReferer(XSType c) {
325N/A if(refFinder==null) {
325N/A refFinder = new RefererFinder();
325N/A refFinder.schemaSet(Ring.get(XSSchemaSet.class));
325N/A }
325N/A return refFinder.getReferer(c);
325N/A }
325N/A
325N/A /**
325N/A * Returns the QName of the declaration.
325N/A * @return null
325N/A * if the declaration is anonymous.
325N/A */
325N/A public static QName getName(XSDeclaration decl) {
325N/A String local = decl.getName();
325N/A if(local==null) return null;
325N/A return new QName(decl.getTargetNamespace(),local);
325N/A }
325N/A
325N/A /**
325N/A * Derives a name from a schema component.
325N/A *
325N/A * This method handles prefix/suffix modification and
325N/A * XML-to-Java name conversion.
325N/A *
325N/A * @param name
325N/A * The base name. This should be things like element names
325N/A * or type names.
325N/A * @param comp
325N/A * The component from which the base name was taken.
325N/A * Used to determine how names are modified.
325N/A */
325N/A public String deriveName( String name, XSComponent comp ) {
325N/A XSSchema owner = comp.getOwnerSchema();
325N/A
325N/A name = getNameConverter().toClassName(name);
325N/A
325N/A if( owner!=null ) {
325N/A BISchemaBinding sb = getBindInfo(owner).get(BISchemaBinding.class);
325N/A
325N/A if(sb!=null) name = sb.mangleClassName(name,comp);
325N/A }
325N/A
325N/A return name;
325N/A }
325N/A
325N/A public boolean isGenerateMixedExtensions() {
325N/A if (globalBinding != null) {
325N/A return globalBinding.isGenerateMixedExtensions();
325N/A }
325N/A return false;
325N/A }
325N/A
325N/A}