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.ArrayList; aoqi@0: import java.util.Collection; aoqi@0: import java.util.HashSet; aoqi@0: import java.util.Iterator; aoqi@0: import java.util.List; aoqi@0: import java.util.Set; aoqi@0: aoqi@0: import javax.xml.bind.annotation.XmlElement; aoqi@0: import javax.xml.bind.annotation.XmlID; aoqi@0: import javax.xml.bind.annotation.XmlIDREF; aoqi@0: import javax.xml.bind.annotation.XmlRootElement; aoqi@0: import javax.xml.namespace.QName; 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.istack.internal.Nullable; aoqi@0: import com.sun.tools.internal.xjc.Language; 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.outline.Aspect; aoqi@0: import com.sun.tools.internal.xjc.outline.Outline; aoqi@0: import com.sun.tools.internal.xjc.reader.Ring; aoqi@0: import com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder; aoqi@0: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIFactoryMethod; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.ClassInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.Element; aoqi@0: import com.sun.xml.internal.xsom.XSComponent; aoqi@0: aoqi@0: import org.xml.sax.Locator; aoqi@0: aoqi@0: /** aoqi@0: * Mutable {@link ClassInfo} represenatation. aoqi@0: * aoqi@0: *

aoqi@0: * Schema parsers build these objects. aoqi@0: * aoqi@0: * @author Kohsuke Kawaguchi aoqi@0: */ aoqi@0: public final class CClassInfo extends AbstractCElement implements ClassInfo, CClassInfoParent, CClass, NClass { aoqi@0: aoqi@0: @XmlIDREF aoqi@0: private CClass baseClass; aoqi@0: aoqi@0: /** aoqi@0: * List of all subclasses, together with {@link #nextSibling}. aoqi@0: * aoqi@0: * If this class has no sub-class, this field is null. Otherwise, aoqi@0: * this field points to a sub-class of this class. From there you can enumerate aoqi@0: * all the sub-classes by using {@link #nextSibling}. aoqi@0: */ aoqi@0: private CClassInfo firstSubclass; aoqi@0: aoqi@0: /** aoqi@0: * @see #firstSubclass aoqi@0: */ aoqi@0: private CClassInfo nextSibling = null; aoqi@0: aoqi@0: /** aoqi@0: * @see #getTypeName() aoqi@0: */ aoqi@0: private final QName typeName; aoqi@0: aoqi@0: /** aoqi@0: * Custom {@link #getSqueezedName() squeezed name}, if any. aoqi@0: */ aoqi@0: private /*almost final*/ @Nullable String squeezedName; aoqi@0: aoqi@0: /** aoqi@0: * If this class also gets {@link XmlRootElement}, the class name. aoqi@0: */ aoqi@0: private final @Nullable QName elementName; aoqi@0: aoqi@0: private boolean isOrdered = true; aoqi@0: aoqi@0: private final List properties = new ArrayList(); aoqi@0: aoqi@0: /** aoqi@0: * TODO: revisit this design. aoqi@0: * we should at least do a basic encapsulation to avoid careless aoqi@0: * mistakes. Maybe we should even differ the javadoc generation aoqi@0: * by queueing runners. aoqi@0: */ aoqi@0: public String javadoc; aoqi@0: aoqi@0: @XmlIDREF aoqi@0: private final CClassInfoParent parent; aoqi@0: aoqi@0: /** aoqi@0: * short name. aoqi@0: */ aoqi@0: public final String shortName; aoqi@0: aoqi@0: /** aoqi@0: * Optional user-specified implementation override class. aoqi@0: */ aoqi@0: private @Nullable String implClass; aoqi@0: aoqi@0: /** aoqi@0: * The {@link Model} object to which this bean belongs. aoqi@0: */ aoqi@0: public final Model model; aoqi@0: aoqi@0: /** aoqi@0: * @see #hasAttributeWildcard() aoqi@0: */ aoqi@0: private boolean hasAttributeWildcard; aoqi@0: aoqi@0: aoqi@0: public CClassInfo(Model model,JPackage pkg, String shortName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) { aoqi@0: this(model,model.getPackage(pkg),shortName,location,typeName,elementName,source,customizations); aoqi@0: } aoqi@0: aoqi@0: public CClassInfo(Model model,CClassInfoParent p, String shortName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) { aoqi@0: super(model,source,location,customizations); aoqi@0: this.model = model; aoqi@0: this.parent = p; aoqi@0: this.shortName = model.allocator.assignClassName(parent,shortName); aoqi@0: this.typeName = typeName; aoqi@0: this.elementName = elementName; aoqi@0: aoqi@0: Language schemaLanguage = model.options.getSchemaLanguage(); aoqi@0: if ((schemaLanguage != null) && aoqi@0: (schemaLanguage.equals(Language.XMLSCHEMA) || schemaLanguage.equals(Language.WSDL))) { aoqi@0: BIFactoryMethod factoryMethod = Ring.get(BGMBuilder.class).getBindInfo(source).get(BIFactoryMethod.class); aoqi@0: if(factoryMethod!=null) { aoqi@0: factoryMethod.markAsAcknowledged(); aoqi@0: this.squeezedName = factoryMethod.name; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: model.add(this); aoqi@0: } aoqi@0: aoqi@0: public CClassInfo(Model model,JCodeModel cm, String fullName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) { aoqi@0: super(model,source,location,customizations); aoqi@0: this.model = model; aoqi@0: int idx = fullName.indexOf('.'); aoqi@0: if(idx<0) { aoqi@0: this.parent = model.getPackage(cm.rootPackage()); aoqi@0: this.shortName = model.allocator.assignClassName(parent,fullName); aoqi@0: } else { aoqi@0: this.parent = model.getPackage(cm._package(fullName.substring(0,idx))); aoqi@0: this.shortName = model.allocator.assignClassName(parent,fullName.substring(idx+1)); aoqi@0: } aoqi@0: this.typeName = typeName; aoqi@0: this.elementName = elementName; aoqi@0: aoqi@0: model.add(this); aoqi@0: } aoqi@0: aoqi@0: public boolean hasAttributeWildcard() { aoqi@0: return hasAttributeWildcard; aoqi@0: } aoqi@0: aoqi@0: public void hasAttributeWildcard(boolean hasAttributeWildcard) { aoqi@0: this.hasAttributeWildcard = hasAttributeWildcard; aoqi@0: } aoqi@0: aoqi@0: public boolean hasSubClasses() { aoqi@0: return firstSubclass!=null; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns true if a new attribute wildcard property needs to be aoqi@0: * declared on this class. aoqi@0: */ aoqi@0: public boolean declaresAttributeWildcard() { aoqi@0: return hasAttributeWildcard && !inheritsAttributeWildcard(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns true if this class inherits a wildcard attribute property aoqi@0: * from its ancestor classes. aoqi@0: */ aoqi@0: public boolean inheritsAttributeWildcard() { aoqi@0: if (getRefBaseClass() != null) { aoqi@0: CClassRef cref = (CClassRef)baseClass; aoqi@0: if (cref.getSchemaComponent().getForeignAttributes().size() > 0) { aoqi@0: return true; aoqi@0: } aoqi@0: } else { aoqi@0: for( CClassInfo c=getBaseClass(); c!=null; c=c.getBaseClass() ) { aoqi@0: if(c.hasAttributeWildcard) aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: public NClass getClazz() { aoqi@0: return this; aoqi@0: } aoqi@0: aoqi@0: public CClassInfo getScope() { aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: @XmlID aoqi@0: public String getName() { aoqi@0: return fullName(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns the "squeezed name" of this bean token. aoqi@0: *

aoqi@0: * The squeezed name of a bean is the concatenation of aoqi@0: * the names of its outer classes and itself. aoqi@0: *

aoqi@0: * Thus if the bean is "org.acme.foo.Bean", then the squeezed name is "Bean", aoqi@0: * if the bean is "org.acme.foo.Outer1.Outer2.Bean", then "Outer1Outer2Bean". aoqi@0: *

aoqi@0: * This is used by the code generator aoqi@0: */ aoqi@0: @XmlElement aoqi@0: public String getSqueezedName() { aoqi@0: if (squeezedName != null) return squeezedName; aoqi@0: return calcSqueezedName.onBean(this); aoqi@0: } aoqi@0: aoqi@0: private static final CClassInfoParent.Visitor calcSqueezedName = new Visitor() { aoqi@0: public String onBean(CClassInfo bean) { aoqi@0: return bean.parent.accept(this)+bean.shortName; aoqi@0: } aoqi@0: aoqi@0: public String onElement(CElementInfo element) { aoqi@0: return element.parent.accept(this)+element.shortName(); aoqi@0: } aoqi@0: aoqi@0: public String onPackage(JPackage pkg) { aoqi@0: return ""; aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: /** aoqi@0: * Returns a mutable list. aoqi@0: */ aoqi@0: public List getProperties() { aoqi@0: return properties; aoqi@0: } aoqi@0: aoqi@0: public boolean hasValueProperty() { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Gets a propery by name. aoqi@0: */ aoqi@0: public CPropertyInfo getProperty(String name) { aoqi@0: // TODO: does this method need to be fast? aoqi@0: for( CPropertyInfo p : properties ) aoqi@0: if(p.getName(false).equals(name)) aoqi@0: return p; aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public boolean hasProperties() { aoqi@0: return !getProperties().isEmpty(); aoqi@0: } aoqi@0: aoqi@0: public boolean isElement() { aoqi@0: return elementName!=null; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Guaranteed to return this. aoqi@0: */ aoqi@0: @Deprecated aoqi@0: public CNonElement getInfo() { aoqi@0: return this; aoqi@0: } aoqi@0: aoqi@0: public Element asElement() { aoqi@0: if(isElement()) aoqi@0: return this; aoqi@0: else aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public boolean isOrdered() { aoqi@0: return isOrdered; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * @deprecated aoqi@0: * if you are calling this method directly, you must be doing something wrong. aoqi@0: */ aoqi@0: public boolean isFinal() { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: public void setOrdered(boolean value) { aoqi@0: isOrdered = value; aoqi@0: } aoqi@0: aoqi@0: public QName getElementName() { aoqi@0: return elementName; aoqi@0: } aoqi@0: aoqi@0: public QName getTypeName() { aoqi@0: return typeName; aoqi@0: } aoqi@0: aoqi@0: public boolean isSimpleType() { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns the FQCN of this bean. aoqi@0: */ aoqi@0: public String fullName() { aoqi@0: String r = parent.fullName(); aoqi@0: if(r.length()==0) return shortName; aoqi@0: else return r+'.'+shortName; aoqi@0: } aoqi@0: aoqi@0: public CClassInfoParent parent() { aoqi@0: return parent; aoqi@0: } aoqi@0: aoqi@0: public void setUserSpecifiedImplClass(String implClass) { aoqi@0: assert this.implClass==null; aoqi@0: assert implClass!=null; aoqi@0: this.implClass = implClass; aoqi@0: } aoqi@0: aoqi@0: public String getUserSpecifiedImplClass() { aoqi@0: return implClass; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Adds a new property. aoqi@0: */ aoqi@0: public void addProperty(CPropertyInfo prop) { aoqi@0: if(prop.ref().isEmpty()) aoqi@0: // this property isn't contributing anything aoqi@0: // this happens when you try to map an empty sequence to a property aoqi@0: return; aoqi@0: prop.setParent(this); aoqi@0: properties.add(prop); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * This method accepts both {@link CClassInfo} (which means the base class aoqi@0: * is also generated), or {@link CClassRef} (which means the base class is aoqi@0: * already generated and simply referenced.) aoqi@0: * aoqi@0: * The latter is treated somewhat special --- from the rest of the model aoqi@0: * this external base class is invisible. This modeling might need more aoqi@0: * thoughts to get right. aoqi@0: */ aoqi@0: public void setBaseClass(CClass base) { aoqi@0: assert baseClass==null; aoqi@0: assert base!=null; aoqi@0: baseClass = base; aoqi@0: aoqi@0: assert nextSibling==null; aoqi@0: if (base instanceof CClassInfo) { aoqi@0: CClassInfo realBase = (CClassInfo) base; aoqi@0: this.nextSibling = realBase.firstSubclass; aoqi@0: realBase.firstSubclass = this; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * This inherited version returns null if this class extends from {@link CClassRef}. aoqi@0: * aoqi@0: * @see #getRefBaseClass() aoqi@0: */ aoqi@0: public CClassInfo getBaseClass() { aoqi@0: if (baseClass instanceof CClassInfo) { aoqi@0: return (CClassInfo) baseClass; aoqi@0: } else { aoqi@0: return null; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public CClassRef getRefBaseClass() { aoqi@0: if (baseClass instanceof CClassRef) { aoqi@0: return (CClassRef) baseClass; aoqi@0: } else { aoqi@0: return null; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Enumerates all the sub-classes of this class. aoqi@0: */ aoqi@0: public Iterator listSubclasses() { aoqi@0: return new Iterator() { aoqi@0: CClassInfo cur = firstSubclass; aoqi@0: public boolean hasNext() { aoqi@0: return cur!=null; aoqi@0: } aoqi@0: aoqi@0: public CClassInfo next() { aoqi@0: CClassInfo r = cur; aoqi@0: cur = cur.nextSibling; aoqi@0: return r; aoqi@0: } aoqi@0: aoqi@0: public void remove() { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: }; aoqi@0: } aoqi@0: aoqi@0: public CClassInfo getSubstitutionHead() { aoqi@0: CClassInfo c=getBaseClass(); aoqi@0: while(c!=null && !c.isElement()) aoqi@0: c=c.getBaseClass(); aoqi@0: return c; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Interfaces to be implemented. aoqi@0: * Lazily constructed. aoqi@0: */ aoqi@0: private Set _implements = null; aoqi@0: aoqi@0: public void _implements(JClass c) { aoqi@0: if(_implements==null) aoqi@0: _implements = new HashSet(); aoqi@0: _implements.add(c); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** Constructor declarations. array of {@link Constructor}s. */ aoqi@0: private final List constructors = new ArrayList(1); aoqi@0: aoqi@0: /** Creates a new constructor declaration and adds it. */ aoqi@0: public void addConstructor( String... fieldNames ) { aoqi@0: constructors.add(new Constructor(fieldNames)); aoqi@0: } aoqi@0: aoqi@0: /** list all constructor declarations. */ aoqi@0: public Collection getConstructors() { aoqi@0: return constructors; aoqi@0: } aoqi@0: aoqi@0: public final T accept(Visitor visitor) { aoqi@0: return visitor.onBean(this); aoqi@0: } aoqi@0: aoqi@0: public JPackage getOwnerPackage() { aoqi@0: return parent.getOwnerPackage(); aoqi@0: } aoqi@0: aoqi@0: public final NClass getType() { aoqi@0: return this; aoqi@0: } aoqi@0: aoqi@0: public final JClass toType(Outline o, Aspect aspect) { aoqi@0: switch(aspect) { aoqi@0: case IMPLEMENTATION: aoqi@0: return o.getClazz(this).implRef; aoqi@0: case EXPOSED: aoqi@0: return o.getClazz(this).ref; aoqi@0: default: aoqi@0: throw new IllegalStateException(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public boolean isBoxedType() { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: public String toString() { aoqi@0: return fullName(); aoqi@0: } aoqi@0: }