/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.webservice.procedure; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.xsd.XSDAttributeDeclaration; import org.eclipse.xsd.XSDAttributeGroupContent; import org.eclipse.xsd.XSDAttributeGroupDefinition; import org.eclipse.xsd.XSDAttributeUse; import org.eclipse.xsd.XSDAttributeUseCategory; import org.eclipse.xsd.XSDComplexTypeDefinition; import org.eclipse.xsd.XSDCompositor; import org.eclipse.xsd.XSDConcreteComponent; import org.eclipse.xsd.XSDContentTypeCategory; import org.eclipse.xsd.XSDElementDeclaration; import org.eclipse.xsd.XSDFeature; import org.eclipse.xsd.XSDForm; import org.eclipse.xsd.XSDModelGroup; import org.eclipse.xsd.XSDModelGroupDefinition; import org.eclipse.xsd.XSDNamedComponent; import org.eclipse.xsd.XSDParticle; import org.eclipse.xsd.XSDParticleContent; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.XSDSimpleTypeDefinition; import org.eclipse.xsd.XSDTerm; import org.eclipse.xsd.XSDTypeDefinition; /** * Instances of this class represent the metadata for a component in a possible instance of an XML Schema (XSD) (i.e., a component * in an XML instance document). This class is <em>not</em> concerned about anything related to data values in an instance * document. The parent-child properties of instances of this class represent the possible resolved "paths" that can be taken in * an instance document, such that only a single iteration of recursive schema definitions are represented. * * @since 8.0 */ public class XsdInstanceNode { // =========================================================================================================================== // Constants // =========================================================================================================================== // Static Variables private static XSDComplexTypeDefinition anyType; // =========================================================================================================================== // Variables private XSDConcreteComponent xsdComponent; private XsdInstanceNode[] children; private XsdInstanceNode parent; private boolean selectable, selected, recursive, hasSelectableChildren; // =========================================================================================================================== // Constructors /** * Creates a root XsdInstanceNode. * * @param element * Must not be <code>null</code>. * @since 5.0.1 */ public XsdInstanceNode(XSDElementDeclaration element) { this(element, null); } /** * @param component * Must not be <code>null</code>. * @param parent * If <code>component</code> is not an <code>XSDElementDeclaration</code>, must not be <code>null</code>. * @since 5.0.1 */ public XsdInstanceNode(XSDConcreteComponent component, XsdInstanceNode parent) { if (component == null) { throw new IllegalArgumentException(); } if (!(component instanceof XSDElementDeclaration) && parent == null) { throw new IllegalArgumentException(); } this.xsdComponent = component; this.parent = parent; if (component instanceof XSDParticle) { // Set XSD component to "real" value component = ((XSDParticle)component).getTerm(); } // Determine if node is selectable if (component instanceof XSDElementDeclaration) { XSDElementDeclaration elem = (XSDElementDeclaration)component; if (elem.isElementDeclarationReference()) { elem = elem.getResolvedElementDeclaration(); } XSDTypeDefinition type = elem.getType(); if( type != null ) { if (type instanceof XSDSimpleTypeDefinition) { this.selectable = true; } else { XSDComplexTypeDefinition complexType = (XSDComplexTypeDefinition)type; if (complexType.isMixed() || complexType.getContentTypeCategory() == XSDContentTypeCategory.SIMPLE_LITERAL) { this.selectable = true; } } } } else if (component instanceof XSDAttributeUse) { this.selectable = true; } // Determine if node is recursive and, if selectable, mark parents as having selectable children while (parent != null) { XSDConcreteComponent ancestorComp = parent.xsdComponent; if (ancestorComp instanceof XSDParticle) { ancestorComp = ((XSDParticle)ancestorComp).getTerm(); } if (component == ancestorComp) { parent.recursive = this.recursive = true; } if (this.selectable) { parent.hasSelectableChildren = true; } parent = parent.parent; } // Resolve anyType if (anyType == null) { anyType = this.xsdComponent.getSchema().getSchemaForSchema().resolveComplexTypeDefinition("anyType"); //$NON-NLS-1$ } } // =========================================================================================================================== // Methods /** * @since 5.0.1 */ private void addAttributes(List contents, List children) { for (Iterator iter = contents.iterator(); iter.hasNext();) { XSDAttributeGroupContent content = (XSDAttributeGroupContent)iter.next(); if (content instanceof XSDAttributeUse) { if (((XSDAttributeUse)content).getUse() != XSDAttributeUseCategory.PROHIBITED_LITERAL) { children.add(new XsdInstanceNode(content, this)); } } else { XSDAttributeGroupDefinition group = (XSDAttributeGroupDefinition)content; if (group.isAttributeGroupDefinitionReference()) { group = group.getResolvedAttributeGroupDefinition(); } addAttributes(group.getContents(), children); } } // for } /** * @since 5.0.1 */ private void addElementChildren(XSDElementDeclaration element, List children) { if (element.isElementDeclarationReference()) { element = element.getResolvedElementDeclaration(); } XSDTypeDefinition type = element.getType(); if (type instanceof XSDComplexTypeDefinition && type != anyType) { addTypeAttributes((XSDComplexTypeDefinition)type, children); XSDParticle particle = type.getComplexType(); if (particle != null) { // Particle must represent a model group (i.e., for a compositor) addSequentialModelGroupChildren(particle, (XSDModelGroup)particle.getContent(), children); } } // else element must have simple type and therefore has no children } /** * @since 5.0.1 */ private void addModelGroupChildren(XSDModelGroup modelGroup, List children) { for (Iterator iter = modelGroup.getParticles().iterator(); iter.hasNext();) { XSDParticle particle = (XSDParticle)iter.next(); XSDParticleContent content = particle.getContent(); if (content instanceof XSDModelGroup) { addSequentialModelGroupChildren(particle, (XSDModelGroup)content, children); } else if (content instanceof XSDModelGroupDefinition) { XSDModelGroupDefinition group = (XSDModelGroupDefinition)content; if (group.isModelGroupDefinitionReference()) { group = group.getResolvedModelGroupDefinition(); } addSequentialModelGroupChildren(particle, group.getModelGroup(), children); } else if (content instanceof XSDElementDeclaration) { if (particle.getMaxOccurs() != 0) { children.add(new XsdInstanceNode(particle, this)); } } else { throw new RuntimeException("Unexpected content: " + content.getClass()); //$NON-NLS-1$ } } // for } /** * @since 5.0.1 */ private void addSequentialModelGroupChildren(XSDParticle particle, XSDModelGroup modelGroup, List children) { if (modelGroup.getCompositor() == XSDCompositor.CHOICE_LITERAL) { children.add(new XsdInstanceNode(particle, this)); } else { addModelGroupChildren(modelGroup, children); } } /** * @since 5.0.1 */ private void addTypeAttributes(XSDComplexTypeDefinition type, List children) { XSDTypeDefinition baseType = type.getBaseType(); if (baseType instanceof XSDComplexTypeDefinition && baseType != anyType) { addTypeAttributes((XSDComplexTypeDefinition)baseType, children); } addAttributes(type.getAttributeContents(), children); } /** * @since 5.0.1 */ public XsdInstanceNode findRecursionRoot() { XSDConcreteComponent comp = this.xsdComponent; if (comp instanceof XSDParticle) { comp = ((XSDParticle)comp).getTerm(); } for (XsdInstanceNode ancestor = this.parent; ancestor != null; ancestor = ancestor.parent) { if (ancestor.recursive) { XSDConcreteComponent ancestorComp = ancestor.xsdComponent; if (ancestorComp instanceof XSDParticle) { ancestorComp = ((XSDParticle)ancestorComp).getTerm(); } if (ancestorComp == comp) { return ancestor; } } } return null; } /** * @since 5.0.1 */ public XsdInstanceNode[] getChildren() { if (this.children == null) { List children = new ArrayList(); if (!this.recursive) { if (this.xsdComponent instanceof XSDElementDeclaration) { addElementChildren((XSDElementDeclaration)this.xsdComponent, children); } else if (this.xsdComponent instanceof XSDAttributeGroupContent) { } else if (this.xsdComponent instanceof XSDParticle) { XSDParticleContent content = ((XSDParticle)this.xsdComponent).getContent(); if (content instanceof XSDElementDeclaration) { addElementChildren((XSDElementDeclaration)content, children); } else if (content instanceof XSDModelGroupDefinition) { // Particle must represent a choice XSDModelGroupDefinition group = (XSDModelGroupDefinition)content; if (group.isModelGroupDefinitionReference()) { group = group.getResolvedModelGroupDefinition(); } addModelGroupChildren(group.getModelGroup(), children); } else if (content instanceof XSDModelGroup) { addModelGroupChildren((XSDModelGroup)content, children); } } else { throw new RuntimeException("Unexpected component: " + this.xsdComponent.getClass()); //$NON-NLS-1$ } } this.children = new XsdInstanceNode[children.size()]; children.toArray(this.children); } return this.children; } /** * @return The name of this node's schema component. * @since 5.0.1 */ public String getName() { XSDConcreteComponent comp = getResolvedXsdComponent(); if (comp instanceof XSDNamedComponent) { return ((XSDNamedComponent)comp).getName(); } // Must be a choice return ((XSDModelGroup)comp).getCompositor().getName(); } /** * @return Returns the parent. * @since 5.0.1 */ public XsdInstanceNode getParent() { return this.parent; } /** * @return Returns the xsdComponent. * @since 5.0.1 */ public XSDConcreteComponent getResolvedXsdComponent() { if (this.xsdComponent instanceof XSDElementDeclaration) { return this.xsdComponent; } if (this.xsdComponent instanceof XSDAttributeUse) { XSDAttributeDeclaration attr = ((XSDAttributeUse)this.xsdComponent).getAttributeDeclaration(); if (attr.isAttributeDeclarationReference()) { return attr.getResolvedAttributeDeclaration(); } return attr; } // Must be a particle XSDTerm term = ((XSDParticle)this.xsdComponent).getTerm(); if (term instanceof XSDModelGroupDefinition) { XSDModelGroupDefinition group = (XSDModelGroupDefinition)term; if (group.isModelGroupDefinitionReference()) { group = group.getResolvedModelGroupDefinition(); } return group.getModelGroup(); } if (term instanceof XSDModelGroup) { return term; } // Must be an element XSDElementDeclaration elem = (XSDElementDeclaration)term; if (elem.isElementDeclarationReference()) { return elem.getResolvedElementDeclaration(); } return elem; } /** * @return The target namespace of this node's schema component, or <code>null</code> if none is defined. * @since 5.0.1 */ public String getTargetNamespace() { return this.xsdComponent.getSchema().getTargetNamespace(); } /** * @return Returns the xsdComponent. * @since 5.0.1 */ public XSDConcreteComponent getXsdComponent() { return this.xsdComponent; } /** * @return Returns the hasSelectableChildren. * @since 5.0.1 */ public boolean hasSelectableChildren() { return this.hasSelectableChildren; } /** * @return True if this node's schema component is a reference. * @since 5.0.1 */ public boolean isLocallyDefined() { if (this.xsdComponent instanceof XSDElementDeclaration) { return false; } if (this.xsdComponent instanceof XSDAttributeUse) { return !((XSDAttributeUse)this.xsdComponent).getAttributeDeclaration().isAttributeDeclarationReference(); } // Must be a particle XSDTerm term = ((XSDParticle)this.xsdComponent).getTerm(); if (term instanceof XSDModelGroupDefinition) { return !((XSDModelGroupDefinition)term).isModelGroupDefinitionReference(); } if (term instanceof XSDModelGroup) { return true; } // Must be an element return !((XSDElementDeclaration)term).isElementDeclarationReference(); } /** * @return True if instances of this node's schema component must be namespace-qualified in instance documents. * @since 5.0.1 */ public boolean isNamespaceQualifiedInDocument() { if (isLocallyDefined()) { XSDConcreteComponent comp = getResolvedXsdComponent(); if (comp instanceof XSDFeature) { XSDFeature feature = (XSDFeature)comp; if (feature.isSetForm()) { return (feature.getForm() == XSDForm.QUALIFIED_LITERAL); } XSDSchema schema = feature.getSchema(); if (feature instanceof XSDAttributeDeclaration) { if (schema.isSetAttributeFormDefault()) { return (schema.getAttributeFormDefault() == XSDForm.QUALIFIED_LITERAL); } } else if (schema.isSetElementFormDefault()) { return (schema.getElementFormDefault() == XSDForm.QUALIFIED_LITERAL); } } return false; } return true; } /** * @return Returns the recursive. * @since 5.0.1 */ public boolean isRecursive() { return this.recursive; } /** * @return Returns the selectable. * @since 5.0.1 */ public boolean isSelectable() { return this.selectable; } /** * @return Returns the selected. * @since 5.0.1 */ public boolean isSelected() { return this.selected; } /** * @param selected * The selected to set. * @since 5.0.1 */ public void setSelected(boolean selected) { this.selected = selected; } /** * @see java.lang.Object#toString() * @since 5.0.1 */ @Override public String toString() { StringBuffer text = new StringBuffer(this.xsdComponent.toString()); text.append('#'); text.append(getResolvedXsdComponent()); return text.toString(); } }