blob: eeb99b76bc3ae18041bad4864fbee0703f6f915b [file] [log] [blame]
/*
* Copyright (c) 2010 JBoss, Inc. 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
*/
package org.eclipse.bpel.model.util;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import org.eclipse.emf.common.util.EList;
import org.eclipse.xsd.XSDAttributeDeclaration;
import org.eclipse.xsd.XSDAttributeGroupContent;
import org.eclipse.xsd.XSDAttributeGroupDefinition;
import org.eclipse.xsd.XSDAttributeUse;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDConcreteComponent;
import org.eclipse.xsd.XSDDiagnostic;
import org.eclipse.xsd.XSDDiagnosticSeverity;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDFactory;
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.XSDSimpleTypeDefinition;
import org.eclipse.xsd.XSDTerm;
import org.eclipse.xsd.XSDTypeDefinition;
import org.eclipse.xsd.XSDWildcard;
import org.eclipse.xsd.util.XSDConstants;
/**
* Compares two XSD fragments for structure and data type compatibility.
* Optional components in either the left or right schema may be skipped
* over during the comparison unless a strict match is requested (see
* <code>setStrict()<code>).
*
* Components which have a "minOccurs" of 0 are considered optional.
* Wildcard components (<xsd:any> elements) will match any component
* in the other schema.
*
* After the comparison, diagnostics are available which indicate the
* components in the left and right schema that do not match. Warning
* diagnostics are generated whenever optional components were skipped
* to force a match.
*
* @see https://jira.jboss.org/browse/JBIDE-7351
* @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=330813
* @author Bob Brodt
* @date Oct 29, 2010
*/
public class XSDComparer {
protected boolean strict = false;
protected boolean debug = false;
protected List<XSDDiagnostic> diagnostics;
protected Hashtable<XSDTerm,XSDTerm> terms = new Hashtable<XSDTerm,XSDTerm>();
public void setStrict(boolean flag) {
strict = flag;
}
public void setDebug(boolean flag) {
debug = flag;
}
protected boolean alreadyCompared(XSDTerm term1, XSDTerm term2) {
XSDTerm t = terms.get(term1);
if (t==term2)
return true;
t = terms.get(term2);
if (t==term1)
return true;
return false;
}
/**
* The main comparison function that compares two XSD type definitions.
* These may be the entire schema or just fragments within a schema.
*
* @param type1 - the "left" type definition
* @param type2 - the "right" type definition
* @return true if the types are compatible, false if not.
* Warning diagnostics may still be generated for a successful match.
*/
public boolean compare(XSDTypeDefinition type1, XSDTypeDefinition type2) {
boolean result = false;
if (type1==null || type2==null)
throw new IllegalArgumentException("XSDComparer: XSD types may not be null");
try {
// always clear out diagnostics before we start
if (diagnostics!=null)
diagnostics.clear();
// for debugging only: run through the exercises even if the
// two objects are one and the same
if (!debug && type1==type2)
return true;
result = compare(getChildTerms(type1), getChildTerms(type2), 0);
} catch (Exception e) {
return false;
}
return result;
}
/**
* Compares a list of the immediate children of a component; called recursively
* for children that are complex elements.
*
* @param list1 - children of the "left" schema
* @param list2 - children of the "right" schema
* @param level - recursion level (for debug printing)
* @return true if the left and right children lists are compatible.
*/
protected boolean compare(List<XSDTerm> list1, List<XSDTerm> list2, int level) {
XSDTerm term1 = null;
XSDTerm term2 = null;
try {
int index1 = 0;
int index2 = 0;
int size1 = list1.size();
int size2 = list2.size();
while (index1 < size1 && index2 < size2) {
term1 = list1.get(index1);
term2 = list2.get(index2);
if (alreadyCompared(term1,term2)) {
dump("Skipping ",term1,null,level);
}
else {
terms.put(term1,term2);
if ( term1 instanceof XSDElementDeclaration && term2 instanceof XSDElementDeclaration) {
XSDElementDeclaration elem1 = (XSDElementDeclaration)term1;
XSDElementDeclaration elem2 = (XSDElementDeclaration)term2;
XSDTypeDefinition type1 = elem1.getTypeDefinition();
XSDTypeDefinition type2 = elem2.getTypeDefinition();
if (type1 instanceof XSDSimpleTypeDefinition && type2 instanceof XSDSimpleTypeDefinition) {
dump("Comparing ", elem1, type1, level);
dump(" to ", elem2, type2, level);
if (!compareQNames(elem1, elem2)) {
addError("different QNames: " + elem1.getQName() + " vs " + elem2.getQName(), term1,term2);
return false;
}
String s1 = getTypeNameHierarchy(elem1);
String s2 = getTypeNameHierarchy(elem2);
if (!s1.equals(s2)) {
addError("different data types: "+s1+" vs "+s2,elem1,elem2);
return false;
}
} else if (type1 instanceof XSDComplexTypeDefinition && type2 instanceof XSDComplexTypeDefinition) {
dump("Comparing ", elem1, type1, level);
dump(" to ", elem2, type2, level);
if (!compareQNames(elem1, elem2)) {
addError("different QNames: " + elem1.getQName() + " vs " + elem2.getQName(), term1,term2);
return false;
}
int min1 = getMinOccurs(term1);
int min2 = getMinOccurs(term2);
int max1 = getMaxOccurs(term1);
int max2 = getMaxOccurs(term2);
if (strict) {
if (min1 != min2 || max1 != max2) {
addError("different cardinality: " + min1 + " to " + max1 + " vs " + min2 + " to " + max2, term1,term2);
return false;
}
} else {
if ((max2>0 && min1 > max2) || (max1>0 && min2 > max1)) {
addError("incompatible cardinality: " + min1 + " to " + max1 + " vs " + min2 + " to " + max2, term1,term2);
return false;
}
}
boolean result = compare(getChildTerms(elem1), getChildTerms(elem2), level + 1);
if (!result) {
addError("different complex element structures",elem1,elem2);
return false;
}
} else {
if (!strict) {
// try shifting optional elements and continue from there
dump("Elements out of sync - skipping optional elements");
if (isOptional(elem1)) {
// skip over this one and compare remaining elements
List<XSDTerm> newList1 = new ArrayList<XSDTerm>();
List<XSDTerm> newList2 = new ArrayList<XSDTerm>();
for (int i = index1 + 1; i < size1; ++i)
newList1.add(list1.get(i));
for (int i = index2; i < size2; ++i)
newList2.add(list2.get(i));
if (compare(newList1, newList2, level)) {
addWarning("skipped optional element(s) in left schema",elem1,elem2);
return true;
}
addError("different complex element structures",elem1,elem2);
}
if (isOptional(elem2)) {
// skip over this one and compare remaining elements
List<XSDTerm> newList1 = new ArrayList<XSDTerm>();
List<XSDTerm> newList2 = new ArrayList<XSDTerm>();
for (int i = index1; i < size1; ++i)
newList1.add(list1.get(i));
for (int i = index2 + 1; i < size2; ++i)
newList2.add(list2.get(i));
if (compare(newList1, newList2, level)) {
addWarning("skipped optional element(s) in right schema",elem1,elem2);
return true;
}
addError("different complex element structures",elem1,elem2);
}
}
addError("different complex element structures",elem1,elem2);
return false;
}
List<XSDAttributeDeclaration> attrs1 = getAttributeDeclarations(elem1);
List<XSDAttributeDeclaration> attrs2 = getAttributeDeclarations(elem2);
if (attrs1.size() != attrs2.size()) {
addError("different number of attributes",elem1,elem2);
return false;
}
for (int i = 0; i < attrs1.size(); ++i) {
XSDAttributeDeclaration attr1 = attrs1.get(i);
XSDAttributeDeclaration attr2 = attrs2.get(i);
if (!attr1.getQName().equals(attr2.getQName())) {
addError("different attribute names: "+attr1.getQName()+" vs "+attr2.getQName(),elem1,elem2);
return false;
}
String value1 = attr1.getLexicalValue();
String value2 = attr2.getLexicalValue();
if (value1==null)
value1 = "";
if (value2==null)
value2 = "";
if (!value1.equals(value2)) {
addError("different attribute values: "+attr1.getLexicalValue()+" vs "+attr2.getLexicalValue(),elem1,elem2);
return false;
}
}
} else if (term1 instanceof XSDWildcard && !(term2 instanceof XSDWildcard) ) {
// left schema term is a wildcard, right schema is not
int min = getMinOccurs(term1);
int max = getMinOccurs(term1);
if (max==-1)
max = size1;
for (int n=min; n<=max; ++n) {
// skip over this one and compare remaining elements
List<XSDTerm> newList1 = new ArrayList<XSDTerm>();
List<XSDTerm> newList2 = new ArrayList<XSDTerm>();
for (int i = index1 + 1 + n; i < size1; ++i)
newList1.add(list1.get(i));
for (int i = index2; i < size2; ++i)
newList2.add(list2.get(i));
if (compare(newList1, newList2, level)) {
addWarning("skipped optional element(s) in left schema",term1,term2);
return true;
}
}
addError("different complex element structures",term1,term2);
return false;
} else if (term1 instanceof XSDWildcard && !(term2 instanceof XSDWildcard) ) {
// right schema is a wildcard, left schema is not
int min = getMinOccurs(term2);
int max = getMinOccurs(term2);
if (max==-1)
max = size2;
for (int n=min; n<=max; ++n) {
// skip over this one and compare remaining elements
List<XSDTerm> newList1 = new ArrayList<XSDTerm>();
List<XSDTerm> newList2 = new ArrayList<XSDTerm>();
for (int i = index1; i < size1; ++i)
newList1.add(list1.get(i));
for (int i = index2 + 1 + n; i < size2; ++i)
newList2.add(list2.get(i));
if (compare(newList1, newList2, level)) {
addWarning("skipped optional element(s) in right schema",term1,term2);
return true;
}
}
addError("different complex element structures",term1,term2);
return false;
} else if (term1 instanceof XSDWildcard && term2 instanceof XSDWildcard) {
// both wildcards - it's a match!
int min1 = getMinOccurs(term1);
int max1 = getMinOccurs(term1);
int min2 = getMinOccurs(term2);
int max2 = getMinOccurs(term2);
if (min1!=min2 || max1!=max2) {
// not possible
addError("different <xsd:any> cardinality: "+min1+" to "+max1+" vs "+min2+" to "+max2,term1,term2);
}
}
}
++index1;
++index2;
}
// any leftovers? they must be optional
if (strict && size1 != size2) {
addError("different number of elements: "+size1+" vs "+size2,term1,term2);
return false;
}
if (index1 < size1) {
dump("Additional elements in left schema");
while (index1 < size1) {
term1 = list1.get(index1);
if (isOptional(term1)) {
dump("Ignoring optional element", term1, null, 1);
addWarning("skipped optional element in left schema",term1,term2);
term1 = list2.get(++index1);
} else {
addError("additional elements in left schema",term1,term2);
return false;
}
}
}
if (index2 < size2) {
dump("Additional elements in right schema");
while (index2 < size2) {
term2 = list2.get(index2);
if (isOptional(term2)) {
dump("Ignoring optional element", term2, null, 1);
addWarning("skipped optional element in right schema",term1,term2);
term2 = list2.get(++index2);
} else {
addError("additional elements in right schema",term1,term2);
return false;
}
}
}
} catch (Exception e) {
addError("caught exception: "+e.toString(),term1,term2);
e.printStackTrace();
return false;
}
if (level==0)
dump("*** Schemas are compatible");
return true;
}
/**
* Returns the data type name of the given element, including base types.
*
* @param elem - the element
* @return data type name hierarchy as a ":" separated string
*/
protected String getTypeNameHierarchy(XSDElementDeclaration elem) {
String s = "";
Stack<String> names = new Stack<String>();
XSDTypeDefinition type = elem.getType();
while (type != type.getBaseType()) {
String name = type.getName();
if (name != null)
names.push(name);
type = type.getBaseType();
}
while (!names.isEmpty()) {
if (s.isEmpty())
s = names.pop();
else
s = s + ":" + names.pop();
}
return s;
}
private boolean compareQNames(XSDNamedComponent elem1, XSDNamedComponent elem2) {
String name1 = elem1.getName();
String name2 = elem2.getName();
if (name1==null || name2==null) {
if (name1==name2)
return true;
return false;
}
if (name1.equals(name2)) {
if (elem1.getTargetNamespace() == null || elem1.getTargetNamespace().isEmpty()
&& elem2.getTargetNamespace() == null || elem2.getTargetNamespace().isEmpty())
return true;
if (elem1.getTargetNamespace().equals(elem2.getTargetNamespace()))
return true;
if ( elem1.getSchema().getTargetNamespace().equals(elem2.getSchema().getTargetNamespace()) )
return true;
}
return false;
}
/**
* Returns a list of immediate children for the specified element.
*
* @param element - the parent element
* @return A list of XSDTerm objects
*/
protected final List<XSDTerm> getChildTerms(XSDElementDeclaration element) {
return getChildTerms(element.getType());
}
/**
* Returns a list of immediate children for the specified XSD type.
*
* @param type - the parent type
* @return A list of XSDTerm objects
*/
protected final List<XSDTerm> getChildTerms(XSDTypeDefinition type) {
final ArrayList<XSDTerm> terms = new ArrayList<XSDTerm>();
TypeWalker.Visitor visitor = new TypeWalker.Visitor() {
public boolean visit(XSDTypeDefinition type) {
// simple types don't have children
if (type instanceof XSDSimpleTypeDefinition)
return true;
XSDComplexTypeDefinition cType = (XSDComplexTypeDefinition) type;
visitTerms(cType, new TermVisitor() {
public void visit(XSDParticle particle) {
// element declaration, or wildcard: add to list
XSDParticleContent content = particle.getContent();
if (content instanceof XSDElementDeclaration) {
XSDElementDeclaration decl = (XSDElementDeclaration) particle.getContent();
if (decl.isElementDeclarationReference())
decl = decl.getResolvedElementDeclaration();
// System.out.println("Adding "+decl.getName());
terms.add(decl);
} else if (content instanceof XSDWildcard) {
// System.out.println("Adding <xsd:any>");
terms.add((XSDWildcard) content);
}
}
});
return true;
}
};
TypeWalker walker = new TypeWalker(type);
walker.walk(visitor);
return terms;
}
/**
* Returns the value of the "minOccurs" attribute for the given XSD element.
*
* @param term - the XSD element
* @return numerical value of "minOccurs" or 1 if the element does not specify
*/
public int getMinOccurs(XSDTerm term) {
String smin = term.getElement().getAttribute("minOccurs");
int min = 1;
if (smin!=null && !smin.isEmpty()) {
try {
min = Integer.parseInt(smin);
} catch (NumberFormatException e) {
}
}
return min;
}
/**
* Returns the value of the "maxOccurs" attribute for the given XSD element.
*
* @param term - the XSD element
* @return numerical value of "maxOccurs" or 1 if the element does not specify
*/
public int getMaxOccurs(XSDTerm term) {
String smax = term.getElement().getAttribute("maxOccurs");
int max = 1;
if (smax!=null && !smax.isEmpty()) {
try {
max = Integer.parseInt(smax);
} catch (NumberFormatException e) {
if (smax.equalsIgnoreCase("unbounded"))
max = -1;
}
}
return max;
}
/**
* Returns true if the specified XSD element is optional.
*
* @param term
* @return true if the element has "minOccurs=0"
*/
protected boolean isOptional(XSDTerm term) {
if (getMinOccurs(term)==0)
return true;
return false;
}
/**
* Examine the content of a complex type and visit each of its particles.
* This will expand <xsd:sequence>, <xsd:choice>, <xsd:all>, <xsd:group>
* and <xsd:any> elements.
*
* @param cType - the complex type to examine
* @param visitor - a TermVisitor which will be called for each particle found
*/
private void visitTerms(XSDComplexTypeDefinition cType, TermVisitor visitor) {
// simple types are leaf nodes
if (cType.getContent() == null || (cType.getContent() instanceof XSDSimpleTypeDefinition))
return;
// the last particle visited; used to detect <xsd:any> elements
XSDParticleContent lastVisited = null;
// use a queue to simulate the recursion
LinkedList<XSDParticle> queue = new LinkedList<XSDParticle>();
queue.addLast((XSDParticle) cType.getContent());
while (!queue.isEmpty()) {
XSDParticle particle = (XSDParticle) queue.removeFirst();
// analyze type of particle content
int pType = org.eclipse.xsd.util.XSDUtil.nodeType(particle.getElement());
if (pType == XSDConstants.ELEMENT_ELEMENT) {
lastVisited = particle.getContent();
visitor.visit(particle);
} else {
// model group
XSDModelGroup modelGroup = null;
switch (pType) {
case XSDConstants.GROUP_ELEMENT:
XSDModelGroupDefinition grpDef = (XSDModelGroupDefinition) particle.getContent();
if (grpDef.isModelGroupDefinitionReference())
grpDef = grpDef.getResolvedModelGroupDefinition();
modelGroup = grpDef.getModelGroup();
break;
case XSDConstants.CHOICE_ELEMENT:
case XSDConstants.ALL_ELEMENT:
case XSDConstants.SEQUENCE_ELEMENT:
modelGroup = (XSDModelGroup) particle.getContent();
break;
case XSDConstants.ANY_ELEMENT:
// see forum post at:
// http://www.eclipse.org/forums/index.php?t=msg&th=20437&start=0&S=52ae90dcc09745f37034e330b91695cd
//
// The XSDWildcard seems to be emitted in the case where an element has no type information;
// and it is also emitted in response to an actual xsd:any element appearing in the schema.
//
// In both cases, the XSDWildcard appears the same (surprisingly it refers to a DOM element
// from Xerces of xsd:any). there is confusion here between elements of type anyType, i..e.,
// <xsd:element name="x" type=xsd:anyType"/> verses element wildcards <xsd:any>. When matching
// element content using the DFA returned by XSDParticle, each element will match either an
// element declaration or a wildcard, which you can tell apart easily by whether you've matched
// an XSDElementDeclaration or an XSDWildcard.
if (lastVisited!=null) {
// found an <xsd:any> element - go visit it!
lastVisited = particle.getContent();
visitor.visit(particle);
}
break;
default:
// TODO: do we need to handle additional XSD node types?
break;
}
if (modelGroup != null) {
// enque all particles in the group
List<XSDParticle> particles = modelGroup.getParticles();
for (XSDParticle p : particles) {
queue.addLast(p);
}
lastVisited = null;
}
}
}
}
/**
* Returns a list of all attribute declarations declared in the type or any
* base type, of the specified element.
*
* @param element - the element
* @return a list of XSDAttributeDeclaration objects
*/
public final List<XSDAttributeDeclaration> getAttributeDeclarations(XSDElementDeclaration element) {
final ArrayList<XSDAttributeDeclaration> attributes = new ArrayList<XSDAttributeDeclaration>();
// walk up the type hierarchy of the element to generate a list of atts
TypeWalker walker = new TypeWalker(element.getType());
TypeWalker.Visitor visitor = new TypeWalker.Visitor() {
public boolean visit(XSDTypeDefinition type) {
// simple types dont have attributes
if (type instanceof XSDSimpleTypeDefinition)
return true;
XSDComplexTypeDefinition cType = (XSDComplexTypeDefinition) type;
// get all the attribute content (groups,or uses) and add to list
List<XSDAttributeGroupContent> contents = cType.getAttributeContents();
for (XSDAttributeGroupContent content : contents) {
if (content instanceof XSDAttributeUse) {
// an attribute, add it to the list
XSDAttributeUse use = (XSDAttributeUse) content;
attributes.add(use.getAttributeDeclaration());
} else if (content instanceof XSDAttributeGroupDefinition) {
// attribute group, add all atts in group to list
XSDAttributeGroupDefinition attGrp = (XSDAttributeGroupDefinition) content;
if (attGrp.isAttributeGroupDefinitionReference()) {
attGrp = attGrp.getResolvedAttributeGroupDefinition();
}
List<XSDAttributeUse> uses = attGrp.getAttributeUses();
for (XSDAttributeUse use : uses) {
attributes.add(use.getAttributeDeclaration());
}
}
}
return true;
}
};
walker.walk(visitor);
return attributes;
}
/**
* Returns a list diagnostics of that may have been created
* during the comparison
*
* @return a list of XSDDiagnostic objects
*/
public List<XSDDiagnostic> getDiagnostics() {
if (diagnostics==null) {
diagnostics = new ArrayList<XSDDiagnostic>();
}
return diagnostics;
}
/**
* Convenience method for formatting diagnostics as strings
*
* @param index - the index into the diagnostics array
* @return a nicely formatted string
*/
public String getDiagnostic(int index) {
String msg = "";
if (diagnostics!=null && index>=0 && index<diagnostics.size()) {
XSDDiagnostic d = diagnostics.get(index);
String name1 = "";
String name2 = "";
String schema1 = "";
String schema2 = "";
XSDTerm comp1 = null;
XSDTerm comp2 = null;
EList<XSDConcreteComponent> components = d.getComponents();
if (components.size()>0 && components.get(0) instanceof XSDTerm)
comp1 = (XSDTerm)components.get(0);
if (components.size()>1 && components.get(1) instanceof XSDTerm)
comp2 = (XSDTerm)components.get(1);
if (comp1 instanceof XSDElementDeclaration) {
name1 = ((XSDElementDeclaration)comp1).getQName();
schema1 = comp1.eResource().getURI().toString();
}
else if (comp1 instanceof XSDWildcard) {
name1 = "<xsd:any>";
schema1 = comp1.eResource().getURI().toString();
}
if (comp2 instanceof XSDElementDeclaration) {
name2 = ((XSDElementDeclaration)comp2).getQName();
schema2 = comp2.eResource().getURI().toString();
}
else if (comp2 instanceof XSDWildcard) {
name2 = "<xsd:any>";
schema2 = comp2.eResource().getURI().toString();
}
if ( comp1!=null && comp2!=null ) {
msg = "Element <" + name1 + ">" +
" in " + schema1 +
" differs from <" + name2 + ">";
if (!schema1.equals(schema2))
msg += " in " + schema2;
msg += " - " + d.getMessage();
}
else {
msg =
"Schema " + d.getLocationURI() +
" - " + d.getMessage();
}
}
return msg;
}
/**
* Creates a new diagnostic and adds it to our list.
*
* @param severity - one of the XSDDiagnosticSeverity values (WARNING, ERROR, etc.)
* @param msg - a human-readable message to associate with the diagnostic
* @param comp1 - the "left" schema component
* @param comp2 - the "right" schema component
* @return a newly constructed XSDDiagnostic object
*/
protected XSDDiagnostic addDiagnostic(int severity, String msg, XSDConcreteComponent comp1, XSDConcreteComponent comp2) {
XSDDiagnostic diag = XSDFactory.eINSTANCE.createXSDDiagnostic();
diag.setSeverity(XSDDiagnosticSeverity.get(severity));
diag.setMessage(msg);
diag.getComponents().add(comp1);
diag.getComponents().add(comp2);
getDiagnostics().add(diag);
if (debug)
dump(getDiagnostic(getDiagnostics().size()-1));
return diag;
}
/**
* Convenience method for creating a WARNING diagnostic
*
* @param msg - a human-readable message to associate with the diagnostic
* @param comp1 - the "left" schema component
* @param comp2 - the "right" schema component
* @return a newly constructed XSDDiagnostic object
*/
protected XSDDiagnostic addWarning(String msg, XSDConcreteComponent comp1, XSDConcreteComponent comp2) {
return addDiagnostic(XSDDiagnosticSeverity.WARNING, msg, comp1, comp2);
}
/**
* Convenience method for creating a WARNING diagnostic
*
* @param msg - a human-readable message to associate with the diagnostic
* @param comp1 - the "left" schema component
* @param comp2 - the "right" schema component
* @return a newly constructed XSDDiagnostic object
*/
protected XSDDiagnostic addError(String msg, XSDConcreteComponent comp1, XSDConcreteComponent comp2) {
return addDiagnostic(XSDDiagnosticSeverity.ERROR, msg, comp1, comp2);
}
/**
* Debug message printer
*
* @param label - a text string to print to stderr
*/
private void dump(String label) {
if (debug)
dump(label, null, null, 0);
}
/**
* Debug message printer
*
* @param label - text string
* @param term - an XSD element to dump
* @param type - and its type
* @param level - indent level
*/
private void dump(String label, XSDTerm term, XSDTypeDefinition type, int level) {
if (debug) {
if (term != null) {
StringBuilder indent = new StringBuilder();
for (int i = 0; i < level; ++i)
indent.append( " " );
if (term instanceof XSDElementDeclaration) {
XSDElementDeclaration decl = (XSDElementDeclaration) term;
if (type instanceof XSDSimpleTypeDefinition)
System.err.println(label + indent + "<" + decl.getName() + "> type=\""
+ this.getTypeNameHierarchy(decl)+"\"");
else if (type instanceof XSDComplexTypeDefinition || type == null)
System.err.println(label + indent + "<" + decl.getName() + ">");
else
System.err.println("dump: unknown XSD type: " + type.getClass().toString());
} else if (term instanceof XSDWildcard) {
System.err.println(label + indent + "<any/>");
} else
System.err.println("dump: unknown term: " + term.getClass().toString());
} else
System.err.println(label);
}
}
/**
* Simple visitor interface for complex types
*/
private interface TermVisitor {
void visit(XSDParticle particle);
}
/**
* XSD type hierarchy walker that visits each XSD type from the bottom-up
*/
private static class TypeWalker {
// bottom of the type hierarchy
XSDTypeDefinition base;
public TypeWalker(XSDTypeDefinition base) {
this.base = base;
}
public void walk(Visitor visitor) {
XSDTypeDefinition type = base;
while(type != null) {
if (!visitor.visit(type))
break;
// walk up the type hierarchy to the top
if (type.equals(type.getBaseType()))
break;
type = type.getBaseType();
}
}
public interface Visitor {
boolean visit(XSDTypeDefinition type);
}
}
private static class XSDTermTuple {
public XSDTerm term1, term2;
public XSDTermTuple(XSDTerm term1, XSDTerm term2) {
this.term1 = term1;
this.term2 = term2;
}
}
}