blob: eff199b5758cf3adbc919df703c224ecb877f350 [file] [log] [blame]
/**
* Copyright (c) 2002-2007 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 - Initial API and implementation
*/
package org.eclipse.xsd.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDImport;
import org.eclipse.xsd.XSDInclude;
import org.eclipse.xsd.XSDRedefine;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.XSDSchemaContent;
import org.eclipse.xsd.XSDSchemaDirective;
import org.eclipse.xsd.XSDTypeDefinition;
// Various Eclipse, etools and EMF dependencies
// xsd:annotation elements are modeled in the DOM
/**
* XSDSchemaQueryTools is a collection of worker methods for performing
* simple queries or searches of schemas.
*
* <p>These are suitable to show examples of how to build convenience
* methods to more easily find components in a schema. They also
* serve as useful examples of usage of the library's functions.</p>
*
* <p>Note that these methods are somewhat simplistic, especially
* in terms of support for namespaces. To simplify the calling
* signatures, and to account for the 80% case where you don't have
* multiple namespaces, most method simply take a localName that's
* assumed to be correct in the current context. However it should
* be simple to copy & paste these methods and add namespaceURI's to
* each method if desired.</p>
*
* @author Shane_Curcuru@us.ibm.com
*/
public abstract class XSDSchemaQueryTools
{
/**
* Find typeDefinitions that derive from a named type.
*
* <p>This shows one way to query the schema for typeDefinitions
* and then how to find specific kinds of typeDefinitions.</p>
*
* @param schema to search for typeDefs
* @param namespace for the type derived from
* @param localName for the type derived from
* @return List of any XSDTypeDefinitions found
*/
public static List<XSDTypeDefinition> findTypesDerivedFrom(XSDSchema schema, String namespace, String localName)
{
if ((null == schema) || (null == localName))
{
throw new IllegalArgumentException("findTypesDerivedFrom called with null schema/localName");
}
ArrayList<XSDTypeDefinition> typesDerivedFrom = new ArrayList<XSDTypeDefinition>();
// A handy convenience method quickly gets all
// typeDefinitions within our schema; note that
// whether or not this returns types in included,
// imported, or redefined schemas is subject to change
List<XSDTypeDefinition> typedefs = schema.getTypeDefinitions();
for (XSDTypeDefinition typedef : typedefs)
{
// Walk the baseTypes from this typedef seeing if any
// of them match the requested one
if (isTypeDerivedFrom(typedef, namespace, localName))
{
// We found it, return the original one and continue
typesDerivedFrom.add(typedef);
continue;
}
}
return typesDerivedFrom;
}
/**
* Recursive worker method to find typeDefinitions that derive
* from a named type.
*
* <p>This is not a terribly time-efficient algorithm, but
* it does show usage of library methods. This works for
* simpleTypes; complexType support needs to be added.</p>
*
* @see #findTypesDerivedFrom(XSDSchema, String, String)
* @param typedef to see if it's derived from
* @param namespace for the type derived from
* @param localName for the type derived from
* @return true if it is; false otherwise
* @deprecated Use {@link XSDConstants#isOrIsDerivedFrom(XSDTypeDefinition, String, String)} instead.
*/
@Deprecated
public static boolean isTypeDerivedFrom(XSDTypeDefinition typedef, String namespace, String localName)
{
return XSDConstants.isOrIsDerivedFrom(typedef, localName, namespace);
}
/**
* Find elementDeclarations that use any types derived
* from a named type.
*
* <p>This shows one way to query the schema for elementDeclarations
* and then how to find specific kinds of typeDefinitions.</p>
*
* @param schema to search for elemDecls
* @param namespace for the type used
* @param localName for the type used
* @return List of any XSDElementDeclarations found
*/
public static List<XSDElementDeclaration> findElementsUsingType(XSDSchema schema, String namespace, String localName)
{
if ((null == schema) || (null == localName))
{
throw new IllegalArgumentException("findElementsUsingType called with null schema/localName");
}
ArrayList<XSDElementDeclaration> elemsUsingType = new ArrayList<XSDElementDeclaration>();
// A handy convenience method quickly gets all
// elementDeclarations within our schema; note that
// whether or not this returns types in included,
// imported, or redefined schemas is subject to change
List<XSDElementDeclaration> elemDecls = schema.getElementDeclarations();
for (XSDElementDeclaration elem : elemDecls)
{
XSDTypeDefinition typedef = null;
if (elem.getAnonymousTypeDefinition() != null)
{
typedef = elem.getAnonymousTypeDefinition();
}
else if (elem.getTypeDefinition() != null)
{
typedef = elem.getTypeDefinition();
}
else
{
// Element is not complete, since it has no type,
// thus it's not using our type
continue;
}
// Ask this type and walk the baseTypes from this
// typedef seeing if any of them match the requested one
if (typedef.hasNameAndTargetNamespace(localName, namespace)
|| isTypeDerivedFrom(typedef, namespace, localName))
{
// We found it, return the element and continue
elemsUsingType.add(elem);
continue;
}
}
return elemsUsingType;
}
/**
* Find any included, imported, or redefined schemas that
* this one references.
*
* <p>This method uses solely the Schema model API to look for
* XSDSchemaDirective objects. Each directive is the result
* of an include, import, or redefine in our schema. We then
* walk the tree of directives, finding their resolved schemas
* (if available), and produce a simple HashMap of them all.</p>
* <p>See also {@link #hasImpInclRedef2(XSDSchema)}
* which uses the underlying ResourceSet model to query the
* external resources that were loaded with this schema: it
* has the same effect, but using different code.</p>
*
* @see #hasImpInclRedef2(XSDSchema)
* @param schema object to search for imports/includes/redefines
* @return HashMap where keys are the URI's of any other schemas
* found, and values are one of 'import', 'include', 'redefine';
* null if none found or an error occurred
*/
public static HashMap<String, String> hasImpInclRedef(XSDSchema schema)
{
if (null == schema)
{
throw new IllegalArgumentException("hasImpInclRedef called with null schema");
}
HashMap<String, String> hash = new HashMap<String, String>();
// Recurse to map all imports/includes/redefines
mapImpInclRedef(schema, hash, 0);
// If none were found, return null
if (hash.size() == 0)
{
return null;
}
return hash;
}
/**
* Find any included, imported, or redefined schemas that
* this one references.
*
* <p>This method uses the underlying ResourceSet model to query the
* external resources that were loaded with this schema.
* Note that the ResourceSet implementation is subject to change.</p>
*
* @see #hasImpInclRedef(XSDSchema)
* @param schema object to search for imports/includes/redefines
* @return HashMap where keys are the URI's of any other schemas
* found, and values are one of 'import', 'include', 'redefine';
* null if none found or an error occurred
*/
public static HashMap<String, String> hasImpInclRedef2(XSDSchema schema)
{
if (null == schema)
{
throw new IllegalArgumentException("hasImpInclRedef2 called with null schema");
}
HashMap<String, String> hash = new HashMap<String, String>();
// Use the Resource framework to iterate through any
// other resources that were loaded along with this schema
// to find what they import/include/redefine
Resource currentResource = schema.eResource();
// Get the whole set of resources that were loaded together
ResourceSet resourceSet = currentResource.getResourceSet();
// Iterate over all the resources, i.e., the main resource
// and those that have been included, imported, or redefined.
for (Resource resource : resourceSet.getResources())
{
// Check for schema resources.
if (resource instanceof XSDResourceImpl)
{
XSDResourceImpl xsdResource = (XSDResourceImpl)resource;
// Iterate over each schema's content looking for directives.
XSDSchema otherSchema = xsdResource.getSchema();
for (XSDSchemaContent content : otherSchema.getContents())
{
if (content instanceof XSDSchemaDirective)
{
XSDSchemaDirective schemaDirective = (XSDSchemaDirective)content;
String type = null;
if (content instanceof XSDImport)
{
type = "XSDImport";
}
else if (content instanceof XSDInclude)
{
type = "XSDInclude";
}
else if (content instanceof XSDRedefine)
{
type = "XSDRedefine";
}
else
{
type = "unknown";
}
if (schemaDirective.getResolvedSchema() == null)
{
type += "-unresolved";
}
// Store the item in our hash to return to caller
hash.put(schemaDirective.getSchemaLocation(), type);
// Recurse down the tree into the next schema
// using the schema API's
XSDSchema nextSchema = schemaDirective.getResolvedSchema();
if (null != nextSchema)
{
mapImpInclRedef(nextSchema, hash, 1);
}
}
}
}
}
// If none were found, return null
if (hash.size() == 0)
{
return null;
}
return hash;
}
/**
* Recursive worker method implementing {@link #hasImpInclRedef(XSDSchema)}
* and {@link #hasImpInclRedef2(XSDSchema)}.
*
* <p>This uses only the schema model library API's to look for
* XSDSchemaDirective objects in it's content. It then checks
* which specific subclass of directive each is to report and
* then recurses to any other schemas found.</p>
*
* @param schema object to search for imports/includes/redefines
* @param hash where keys are the URI's of any other schemas
* found, and values are one of 'import', 'include', 'redefine';
* this object is mutated by this method
* @param level of iteration we're on
*/
protected static void mapImpInclRedef(XSDSchema schema, HashMap<String, String> hash, int level)
{
// Iterate through this schema itself to see what
// other schemas it *directly* import/include/redefines
for (XSDSchemaContent schemaContent : schema.getContents())
{
if (schemaContent instanceof XSDSchemaDirective)
{
XSDSchemaDirective schemaDirective = (XSDSchemaDirective)schemaContent;
String type = null;
if (schemaContent instanceof XSDImport)
{
type = "XSDImport-level" + level;
}
else if (schemaContent instanceof XSDInclude)
{
type = "XSDInclude-level" + level;
}
else if (schemaContent instanceof XSDRedefine)
{
type = "XSDRedefine-level" + level;
}
else
{
type = "unknown-level" + level;
}
if (schemaDirective.getResolvedSchema() == null)
{
type += "-unresolved";
}
// Store the item in our caller's hashtable
hash.put(schemaDirective.getSchemaLocation(), type);
// Recurse down the tree into the next schema
XSDSchema nextSchema = schemaDirective.getResolvedSchema();
if (null != nextSchema)
{
mapImpInclRedef(nextSchema, hash, level + 1);
}
}
}
}
}