aoqi@0: /*
aoqi@0: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0: *
aoqi@0: * This code is free software; you can redistribute it and/or modify it
aoqi@0: * under the terms of the GNU General Public License version 2 only, as
aoqi@0: * published by the Free Software Foundation. Oracle designates this
aoqi@0: * particular file as subject to the "Classpath" exception as provided
aoqi@0: * by Oracle in the LICENSE file that accompanied this code.
aoqi@0: *
aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0: * accompanied this code).
aoqi@0: *
aoqi@0: * You should have received a copy of the GNU General Public License version
aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0: *
aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0: * or visit www.oracle.com if you need additional information or have any
aoqi@0: * questions.
aoqi@0: */
aoqi@0:
aoqi@0: package com.sun.tools.internal.xjc.model;
aoqi@0:
aoqi@0: import java.util.Collections;
aoqi@0: import java.util.HashMap;
aoqi@0: import java.util.HashSet;
aoqi@0: import java.util.Iterator;
aoqi@0: import java.util.LinkedHashMap;
aoqi@0: import java.util.Map;
aoqi@0: import java.util.Set;
aoqi@0:
aoqi@0: import javax.xml.bind.annotation.XmlAttribute;
aoqi@0: import javax.xml.bind.annotation.XmlNsForm;
aoqi@0: import javax.xml.bind.annotation.XmlTransient;
aoqi@0: import javax.xml.namespace.QName;
aoqi@0: import javax.xml.transform.Result;
aoqi@0:
aoqi@0: import com.sun.codemodel.internal.JClass;
aoqi@0: import com.sun.codemodel.internal.JCodeModel;
aoqi@0: import com.sun.codemodel.internal.JPackage;
aoqi@0: import com.sun.tools.internal.xjc.ErrorReceiver;
aoqi@0: import com.sun.tools.internal.xjc.Options;
aoqi@0: import com.sun.tools.internal.xjc.Plugin;
aoqi@0: import com.sun.tools.internal.xjc.api.ClassNameAllocator;
aoqi@0: import com.sun.tools.internal.xjc.generator.bean.BeanGenerator;
aoqi@0: import com.sun.tools.internal.xjc.generator.bean.ImplStructureStrategy;
aoqi@0: import com.sun.tools.internal.xjc.model.nav.NClass;
aoqi@0: import com.sun.tools.internal.xjc.model.nav.NType;
aoqi@0: import com.sun.tools.internal.xjc.model.nav.NavigatorImpl;
aoqi@0: import com.sun.tools.internal.xjc.outline.Outline;
aoqi@0: import com.sun.tools.internal.xjc.reader.xmlschema.Messages;
aoqi@0: import com.sun.tools.internal.xjc.util.ErrorReceiverFilter;
aoqi@0: import com.sun.xml.internal.bind.api.impl.NameConverter;
aoqi@0: import com.sun.xml.internal.bind.v2.model.core.Ref;
aoqi@0: import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
aoqi@0: import com.sun.xml.internal.bind.v2.model.nav.Navigator;
aoqi@0: import com.sun.xml.internal.bind.v2.util.FlattenIterator;
aoqi@0: import com.sun.xml.internal.xsom.XSComponent;
aoqi@0: import com.sun.xml.internal.xsom.XSSchemaSet;
aoqi@0:
aoqi@0: import org.xml.sax.Locator;
aoqi@0: import org.xml.sax.SAXException;
aoqi@0: import org.xml.sax.helpers.LocatorImpl;
aoqi@0:
aoqi@0: /**
aoqi@0: * Root of the object model that represents the code that needs to be generated.
aoqi@0: *
aoqi@0: *
aoqi@0: * A {@link Model} is a schema language neutral representation of the
aoqi@0: * result of a schema parsing. The back-end then works against this model
aoqi@0: * to turn this into a series of Java source code.
aoqi@0: *
aoqi@0: * @author Kohsuke Kawaguchi
aoqi@0: */
aoqi@0: public final class Model implements TypeInfoSet, CCustomizable {
aoqi@0:
aoqi@0: /**
aoqi@0: * Generated beans.
aoqi@0: */
aoqi@0: private final Map beans = new LinkedHashMap();
aoqi@0:
aoqi@0: /**
aoqi@0: * Generated enums.
aoqi@0: */
aoqi@0: private final Map enums = new LinkedHashMap();
aoqi@0:
aoqi@0: /**
aoqi@0: * The element mappings.
aoqi@0: */
aoqi@0: private final Map> elementMappings =
aoqi@0: new HashMap>();
aoqi@0:
aoqi@0: private final Iterable extends CElementInfo> allElements =
aoqi@0: new Iterable() {
aoqi@0: public Iterator iterator() {
aoqi@0: return new FlattenIterator(elementMappings.values());
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: /**
aoqi@0: * {@link TypeUse}s for all named types.
aoqi@0: *
aoqi@0: * I really don't want to promote the notion of a 'type' in any place except in the XML Schema code,
aoqi@0: * but this needs to be exposed for JAX-RPC. A reference to a named XML type will be converted into
aoqi@0: * a reference to a Java type with annotations.
aoqi@0: */
aoqi@0: private final Map typeUses = new LinkedHashMap();
aoqi@0:
aoqi@0: /**
aoqi@0: * {@link NameConverter} to be used.
aoqi@0: */
aoqi@0: private NameConverter nameConverter;
aoqi@0:
aoqi@0: /**
aoqi@0: * Single linked list that connects all {@link CCustomizations} that belong to this model.
aoqi@0: *
aoqi@0: * @see CCustomizations#next
aoqi@0: */
aoqi@0: /*package*/ CCustomizations customizations;
aoqi@0:
aoqi@0: /**
aoqi@0: * This field controls the generation of package level annotations for s2j
aoqi@0: */
aoqi@0: private boolean packageLevelAnnotations = true;
aoqi@0:
aoqi@0: /**
aoqi@0: * If this model was built from XML Schema, this field
aoqi@0: * stores the root object of the parse schema model.
aoqi@0: * Otherwise null.
aoqi@0: *
aoqi@0: * @sine 2.1.1
aoqi@0: */
aoqi@0: public final XSSchemaSet schemaComponent;
aoqi@0:
aoqi@0: private CCustomizations gloablCustomizations = new CCustomizations();
aoqi@0:
aoqi@0: /**
aoqi@0: * @param nc
aoqi@0: * Usually this should be set in the constructor, but we do allow this parameter
aoqi@0: * to be initially null, and then set later.
aoqi@0: * @param schemaComponent
aoqi@0: * The source schema model, if this is built from XSD.
aoqi@0: */
aoqi@0: public Model( Options opts, JCodeModel cm, NameConverter nc, ClassNameAllocator allocator, XSSchemaSet schemaComponent ) {
aoqi@0: this.options = opts;
aoqi@0: this.codeModel = cm;
aoqi@0: this.nameConverter = nc;
aoqi@0: this.defaultSymbolSpace = new SymbolSpace(codeModel);
aoqi@0: defaultSymbolSpace.setType(codeModel.ref(Object.class));
aoqi@0:
aoqi@0: elementMappings.put(null,new HashMap());
aoqi@0:
aoqi@0: if(opts.automaticNameConflictResolution)
aoqi@0: allocator = new AutoClassNameAllocator(allocator);
aoqi@0: this.allocator = new ClassNameAllocatorWrapper(allocator);
aoqi@0: this.schemaComponent = schemaComponent;
aoqi@0: this.gloablCustomizations.setParent(this,this);
aoqi@0: }
aoqi@0:
aoqi@0: public void setNameConverter(NameConverter nameConverter) {
aoqi@0: assert this.nameConverter==null;
aoqi@0: assert nameConverter!=null;
aoqi@0: this.nameConverter = nameConverter;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Gets the name converter that shall be used to parse XML names into Java names.
aoqi@0: */
aoqi@0: public final NameConverter getNameConverter() {
aoqi@0: return nameConverter;
aoqi@0: }
aoqi@0:
aoqi@0: public boolean isPackageLevelAnnotations() {
aoqi@0: return packageLevelAnnotations;
aoqi@0: }
aoqi@0:
aoqi@0: public void setPackageLevelAnnotations(boolean packageLevelAnnotations) {
aoqi@0: this.packageLevelAnnotations = packageLevelAnnotations;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * This model uses this code model exclusively.
aoqi@0: */
aoqi@0: @XmlTransient
aoqi@0: public final JCodeModel codeModel;
aoqi@0:
aoqi@0: /**
aoqi@0: * Command-line options used for building this model.
aoqi@0: */
aoqi@0: public final Options options;
aoqi@0:
aoqi@0: /**
aoqi@0: * True to generate serializable classes.
aoqi@0: */
aoqi@0: @XmlAttribute
aoqi@0: public boolean serializable;
aoqi@0:
aoqi@0: /**
aoqi@0: * serial version UID to be generated.
aoqi@0: *
aoqi@0: * null if not to generate serialVersionUID field.
aoqi@0: */
aoqi@0: @XmlAttribute
aoqi@0: public Long serialVersionUID;
aoqi@0:
aoqi@0: /**
aoqi@0: * If non-null, all the generated classes should eventually derive from this class.
aoqi@0: */
aoqi@0: @XmlTransient
aoqi@0: public JClass rootClass;
aoqi@0:
aoqi@0: /**
aoqi@0: * If non-null, all the generated interfaces should eventually derive from this interface.
aoqi@0: */
aoqi@0: @XmlTransient
aoqi@0: public JClass rootInterface;
aoqi@0:
aoqi@0: /**
aoqi@0: * Specifies the code generation strategy.
aoqi@0: * Must not be null.
aoqi@0: */
aoqi@0: public ImplStructureStrategy strategy = ImplStructureStrategy.BEAN_ONLY;
aoqi@0:
aoqi@0: /**
aoqi@0: * This allocator has the final say on deciding the class name.
aoqi@0: * Must not be null.
aoqi@0: *
aoqi@0: *
aoqi@0: * Model classes are responsible for using the allocator.
aoqi@0: * This allocator interaction should be transparent to the user/builder
aoqi@0: * of the model.
aoqi@0: */
aoqi@0: /*package*/ final ClassNameAllocatorWrapper allocator;
aoqi@0:
aoqi@0: /**
aoqi@0: * Default ID/IDREF symbol space. Any ID/IDREF without explicit
aoqi@0: * reference to a symbol space is assumed to use this default
aoqi@0: * symbol space.
aoqi@0: */
aoqi@0: @XmlTransient
aoqi@0: public final SymbolSpace defaultSymbolSpace;
aoqi@0:
aoqi@0: /** All the defined {@link SymbolSpace}s keyed by their name. */
aoqi@0: private final Map symbolSpaces = new HashMap();
aoqi@0:
aoqi@0: public SymbolSpace getSymbolSpace( String name ) {
aoqi@0: SymbolSpace ss = symbolSpaces.get(name);
aoqi@0: if(ss==null)
aoqi@0: symbolSpaces.put(name,ss=new SymbolSpace(codeModel));
aoqi@0: return ss;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Fully-generate the source code into the given model.
aoqi@0: *
aoqi@0: * @return
aoqi@0: * null if there was any errors. Otherwise it returns a valid
aoqi@0: * {@link Outline} object, which captures how the model objects
aoqi@0: * are mapped to the generated source code.
aoqi@0: *
aoqi@0: * Add-ons can use those information to further augment the generated
aoqi@0: * source code.
aoqi@0: */
aoqi@0: public Outline generateCode(Options opt,ErrorReceiver receiver) {
aoqi@0: ErrorReceiverFilter ehf = new ErrorReceiverFilter(receiver);
aoqi@0:
aoqi@0: // run extensions // moved to BGMBuilder._build() - issue with hyperjaxb3
aoqi@0: // for( Plugin ma : opt.activePlugins )
aoqi@0: // ma.postProcessModel(this,ehf);
aoqi@0:
aoqi@0: Outline o = BeanGenerator.generate(this, ehf);
aoqi@0:
aoqi@0: try {// run extensions
aoqi@0: for( Plugin ma : opt.activePlugins )
aoqi@0: ma.run(o,opt,ehf);
aoqi@0: } catch (SAXException e) {
aoqi@0: // fatal error. error should have been reported
aoqi@0: return null;
aoqi@0: }
aoqi@0:
aoqi@0: // check for unused plug-in customizations.
aoqi@0: // these can be only checked after the plug-ins run, so it's here.
aoqi@0: // the JAXB bindings are checked by XMLSchema's builder.
aoqi@0: Set check = new HashSet();
aoqi@0: for( CCustomizations c=customizations; c!=null; c=c.next ) {
aoqi@0: if(!check.add(c)) {
aoqi@0: throw new AssertionError(); // detect a loop
aoqi@0: }
aoqi@0: for (CPluginCustomization p : c) {
aoqi@0: if(!p.isAcknowledged()) {
aoqi@0: ehf.error(
aoqi@0: p.locator,
aoqi@0: Messages.format(
aoqi@0: Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION,
aoqi@0: p.element.getNodeName()
aoqi@0: ));
aoqi@0: ehf.error(
aoqi@0: c.getOwner().getLocator(),
aoqi@0: Messages.format(
aoqi@0: Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION_LOCATION));
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: if(ehf.hadError())
aoqi@0: o = null;
aoqi@0: return o;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Represents the "top-level binding".
aoqi@0: *
aoqi@0: *
aoqi@0: * This is used to support the use of a schema inside WSDL.
aoqi@0: * For XML Schema, the top-level binding is a map from
aoqi@0: * global element declarations to its representation class.
aoqi@0: *
aoqi@0: *
aoqi@0: * For other schema languages, it should follow the appendicies in
aoqi@0: * WSDL (but in practice no one would use WSDL with a schema language
aoqi@0: * other than XML Schema, so it doesn't really matter.)
aoqi@0: *
aoqi@0: *
aoqi@0: * This needs to be filled by the front-end.
aoqi@0: */
aoqi@0: public final Map createTopLevelBindings() {
aoqi@0: Map r = new HashMap();
aoqi@0: for( CClassInfo b : beans().values() ) {
aoqi@0: if(b.isElement())
aoqi@0: r.put(b.getElementName(),b);
aoqi@0: }
aoqi@0: return r;
aoqi@0: }
aoqi@0:
aoqi@0: public Navigator getNavigator() {
aoqi@0: return NavigatorImpl.theInstance;
aoqi@0: }
aoqi@0:
aoqi@0: public CNonElement getTypeInfo(NType type) {
aoqi@0: CBuiltinLeafInfo leaf = CBuiltinLeafInfo.LEAVES.get(type);
aoqi@0: if(leaf!=null) return leaf;
aoqi@0:
aoqi@0: return getClassInfo(getNavigator().asDecl(type));
aoqi@0: }
aoqi@0:
aoqi@0: public CBuiltinLeafInfo getAnyTypeInfo() {
aoqi@0: return CBuiltinLeafInfo.ANYTYPE;
aoqi@0: }
aoqi@0:
aoqi@0: public CNonElement getTypeInfo(Ref ref) {
aoqi@0: // TODO: handle XmlValueList
aoqi@0: assert !ref.valueList;
aoqi@0: return getTypeInfo(ref.type);
aoqi@0: }
aoqi@0:
aoqi@0: public Map beans() {
aoqi@0: return beans;
aoqi@0: }
aoqi@0:
aoqi@0: public Map enums() {
aoqi@0: return enums;
aoqi@0: }
aoqi@0:
aoqi@0: public Map typeUses() {
aoqi@0: return typeUses;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * No array mapping generation for XJC.
aoqi@0: */
aoqi@0: public Map arrays() {
aoqi@0: return Collections.emptyMap();
aoqi@0: }
aoqi@0:
aoqi@0: public Map builtins() {
aoqi@0: return CBuiltinLeafInfo.LEAVES;
aoqi@0: }
aoqi@0:
aoqi@0: public CClassInfo getClassInfo(NClass t) {
aoqi@0: return beans.get(t);
aoqi@0: }
aoqi@0:
aoqi@0: public CElementInfo getElementInfo(NClass scope,QName name) {
aoqi@0: Map m = elementMappings.get(scope);
aoqi@0: if(m!=null) {
aoqi@0: CElementInfo r = m.get(name);
aoqi@0: if(r!=null) return r;
aoqi@0: }
aoqi@0: return elementMappings.get(null).get(name);
aoqi@0: }
aoqi@0:
aoqi@0: public Map getElementMappings(NClass scope) {
aoqi@0: return elementMappings.get(scope);
aoqi@0: }
aoqi@0:
aoqi@0: public Iterable extends CElementInfo> getAllElements() {
aoqi@0: return allElements;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * @deprecated
aoqi@0: * Always return null. Perhaps you are interested in {@link #schemaComponent}?
aoqi@0: */
aoqi@0: public XSComponent getSchemaComponent() {
aoqi@0: return null;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * @deprecated
aoqi@0: * No line number available for the "root" component.
aoqi@0: */
aoqi@0: public Locator getLocator() {
aoqi@0: LocatorImpl r = new LocatorImpl();
aoqi@0: r.setLineNumber(-1);
aoqi@0: r.setColumnNumber(-1);
aoqi@0: return r;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Gets the global customizations.
aoqi@0: */
aoqi@0: public CCustomizations getCustomizations() {
aoqi@0: return gloablCustomizations;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Not implemented in the compile-time model.
aoqi@0: */
aoqi@0: public Map getXmlNs(String namespaceUri) {
aoqi@0: return Collections.emptyMap();
aoqi@0: }
aoqi@0:
aoqi@0: public Map getSchemaLocations() {
aoqi@0: return Collections.emptyMap();
aoqi@0: }
aoqi@0:
aoqi@0: public XmlNsForm getElementFormDefault(String nsUri) {
aoqi@0: throw new UnsupportedOperationException();
aoqi@0: }
aoqi@0:
aoqi@0: public XmlNsForm getAttributeFormDefault(String nsUri) {
aoqi@0: throw new UnsupportedOperationException();
aoqi@0: }
aoqi@0:
aoqi@0: public void dump(Result out) {
aoqi@0: // TODO
aoqi@0: throw new UnsupportedOperationException();
aoqi@0: }
aoqi@0:
aoqi@0: /*package*/ void add( CEnumLeafInfo e ) {
aoqi@0: enums.put( e.getClazz(), e );
aoqi@0: }
aoqi@0:
aoqi@0: /*package*/ void add( CClassInfo ci ) {
aoqi@0: beans.put( ci.getClazz(), ci );
aoqi@0: }
aoqi@0:
aoqi@0: /*package*/ void add( CElementInfo ei ) {
aoqi@0: NClass clazz = null;
aoqi@0: if(ei.getScope()!=null)
aoqi@0: clazz = ei.getScope().getClazz();
aoqi@0:
aoqi@0: Map m = elementMappings.get(clazz);
aoqi@0: if(m==null)
aoqi@0: elementMappings.put(clazz,m=new HashMap());
aoqi@0: m.put(ei.getElementName(),ei);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: private final Map cache = new HashMap();
aoqi@0:
aoqi@0: public CClassInfoParent.Package getPackage(JPackage pkg) {
aoqi@0: CClassInfoParent.Package r = cache.get(pkg);
aoqi@0: if(r==null)
aoqi@0: cache.put(pkg,r=new CClassInfoParent.Package(pkg));
aoqi@0: return r;
aoqi@0: }
aoqi@0:
aoqi@0: /*package*/ static final Locator EMPTY_LOCATOR;
aoqi@0:
aoqi@0: static {
aoqi@0: LocatorImpl l = new LocatorImpl();
aoqi@0: l.setColumnNumber(-1);
aoqi@0: l.setLineNumber(-1);
aoqi@0: EMPTY_LOCATOR = l;
aoqi@0: }
aoqi@0: }