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