in xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaPathFinder.java [480:750]
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
final QName elemQName = new QName(uri, localName);
try {
if (currentPath == null) {
/*
* We just started a new document. Likewise we need to move into
* a position to process the root element.
*/
currentPath = rootPathNode;
} else if (currentPath.getStateMachineNode().getNodeType()
.equals(XmlSchemaStateMachineNode.Type.ANY)
&& (anyStack != null) && !anyStack.isEmpty()) {
/*
* If we are currently following a wildcard element node, we
* don't know anything about the element or its children. So it
* does not make sense to follow the children or grandchildren
* of this element.
*/
elementStack.add(elemQName);
anyStack.add(elemQName);
return;
}
// 1. Find possible paths.
List<PathSegment<U, V>> possiblePaths = find(currentPath, elemQName);
PathSegment<U, V> nextPath = null;
if ((possiblePaths != null) && !possiblePaths.isEmpty()) {
/*
* 2. If multiple paths were returned, add a DecisionPoint. Sort
* the paths where paths ending in elements are favored over
* element wild cards, and shorter paths are favored over longer
* paths.
*/
if (possiblePaths.size() > 1) {
final DecisionPoint<U, V> decisionPoint =
new DecisionPoint<U, V>(currentPath, possiblePaths, traversedElements.size(),
elementStack, anyStack);
if (decisionPoints == null) {
decisionPoints = new ArrayList<DecisionPoint<U, V>>(4);
}
decisionPoints.add(decisionPoint);
nextPath = decisionPoint.tryNextPath();
} else {
nextPath = possiblePaths.get(0);
}
if (nextPath == null) {
throw new IllegalStateException("When searching for " + elemQName
+ ", received a set of path choices of size "
+ possiblePaths.size() + ", but the next path is null.");
}
followPath(nextPath);
} else {
// OR: If no paths are returned:
while ((decisionPoints != null) && !decisionPoints.isEmpty()) {
/*
* 2a. Backtrack to the most recent decision point. Remove
* the top path (the one we just tried), and select the next
* one.
*/
final DecisionPoint<U, V> priorPoint = decisionPoints.get(decisionPoints.size() - 1);
nextPath = priorPoint.tryNextPath();
if (nextPath == null) {
/*
* We have tried all paths at this decision point.
* Remove it and try the next prior decision point.
*/
decisionPoints.remove(decisionPoints.size() - 1);
continue;
}
pathMgr.unfollowPath(priorPoint.getDecisionPoint());
elementStack = priorPoint.getElementStack();
anyStack = priorPoint.getAnyStack();
/*
* Walk through the traversedElements list again from that
* index and see if we traverse through all of the elements
* in the list, including this one. If not, repeat step 2a,
* removing decision points from the stack as we refute
* them.
*/
followPath(nextPath);
final QName traversedQName = traversedElements.get(priorPoint.traversedElementIndex).elemName;
elementStack.add(traversedQName);
if (currentPath.getStateMachineNode().getNodeType()
.equals(XmlSchemaStateMachineNode.Type.ANY)) {
if (anyStack == null) {
anyStack = new ArrayList<QName>();
}
anyStack.add(traversedQName);
}
int index = priorPoint.traversedElementIndex + 1;
for (; index < traversedElements.size(); ++index) {
nextPath = null;
final TraversedElement te = traversedElements.get(index);
if (te.traversal.equals(TraversedElement.Traversal.START)) {
possiblePaths = find(currentPath, te.elemName);
if ((possiblePaths == null) || possiblePaths.isEmpty()) {
break;
} else if (possiblePaths.size() > 1) {
final DecisionPoint<U, V> decisionPoint =
new DecisionPoint<U, V>(currentPath, possiblePaths, index, elementStack, anyStack);
decisionPoints.add(decisionPoint);
nextPath = decisionPoint.tryNextPath();
} else {
nextPath = possiblePaths.get(0);
}
if (nextPath == null) {
throw new IllegalStateException("Somehow after finding a new path to follow,"
+ " that path is null.");
}
// If we find (a) path(s) that match(es), success!
// Follow it.
followPath(nextPath);
if (currentPath.getStateMachineNode().getNodeType()
.equals(XmlSchemaStateMachineNode.Type.ANY)) {
if (anyStack == null) {
anyStack = new ArrayList<QName>();
}
anyStack.add(te.elemName);
}
elementStack.add(te.elemName);
} else if (te.traversal.equals(TraversedElement.Traversal.END)) {
final boolean isAny = (currentPath.getStateMachineNode().getNodeType()
.equals(XmlSchemaStateMachineNode.Type.ANY)
&& (anyStack != null) && !anyStack.isEmpty());
if (!isAny) {
walkUpToElement(te.elemName);
}
walkUpTree(te.elemName);
final QName endingElemName = elementStack.remove(elementStack.size() - 1);
if (!te.elemName.equals(endingElemName)) {
throw new IllegalStateException("Attempted to end element " + te.elemName
+ " but found " + endingElemName
+ " on the stack instead!");
}
if (isAny) {
anyStack.remove(anyStack.size() - 1);
}
} else if (te.traversal.equals(TraversedElement.Traversal.CONTENT)) {
final XmlSchemaPathNode<U, V> contentPath =
pathMgr
.addParentSiblingOrContentNodeToPath(currentPath,
XmlSchemaPathNode.Direction.CONTENT);
currentPath.setNextNode(-1, contentPath);
currentPath = contentPath;
} else {
throw new IllegalStateException("Unrecognized element traversal direction for "
+ te.elemName + " of " + te.traversal + '.');
}
}
if (index < traversedElements.size()) {
/*
* This attempt is also incorrect. However, we may have
* introduced new decision points along the way, and we
* want to follow them first. So let's go back around
* for another try.
*/
continue;
}
/*
* We made it to the end of the element list! Now try the
* current one again.
*/
possiblePaths = find(currentPath, elemQName);
if (possiblePaths == null) {
// Still incorrect!
continue;
} else if (possiblePaths.size() > 1) {
final DecisionPoint<U, V> decisionPoint =
new DecisionPoint<U, V>(currentPath, possiblePaths, traversedElements.size(),
elementStack, anyStack);
decisionPoints.add(decisionPoint);
nextPath = decisionPoint.tryNextPath();
} else {
nextPath = possiblePaths.get(0);
}
if (nextPath != null) {
followPath(nextPath);
break;
}
}
}
if (nextPath == null) {
/*
* If we go through all prior decision points and are unable to
* find one or more paths through the XML Schema that match the
* document, throw an error. There is nothing more we can do
* here.
*/
throw new IllegalStateException(
"Walked through XML Schema and could not find a traversal that "
+ "represented this XML Document.");
}
/*
* Current path now points to the element we just started. Validate
* its attributes.
*/
validateAttributes(atts);
traversedElements.add(new TraversedElement(elemQName, TraversedElement.Traversal.START));
elementStack.add(elemQName);
/*
* If this is element is of type xsd:any, we do not track it or its
* children. So, we keep a stack of the element and its children,
* allowing us to know when we leave and can start tracking elements
* again.
*/
if (currentPath.getStateMachineNode().getNodeType().equals(XmlSchemaStateMachineNode.Type.ANY)) {
if (anyStack == null) {
anyStack = new ArrayList<QName>();
}
anyStack.add(elemQName);
}
} catch (Exception e) {
/*
* A SAX Exception cannot be thrown because it is caught, and its
* internal exception is thrown instead. Likewise, any useful info
* about the error reported in the wrapper SAXException is lost.
*/
throw new RuntimeException("Error occurred while starting element " + elemQName
+ "; traversed path is " + getElementsTraversedAsString(), e);
}
}