blob: 4b04ed3ec87d26ad7a448706f10be45a98f62e78 [file] [log] [blame]
/*******************************************************************************
* 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());
}
}