/******************************************************************************* * Copyright (c) 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.bpel.validator; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xsd.XSDAttributeDeclaration; import org.eclipse.xsd.XSDAttributeGroupDefinition; import org.eclipse.xsd.XSDAttributeUse; import org.eclipse.xsd.XSDAttributeUseCategory; import org.eclipse.xsd.XSDComplexTypeContent; import org.eclipse.xsd.XSDComplexTypeDefinition; import org.eclipse.xsd.XSDConstraint; import org.eclipse.xsd.XSDElementDeclaration; import org.eclipse.xsd.XSDFeature; import org.eclipse.xsd.XSDModelGroup; import org.eclipse.xsd.XSDModelGroupDefinition; import org.eclipse.xsd.XSDParticle; import org.eclipse.xsd.XSDParticleContent; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.XSDSimpleTypeDefinition; import org.eclipse.xsd.XSDTypeDefinition; import org.eclipse.xsd.util.XSDConstants; import org.eclipse.xsd.util.XSDUtil; /** * Collection of utility methods for dealing with navigation of the XSD model */ public class XSDUtils { // singleton lists of XSD simple type definitions for supported primitives (see getPrimitives()) and // all xsd primitives (see getAdvancedPrimitives()) respectively private static List<XSDSimpleTypeDefinition> advancedPrimitives = new ArrayList<XSDSimpleTypeDefinition> (); private static final AtomicBoolean advancedPrimitiveWasSet = new AtomicBoolean( false ); // XSD short list -- these are the types presented to the user by default, rather than inundating them with // all the available types private static List<String> xsdShortList = new ArrayList<String>(); static { xsdShortList.add("string"); //$NON-NLS-1$ xsdShortList.add("int"); //$NON-NLS-1$ xsdShortList.add("double"); //$NON-NLS-1$ xsdShortList.add("date"); //$NON-NLS-1$ xsdShortList.add("time"); //$NON-NLS-1$ xsdShortList.add("dateTime"); //$NON-NLS-1$ xsdShortList.add("boolean"); //$NON-NLS-1$ xsdShortList.add("hexBinary"); //$NON-NLS-1$ xsdShortList.add("float"); //$NON-NLS-1$ } // A list of all supported XSD types. Usually the user will not be presented with the full list, but // rather with the xsd short list private static List<String> supportedPrimitives = new ArrayList<String>(); static { supportedPrimitives.add("anyType"); //$NON-NLS-1$ supportedPrimitives.add("anyURI"); //$NON-NLS-1$ supportedPrimitives.add("base64Binary"); //$NON-NLS-1$ supportedPrimitives.add("boolean"); //$NON-NLS-1$ supportedPrimitives.add("byte"); //$NON-NLS-1$ supportedPrimitives.add("date"); //$NON-NLS-1$ supportedPrimitives.add("dateTime"); //$NON-NLS-1$ supportedPrimitives.add("decimal"); //$NON-NLS-1$ supportedPrimitives.add("double"); //$NON-NLS-1$ supportedPrimitives.add("duration"); //$NON-NLS-1$ supportedPrimitives.add("ENTITIES"); //$NON-NLS-1$ supportedPrimitives.add("ENTITY"); //$NON-NLS-1$ supportedPrimitives.add("float"); //$NON-NLS-1$ supportedPrimitives.add("gDay"); //$NON-NLS-1$ supportedPrimitives.add("gMonth"); //$NON-NLS-1$ supportedPrimitives.add("gMonthDay"); //$NON-NLS-1$ supportedPrimitives.add("gYear"); //$NON-NLS-1$ supportedPrimitives.add("gYearMonth"); //$NON-NLS-1$ supportedPrimitives.add("hexBinary"); //$NON-NLS-1$ supportedPrimitives.add("ID"); //$NON-NLS-1$ supportedPrimitives.add("IDREF"); //$NON-NLS-1$ supportedPrimitives.add("IDREFS"); //$NON-NLS-1$ supportedPrimitives.add("int"); //$NON-NLS-1$ supportedPrimitives.add("integer"); //$NON-NLS-1$ supportedPrimitives.add("language"); //$NON-NLS-1$ supportedPrimitives.add("long"); //$NON-NLS-1$ supportedPrimitives.add("Name"); //$NON-NLS-1$ supportedPrimitives.add("NCName"); //$NON-NLS-1$ supportedPrimitives.add("negativeInteger"); //$NON-NLS-1$ supportedPrimitives.add("NMTOKEN"); //$NON-NLS-1$ supportedPrimitives.add("NMTOKENS"); //$NON-NLS-1$ supportedPrimitives.add("nonNegativeInteger"); //$NON-NLS-1$ supportedPrimitives.add("nonPositiveInteger"); //$NON-NLS-1$ supportedPrimitives.add("normalizedString"); //$NON-NLS-1$ supportedPrimitives.add("NOTATION"); //$NON-NLS-1$ supportedPrimitives.add("positiveInteger"); //$NON-NLS-1$ supportedPrimitives.add("QName"); //$NON-NLS-1$ supportedPrimitives.add("short"); //$NON-NLS-1$ supportedPrimitives.add("string"); //$NON-NLS-1$ supportedPrimitives.add("time"); //$NON-NLS-1$ supportedPrimitives.add("token"); //$NON-NLS-1$ supportedPrimitives.add("unsignedByte"); //$NON-NLS-1$ supportedPrimitives.add("unsignedInt"); //$NON-NLS-1$ supportedPrimitives.add("unsignedLong"); //$NON-NLS-1$ supportedPrimitives.add("unsignedShort"); //$NON-NLS-1$ } /** * Like getPrimitives(), this returns a list of XSDSimpleTypeDefinition. However where getPrimitives() * returns the basic set supported by the editor, getAdvancedPrimitives returns every known XSD * primitive type. * * @return list of simple type definitions. */ public static List<XSDSimpleTypeDefinition> getAdvancedPrimitives() { // (VZ) FIXME: use a singleton instance of a set of static methods if( ! advancedPrimitiveWasSet.get()) { // Get the schema for schemas instance to use when resolving primitives XSDSchema schemaForSchemas = XSDUtil.getSchemaForSchema(XSDConstants.SCHEMA_FOR_SCHEMA_URI_2001); // Start adding the simple types using the supportedPrimitives list for(String typeName : supportedPrimitives) { XSDSimpleTypeDefinition type = schemaForSchemas.resolveSimpleTypeDefinition(typeName); advancedPrimitives.add(type); } // Return primitives in alpha order Collections.sort(advancedPrimitives, new Comparator<XSDSimpleTypeDefinition>() { public int compare(XSDSimpleTypeDefinition o1, XSDSimpleTypeDefinition o2) { if(o1 == null || o2 == null || o1.getName() == null) return 0; return o1.getName().compareToIgnoreCase(o2.getName()); } }); advancedPrimitiveWasSet.set( true ); } return advancedPrimitives; } /** * Retrieves all the root Data types defined in the schema including complex types, user-defined simple types and anonymous * complex types. If there's an anonymous complex type definition (from a root element declaration) then we return * the element declaration's anonymous type. * @param schema * @return list of type definitions. */ public static List<XSDTypeDefinition> getAllDataTypes(XSDSchema schema) { if (schema==null) { return Collections.emptyList(); } List<XSDTypeDefinition> bos = new ArrayList<XSDTypeDefinition>(); for (Object item : schema.getContents()) { if (item instanceof XSDTypeDefinition) { bos.add( (XSDTypeDefinition) item); } else if (item instanceof XSDElementDeclaration) { XSDElementDeclaration element = (XSDElementDeclaration) item; if (element.getAnonymousTypeDefinition() instanceof XSDComplexTypeDefinition) { bos.add(element.getAnonymousTypeDefinition()); } } } return bos; } /** * Given a BO (XSD Complex Type), return a list of the attributes * within the complexType. * @param bo * @return List of XSDAttributeDeclaration */ public static List<XSDAttributeDeclaration> getChildAttributes (XSDComplexTypeDefinition bo) { EList attrContents = bo.getAttributeContents(); List<XSDAttributeDeclaration> attrs = new ArrayList<XSDAttributeDeclaration>(); for (int i=0; i< attrContents.size(); i++) { Object next = attrContents.get(i); // Attribute contents may include actual attribute declarations (wrapped in XSDAttributeUses) or // attribute group definitions, containing bundles of attributes if(next instanceof XSDAttributeUse) { attrs.add( ((XSDAttributeUse) next).getContent().getResolvedAttributeDeclaration() ); } else if (next instanceof XSDAttributeGroupDefinition) { // Add these attributes to the end of attrContents to be processed in turn XSDAttributeGroupDefinition attrGroup = (XSDAttributeGroupDefinition) next; if(attrGroup.getResolvedAttributeGroupDefinition() != null) attrContents.addAll(attrGroup.getResolvedAttributeGroupDefinition().getAttributeUses()); } } return attrs; } /** * Given a BO (XSD Complex Type), return a list of the XSDFeatures * within the complexType's modelGroup (sequence, choice, etc.) * @param bo * @return list of features */ public static List<XSDParticleContent> getChildElements (XSDComplexTypeDefinition bo) { List<XSDParticleContent> children = new ArrayList<XSDParticleContent>(); children.addAll(getChildElements(getModelGroup(bo))); if(bo.getBaseTypeDefinition() instanceof XSDComplexTypeDefinition){ children.addAll(getChildElements(getModelGroup((XSDComplexTypeDefinition)bo.getBaseTypeDefinition()))); } return children; } /** * Given an XSDFeature, return a list of the XSDFeatures * within the complexType's modelGroup (sequence, choice, etc.) * @param elem * @return list of the XSDFeatures within the complexType's modelGroup */ public static List<XSDParticleContent> getChildElements(XSDFeature elem) { XSDComplexTypeDefinition cType = getResolvedComplexType(elem); if (cType != null) { return getChildElements(cType); } return Collections.emptyList(); } /** * Given a Model group, return a list of the XSDFeatures * declared within. * @param group * @return the child elements. */ public static List<XSDParticleContent> getChildElements (XSDModelGroup group) { if(group == null) { return new ArrayList<XSDParticleContent>(); } List<XSDParticleContent> children = new ArrayList<XSDParticleContent>(); for (XSDParticle next : group.getContents() ) { if(next.getContent() instanceof XSDFeature) { children.add(next.getContent()); } else if (next.getTerm() instanceof XSDModelGroup) { children.addAll(getChildElements((XSDModelGroup) next.getTerm())); } } return children; } /** * Given an XSD Complex Type Definition, return the model group containing * its child elements. * @param cType * @return the model group */ public static XSDModelGroup getModelGroup(XSDComplexTypeDefinition cType) { XSDParticle particle = cType.getComplexType(); // In cases where cType doesn't have a model group AND cType has a parent with a modelgroup, the // call above will rather unexpectedly give us cType's PARENT's model group, rather than the null we // might expect. We don't want that here, if the model group returned is null or belongs to someone // other than us, return null if (particle==null || particle.eContainer() != cType) { return null; } // get the model group Object particleContent = particle.getContent(); XSDModelGroup group = null; if (particleContent instanceof XSDModelGroupDefinition) { group = ((XSDModelGroupDefinition)particleContent).getResolvedModelGroupDefinition().getModelGroup(); } else if (particleContent instanceof XSDModelGroup) { group = (XSDModelGroup)particleContent; } if (group == null) { return null; } // if the content of the complex type is empty then the content // must be in the complexContent, ie. we're extending another BO. // if the group and the type are not in the same resource then // we are extending another BO and we don't want to show inherited // attributes. if ( group.getContents().isEmpty() || group.eResource() != cType.eResource()) { // if we are extending another BO then get the elements // we are adding if (cType.getBaseType()!=null) { XSDComplexTypeContent content = cType.getContent(); if (content instanceof XSDParticle) { particleContent = ((XSDParticle)content).getContent(); if (particleContent instanceof XSDModelGroupDefinition) { group = ((XSDModelGroupDefinition)particleContent).getResolvedModelGroupDefinition().getModelGroup(); } else if (particleContent instanceof XSDModelGroup) { group = (XSDModelGroup)particleContent; } } } } return group; } /** * Return the type definition for the primitive with name xsdName (note, this is not the human-readable * name, but the actual XSD type name.) Return null if a type with this name is not in the list of * all primitives * @param xsdName * @return the simple type definition with the given xsdName */ public static XSDSimpleTypeDefinition getPrimitive(String xsdName) { for(XSDSimpleTypeDefinition next : getAdvancedPrimitives() ) { if(next.getName().equals(xsdName)) { return next; } } return null; } /** * Return a collection of all types referenced by the attributes (XSDFeatures) of source. This * method is non-recursive (i.e. the list only contains direct references, not references-of-references) * Will not return null, but may return an empty set. This will not return a BO reference if the file * has been deleted (or just doesn't exist). * @param source The complex type to examine for references * @return a Collection of XSDComplexTypeDefinition instances -- no duplicates */ public static Collection<XSDTypeDefinition> getReferencedTypes(XSDComplexTypeDefinition source) { if(source == null) { return Collections.emptySet(); } List<XSDTypeDefinition> results = new ArrayList<XSDTypeDefinition>(); XSDFeature element = null; XSDComplexTypeDefinition elementType = null; for(Iterator<XSDParticleContent> i = getChildElements(source).iterator(); i.hasNext(); ) { element = (XSDFeature) i.next(); elementType = getResolvedComplexType(element); if(elementType != null && !results.contains(elementType) && !XSDConstants.isSchemaForSchemaNamespace(elementType.getTargetNamespace())) results.add(elementType); } return results; } /** * Return a collection of all types referenced by the attributes (XSDFeatures) of source. This * method is non-recursive (i.e. the list only contains direct references, not references-of-references) * Will not return null, but may return an empty set. This will return a BO reference if the file * has been deleted (or just doesn't exist). * @param source The complex type to examine for references * @return a Collection of XSDTypeDefinition (could be complex or simple type) instances -- no duplicates */ public static Collection<XSDTypeDefinition> getAllReferencedTypes(XSDComplexTypeDefinition source) { return getAllReferencedTypes(source, false); } /** * Return a collection of all types referenced by the attributes (XSDFeatures) of source. This * method is non-recursive (i.e. the list only contains direct references, not references-of-references) * Will not return null, but may return an empty set. This will return a BO reference if the file * has been deleted (or just doesn't exist). * @param source The complex type to examine for references * @param includeAnonymous if true, the returned list will include anonymous inlined types as well. These * are not technically "referenced", however it allows this method to be used as a way to get all non-primitive * types used in any way by source * @return a Collection of XSDTypeDefinition (could be complex or simple type) instances -- no duplicates */ public static Collection<XSDTypeDefinition> getAllReferencedTypes(XSDComplexTypeDefinition source, boolean includeAnonymous) { if (source == null) { return Collections.emptySet(); } List<XSDTypeDefinition> results = new ArrayList<XSDTypeDefinition>(); XSDTypeDefinition elementType = null; for (Iterator<XSDParticleContent> i = getChildElements(source).iterator(); i.hasNext();) { XSDFeature next = (XSDFeature) i.next(); elementType = getResolvedType(next); // Only add non-null, non-duplicate, non-primitive types. If includeAnonymous is false, // anonymous types should be filtered out as well if( elementType != null && !results.contains(elementType) && !XSDConstants.isSchemaForSchemaNamespace(elementType.getTargetNamespace()) && (includeAnonymous || elementType.eContainer() != next) ) { results.add(elementType); } } return results; } /** * Given an element, return its complex type, or null if it does not have a complex type. This is * slightly more complicated than just calling getType() since an element may not have a type at all, it * may reference another element. * @param feature * * @return the complex type of the element (if any) */ public static XSDComplexTypeDefinition getResolvedComplexType(XSDFeature feature) { // The contents of this method have been adapted to the more general getResolvedType, // but this method is maintained for compatibility and convenience XSDTypeDefinition resolvedType = getResolvedType(feature); if(resolvedType instanceof XSDComplexTypeDefinition) return (XSDComplexTypeDefinition) resolvedType; return null; } /** * Given an element, return its type, or null if it does not have a type. This is * slightly more complicated than just calling getType() since an element may not have * a type at all, it may reference another element. * @param feature * @return the type of the element */ public static XSDTypeDefinition getResolvedType(XSDFeature feature) { // Special case of elements referencing stale XSD complex types if (feature instanceof XSDElementDeclaration && ((XSDElementDeclaration) feature).getTypeDefinition() instanceof XSDComplexTypeDefinition) { XSDElementDeclaration element = (XSDElementDeclaration) feature; // We have a type, but types can be proxies, and proxies can become // stale if the referenced // type changes, so before we return it, re-resolve the proxy and // then return it XSDComplexTypeDefinition oldType = (XSDComplexTypeDefinition) element.getTypeDefinition(); EObject newType = EcoreUtil.resolve(element.getTypeDefinition(), element); if (oldType != newType) { // We only return the resolved type if the name and the namespace has not changed. Changing the name // and namespace is essentially an unresolved BO. String oldName = oldType.getName(); String newName = ((XSDTypeDefinition)newType).getName(); String oldTNS = oldType.getTargetNamespace(); String newTNS = ((XSDTypeDefinition)newType).getTargetNamespace(); if ( ((oldName==newName) || (oldName!=null && oldName.equals(newName))) && ((oldTNS==newTNS) || (oldTNS!=null && oldTNS.equals(newTNS))) ) element.setTypeDefinition((XSDTypeDefinition) newType); } return element.getTypeDefinition(); } else if (feature.getType() != null) { return feature.getType(); } else if (feature.getResolvedFeature() != null && feature.getResolvedFeature().getType() != null) { // We reference another element return feature.getResolvedFeature().getType(); } else { return null; } } /** * Return the base type from which this type inherits - that is, all xsd types are * either xsd:anyType or xsd:anySimpleType at the topmost level of inheritance, so return the second * topmost level of type's inheritance. The first specific type from which type inherits. * @param type * @return the root type definition */ public static XSDTypeDefinition getRootType(XSDTypeDefinition type) { if(type == null) return null; XSDTypeDefinition baseType = type.getBaseType(); while(baseType != null && !XSDConstants.isAnySimpleType(baseType) && !XSDConstants.isAnyType(baseType)) { // walk one more step up the hierarchy type = baseType; baseType = type.getBaseType(); } // Since baseType, type's immediate parent, broke the while condition, we know that type is now // as high up the tree as we want to be return type; } /** * Given a schema, return the list of XSDSimpleTypeDefinitions found within it. Will not return null, * but may return an empty list * @param schema * @return the user defined simple types. */ public static List<XSDSimpleTypeDefinition> getUserDefinedSimpleTypes(XSDSchema schema) { if(schema == null) { return Collections.emptyList(); } List<XSDSimpleTypeDefinition> result = new ArrayList<XSDSimpleTypeDefinition>(); for(Object next : schema.getContents()) { if(next instanceof XSDSimpleTypeDefinition) result.add((XSDSimpleTypeDefinition) next); } return result; } /** * Return true if type is a descendant of a primitive xsd type. Will not return true for primitives * themselves. * @param type * @return if type is a descendant of a primitive XSD type. */ public static boolean isRestrictedPrimitiveType(XSDTypeDefinition type) { if(type instanceof XSDComplexTypeDefinition) { return false; } XSDTypeDefinition baseType = getRootType(type); return getAdvancedPrimitives().contains(baseType); } /** * Gets the "minOccurs" attribute value for the given XSDFeature, if * there is none then it returns the default 1. * @param xsdElem * @return min occurs */ public static int getMinOccurs (XSDFeature xsdElem) { if (xsdElem.eContainer() instanceof XSDAttributeUse) { return (((XSDAttributeUse)xsdElem.eContainer()).getUse()==XSDAttributeUseCategory.REQUIRED_LITERAL?1:0); } XSDParticle particle = (XSDParticle)xsdElem.eContainer(); int min = 1; if (particle.isSetMinOccurs()) min = particle.getMinOccurs(); return min; } /** * Gets the "maxOccurs" attribute value for the given XSDFeature, if * there is none then it returns the default 1. * @param xsdElem * @return max occurs */ public static int getMaxOccurs(XSDFeature xsdElem) { int max = 1; // not a particle means an attribute use. attributes are maxed at 1. if ( !(xsdElem.eContainer() instanceof XSDParticle) ) return max; XSDParticle particle = (XSDParticle)xsdElem.eContainer(); if (particle.isSetMaxOccurs()) max = particle.getMaxOccurs(); return max; } static final String EMPTY_STRING = ""; //$NON-NLS-1$ /** * Gets the "default" attribute value for the given XSDFeature, if there * is none then it returns an empty string. * @param xsdElem * @return the default value string */ public static String getDefaultValue(XSDFeature xsdElem) { XSDConstraint constraint = null; if (xsdElem instanceof XSDAttributeDeclaration) { // attribute declarations store their default values in // their containers (attribute uses) XSDAttributeUse use = (XSDAttributeUse)xsdElem.getContainer(); if (use.isSetConstraint()) { constraint = use.getConstraint(); } if (constraint!=null && constraint.equals(XSDConstraint.DEFAULT_LITERAL)) { if (use.getLexicalValue()!=null) { return use.getLexicalValue(); } return EMPTY_STRING; } } else if (xsdElem instanceof XSDElementDeclaration) { if (xsdElem.isSetConstraint()) constraint = xsdElem.getConstraint(); if (constraint!=null && constraint.equals(XSDConstraint.DEFAULT_LITERAL)) { if (xsdElem.getLexicalValue()!=null) { return xsdElem.getLexicalValue(); } return EMPTY_STRING; } } return EMPTY_STRING; } /** * Return the enclosing Complex Type definition. * @param component * @return Return the enclosing Complex Type definition. */ public static XSDComplexTypeDefinition getEnclosingTypeDefinition(EObject component) { if (component == null) { return null; } if (component instanceof XSDComplexTypeDefinition) return (XSDComplexTypeDefinition)component; return getEnclosingTypeDefinition(component.eContainer()); } }