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 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 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: }