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