| /* |
| * Copyright (c) 1997, 2022 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Distribution License v. 1.0, which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| package com.sun.tools.xjc.model; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| |
| import jakarta.xml.bind.annotation.XmlElement; |
| import jakarta.xml.bind.annotation.XmlID; |
| import jakarta.xml.bind.annotation.XmlIDREF; |
| import jakarta.xml.bind.annotation.XmlRootElement; |
| import javax.xml.namespace.QName; |
| |
| import com.sun.codemodel.JClass; |
| import com.sun.codemodel.JCodeModel; |
| import com.sun.codemodel.JPackage; |
| import com.sun.istack.Nullable; |
| import com.sun.tools.xjc.Language; |
| import com.sun.tools.xjc.model.nav.NClass; |
| import com.sun.tools.xjc.model.nav.NType; |
| import com.sun.tools.xjc.outline.Aspect; |
| import com.sun.tools.xjc.outline.Outline; |
| import com.sun.tools.xjc.reader.Ring; |
| import com.sun.tools.xjc.reader.xmlschema.BGMBuilder; |
| import com.sun.tools.xjc.reader.xmlschema.bindinfo.BIFactoryMethod; |
| import org.glassfish.jaxb.core.v2.model.core.ClassInfo; |
| import org.glassfish.jaxb.core.v2.model.core.Element; |
| import com.sun.xml.xsom.ForeignAttributes; |
| import com.sun.xml.xsom.XSComponent; |
| |
| import org.xml.sax.Locator; |
| |
| /** |
| * Mutable {@link ClassInfo} representation. |
| * |
| * <p> |
| * Schema parsers build these objects. |
| * |
| * @author Kohsuke Kawaguchi |
| */ |
| public final class CClassInfo extends AbstractCElement implements ClassInfo<NType,NClass>, CClassInfoParent, CClass, NClass { |
| |
| @XmlIDREF |
| private CClass baseClass; |
| |
| /** |
| * List of all subclasses, together with {@link #nextSibling}. |
| * |
| * If this class has no sub-class, this field is null. Otherwise, |
| * this field points to a sub-class of this class. From there you can enumerate |
| * all the sub-classes by using {@link #nextSibling}. |
| */ |
| private CClassInfo firstSubclass; |
| |
| /** |
| * @see #firstSubclass |
| */ |
| private CClassInfo nextSibling = null; |
| |
| /** |
| * @see #getTypeName() |
| */ |
| private final QName typeName; |
| |
| /** |
| * Custom {@link #getSqueezedName() squeezed name}, if any. |
| */ |
| private /*almost final*/ @Nullable String squeezedName; |
| |
| /** |
| * If this class also gets {@link XmlRootElement}, the class name. |
| */ |
| private final @Nullable QName elementName; |
| |
| private boolean isOrdered = true; |
| |
| private final List<CPropertyInfo> properties = new ArrayList<>(); |
| |
| /** |
| * TODO: revisit this design. |
| * we should at least do a basic encapsulation to avoid careless |
| * mistakes. Maybe we should even differ the javadoc generation |
| * by queueing runners. |
| */ |
| public String javadoc; |
| |
| @XmlIDREF |
| private final CClassInfoParent parent; |
| |
| /** |
| * short name. |
| */ |
| public final String shortName; |
| |
| /** |
| * Optional user-specified implementation override class. |
| */ |
| private @Nullable String implClass; |
| |
| /** |
| * The {@link Model} object to which this bean belongs. |
| */ |
| public final Model model; |
| |
| /** |
| * @see #hasAttributeWildcard() |
| */ |
| private boolean hasAttributeWildcard; |
| |
| |
| public CClassInfo(Model model,JPackage pkg, String shortName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) { |
| this(model,model.getPackage(pkg),shortName,location,typeName,elementName,source,customizations); |
| } |
| |
| public CClassInfo(Model model,CClassInfoParent p, String shortName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) { |
| super(model,source,location,customizations); |
| this.model = model; |
| this.parent = p; |
| this.shortName = model.allocator.assignClassName(parent,shortName); |
| this.typeName = typeName; |
| this.elementName = elementName; |
| |
| Language schemaLanguage = model.options.getSchemaLanguage(); |
| if ((schemaLanguage != null) && |
| (schemaLanguage.equals(Language.XMLSCHEMA) || schemaLanguage.equals(Language.WSDL))) { |
| BIFactoryMethod factoryMethod = Ring.get(BGMBuilder.class).getBindInfo(source).get(BIFactoryMethod.class); |
| if(factoryMethod!=null) { |
| factoryMethod.markAsAcknowledged(); |
| this.squeezedName = factoryMethod.name; |
| } |
| } |
| |
| model.add(this); |
| } |
| |
| public CClassInfo(Model model,JCodeModel cm, String fullName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) { |
| super(model,source,location,customizations); |
| this.model = model; |
| int idx = fullName.lastIndexOf('.'); |
| if(idx<0) { |
| this.parent = model.getPackage(cm.rootPackage()); |
| this.shortName = model.allocator.assignClassName(parent,fullName); |
| } else { |
| this.parent = model.getPackage(cm._package(fullName.substring(0,idx))); |
| this.shortName = model.allocator.assignClassName(parent,fullName.substring(idx+1)); |
| } |
| this.typeName = typeName; |
| this.elementName = elementName; |
| |
| model.add(this); |
| } |
| |
| @Override |
| public boolean hasAttributeWildcard() { |
| return hasAttributeWildcard; |
| } |
| |
| public void hasAttributeWildcard(boolean hasAttributeWildcard) { |
| this.hasAttributeWildcard = hasAttributeWildcard; |
| } |
| |
| @Override |
| public boolean hasSubClasses() { |
| return firstSubclass!=null; |
| } |
| |
| /** |
| * Returns true if a new attribute wildcard property needs to be |
| * declared on this class. |
| */ |
| @Override |
| public boolean declaresAttributeWildcard() { |
| return hasAttributeWildcard && !inheritsAttributeWildcard(); |
| } |
| |
| /** |
| * Returns true if this class inherits a wildcard attribute property |
| * from its ancestor classes. |
| */ |
| @Override |
| public boolean inheritsAttributeWildcard() { |
| if (getRefBaseClass() != null) { |
| CClassRef cref = (CClassRef)baseClass; |
| |
| for(ForeignAttributes foreignAttributes: cref.getSchemaComponent().getForeignAttributes()) { |
| if(foreignAttributes.getLength() > 0) { |
| return true; |
| } |
| } |
| } else { |
| for( CClassInfo c=getBaseClass(); c!=null; c=c.getBaseClass() ) { |
| if(c.hasAttributeWildcard) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| @Override |
| public NClass getClazz() { |
| return this; |
| } |
| |
| @Override |
| public CClassInfo getScope() { |
| return null; |
| } |
| |
| @XmlID |
| @Override |
| public String getName() { |
| return fullName(); |
| } |
| |
| /** |
| * Returns the "squeezed name" of this bean token. |
| * <p> |
| * The squeezed name of a bean is the concatenation of |
| * the names of its outer classes and itself. |
| * <p> |
| * Thus if the bean is "org.acme.foo.Bean", then the squeezed name is "Bean", |
| * if the bean is "org.acme.foo.Outer1.Outer2.Bean", then "Outer1Outer2Bean". |
| * <p> |
| * This is used by the code generator |
| */ |
| @XmlElement |
| public String getSqueezedName() { |
| if (squeezedName != null) return squeezedName; |
| return calcSqueezedName.onBean(this); |
| } |
| |
| private static final CClassInfoParent.Visitor<String> calcSqueezedName = new Visitor<>() { |
| @Override |
| public String onBean(CClassInfo bean) { |
| return bean.parent.accept(this) + bean.shortName; |
| } |
| |
| @Override |
| public String onElement(CElementInfo element) { |
| return element.parent.accept(this) + element.shortName(); |
| } |
| |
| @Override |
| public String onPackage(JPackage pkg) { |
| return ""; |
| } |
| }; |
| |
| /** |
| * Returns a mutable list. |
| */ |
| @Override |
| public List<CPropertyInfo> getProperties() { |
| return properties; |
| } |
| |
| @Override |
| public boolean hasValueProperty() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Gets a propery by name. |
| */ |
| @Override |
| public CPropertyInfo getProperty(String name) { |
| // TODO: does this method need to be fast? |
| for( CPropertyInfo p : properties ) |
| if(p.getName(false).equals(name)) |
| return p; |
| return null; |
| } |
| |
| @Override |
| public boolean hasProperties() { |
| return !getProperties().isEmpty(); |
| } |
| |
| @Override |
| public boolean isElement() { |
| return elementName!=null; |
| } |
| |
| /** |
| * Guaranteed to return this. |
| */ |
| @Deprecated |
| @Override |
| public CNonElement getInfo() { |
| return this; |
| } |
| |
| @Override |
| public Element<NType,NClass> asElement() { |
| if(isElement()) |
| return this; |
| else |
| return null; |
| } |
| |
| @Override |
| public boolean isOrdered() { |
| return isOrdered; |
| } |
| |
| /** |
| * @deprecated |
| * if you are calling this method directly, you must be doing something wrong. |
| */ |
| @Deprecated |
| @Override |
| public boolean isFinal() { |
| return false; |
| } |
| |
| public void setOrdered(boolean value) { |
| isOrdered = value; |
| } |
| |
| @Override |
| public QName getElementName() { |
| return elementName; |
| } |
| |
| @Override |
| public QName getTypeName() { |
| return typeName; |
| } |
| |
| @Override |
| public boolean isSimpleType() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Returns the FQCN of this bean. |
| */ |
| @Override |
| public String fullName() { |
| String r = parent.fullName(); |
| if(r.length()==0) return shortName; |
| else return r+'.'+shortName; |
| } |
| |
| public CClassInfoParent parent() { |
| return parent; |
| } |
| |
| public void setUserSpecifiedImplClass(String implClass) { |
| assert this.implClass==null; |
| assert implClass!=null; |
| this.implClass = implClass; |
| } |
| |
| public String getUserSpecifiedImplClass() { |
| return implClass; |
| } |
| |
| |
| /** |
| * Adds a new property. |
| */ |
| public void addProperty(CPropertyInfo prop) { |
| if(prop.ref().isEmpty()) |
| // this property isn't contributing anything |
| // this happens when you try to map an empty sequence to a property |
| return; |
| prop.setParent(this); |
| properties.add(prop); |
| } |
| |
| /** |
| * This method accepts both (which means the base class |
| * is also generated), or {@link CClassRef} (which means the base class is |
| * already generated and simply referenced.) |
| * |
| * The latter is treated somewhat special --- from the rest of the model |
| * this external base class is invisible. This modeling might need more |
| * thoughts to get right. |
| */ |
| public void setBaseClass(CClass base) { |
| assert baseClass==null; |
| assert base!=null; |
| baseClass = base; |
| |
| assert nextSibling==null; |
| if (base instanceof CClassInfo) { |
| CClassInfo realBase = (CClassInfo) base; |
| this.nextSibling = realBase.firstSubclass; |
| realBase.firstSubclass = this; |
| } |
| } |
| |
| /** |
| * This inherited version returns null if this class extends from {@link CClassRef}. |
| * |
| * @see #getRefBaseClass() |
| */ |
| @Override |
| public CClassInfo getBaseClass() { |
| if (baseClass instanceof CClassInfo) { |
| return (CClassInfo) baseClass; |
| } else { |
| return null; |
| } |
| } |
| |
| public CClassRef getRefBaseClass() { |
| if (baseClass instanceof CClassRef) { |
| return (CClassRef) baseClass; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Enumerates all the sub-classes of this class. |
| */ |
| public Iterator<CClassInfo> listSubclasses() { |
| return new Iterator<>() { |
| CClassInfo cur = firstSubclass; |
| |
| @Override |
| public boolean hasNext() { |
| return cur != null; |
| } |
| |
| @Override |
| public CClassInfo next() { |
| CClassInfo r = cur; |
| cur = cur.nextSibling; |
| return r; |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| |
| @Override |
| public CClassInfo getSubstitutionHead() { |
| CClassInfo c=getBaseClass(); |
| while(c!=null && !c.isElement()) |
| c=c.getBaseClass(); |
| return c; |
| } |
| |
| |
| /** |
| * Interfaces to be implemented. |
| * Lazily constructed. |
| */ |
| private Set<JClass> _implements = null; |
| |
| public void _implements(JClass c) { |
| if(_implements==null) |
| _implements = new HashSet<>(); |
| _implements.add(c); |
| } |
| |
| |
| /** Constructor declarations. array of {@link Constructor}s. */ |
| private final List<Constructor> constructors = new ArrayList<>(1); |
| |
| /** Creates a new constructor declaration and adds it. */ |
| public void addConstructor( String... fieldNames ) { |
| constructors.add(new Constructor(fieldNames)); |
| } |
| |
| /** list all constructor declarations. */ |
| public Collection<? extends Constructor> getConstructors() { |
| return constructors; |
| } |
| |
| @Override |
| public <T> T accept(Visitor<T> visitor) { |
| return visitor.onBean(this); |
| } |
| |
| @Override |
| public JPackage getOwnerPackage() { |
| return parent.getOwnerPackage(); |
| } |
| |
| @Override |
| public NClass getType() { |
| return this; |
| } |
| |
| @Override |
| public JClass toType(Outline o, Aspect aspect) { |
| switch(aspect) { |
| case IMPLEMENTATION: |
| return o.getClazz(this).implRef; |
| case EXPOSED: |
| return o.getClazz(this).ref; |
| default: |
| throw new IllegalStateException(); |
| } |
| } |
| |
| @Override |
| public boolean isBoxedType() { |
| return false; |
| } |
| |
| @Override |
| public String toString() { |
| return fullName(); |
| } |
| } |