| /******************************************************************************* |
| * Copyright (c) 2002, 2009 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 |
| * Jens Lukowski/Innoopract - initial renaming/restructuring |
| * David Carver (STAR) - bug 297005 - Some static constants not made final. |
| *******************************************************************************/ |
| package org.eclipse.wst.xml.core.internal.contentmodel.internal.util; |
| |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Stack; |
| import java.util.Vector; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMAnyElement; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMContent; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMDataType; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMGroup; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMNode; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMNodeList; |
| import org.eclipse.wst.xml.core.internal.contentmodel.util.CMVisitor; |
| |
| |
| |
| public class CMValidator |
| { |
| protected static final StringElementContentComparator stringContentComparitor = new StringElementContentComparator(); |
| protected Hashtable graphNodeTable = new Hashtable(); |
| |
| /** |
| * GraphNode |
| */ |
| protected static class GraphNode |
| { |
| public String name; |
| public boolean isTerminal; |
| public Vector arcList = new Vector(); |
| |
| public GraphNode(String name) |
| { |
| this.name = name; |
| } |
| |
| public void addArc(Arc arc) |
| { |
| arcList.addElement(arc); |
| } |
| |
| public String toString() |
| { |
| return "[GraphNode " + name + "]"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| |
| /** |
| * Arc |
| */ |
| protected static class Arc |
| { |
| public static final int ELEMENT = 1; |
| public static final int REPEAT = 2; |
| public static final int OPTIONAL = 3; |
| public static final int PREV_IN = 4; |
| public static final int OUT_NEXT = 5; |
| public static final int LINK = 6; |
| |
| public int kind; |
| public String name; |
| public GraphNode node; |
| public CMNode cmNode; |
| |
| public Arc(int kind, GraphNode node, CMNode cmNode) |
| { |
| this(kind, "", node, cmNode); //$NON-NLS-1$ |
| } |
| |
| protected Arc(int kind, String name, GraphNode node, CMNode cmNode) |
| { |
| this.name = name; |
| this.kind = kind; |
| this.node = node; |
| this.cmNode = cmNode; |
| } |
| } |
| |
| |
| /** |
| * GraphGenerator |
| */ |
| protected static class GraphGenerator extends CMVisitor |
| { |
| public int indent; |
| public int count; |
| public GraphNode startGraphNode; |
| public Context context; |
| |
| protected static class Context |
| { |
| GraphNode from; |
| GraphNode to; |
| |
| Context(GraphNode from, GraphNode to) |
| { |
| this.from = from; |
| this.to = to; |
| } |
| |
| GraphNode getLastGraphNode() |
| { |
| return (to != null) ? to : from; |
| } |
| } |
| |
| |
| protected GraphGenerator() |
| { |
| startGraphNode = new GraphNode(getGraphNodeName()); |
| context = new Context(startGraphNode, null); |
| } |
| |
| |
| protected void generateGraph(CMElementDeclaration ed) |
| { |
| int contentType = ed.getContentType(); |
| |
| if (contentType == CMElementDeclaration.MIXED || |
| contentType == CMElementDeclaration.ELEMENT) |
| { |
| visitCMNode(ed.getContent()); |
| } |
| // CMElementDeclaration.PCDATA... no graph required |
| // CMElementDeclaration.ANY... no graph required |
| context.getLastGraphNode().isTerminal = true; |
| } |
| |
| |
| protected String getGraphNodeName() |
| { |
| return "n" + count++; //$NON-NLS-1$ |
| } |
| |
| |
| protected GraphNode getStartGraphNode() |
| { |
| return startGraphNode; |
| } |
| |
| |
| /** |
| * repeat |
| * +----#-----+ |
| * | | |
| * v | |
| * prev --#-> in --'x'-> out --#-> next |
| * | ^ |
| * | | |
| * +----------------#--------------+ |
| * optional |
| * |
| */ |
| protected void createArcs(GraphNode in, GraphNode out, CMContent cmContent) |
| { |
| createArcs(in, out, cmContent, false); |
| } |
| |
| protected void createArcs(GraphNode in, GraphNode out, CMContent cmContent, boolean isAllGroup) |
| { |
| //println("+createArcs() " + ed.getDescription() + " " + ed.getMinOccur()); |
| GraphNode prev = context.from; |
| GraphNode next = new GraphNode(getGraphNodeName()); |
| |
| prev.addArc(new Arc(Arc.PREV_IN, in, cmContent)); |
| out.addArc(new Arc(Arc.OUT_NEXT, next, cmContent)); |
| |
| if (context.to != null) |
| { |
| next.addArc(new Arc(Arc.LINK, context.to, cmContent)); |
| } |
| else |
| { |
| context.from = next; |
| } |
| |
| if (cmContent.getMinOccur() == 0) |
| { |
| // todo... should we see if an optional arc has already been added? |
| prev.addArc(new Arc(Arc.OPTIONAL, next, cmContent)); |
| } |
| |
| if (cmContent.getMaxOccur() == -1 || cmContent.getMaxOccur() > 1 || isAllGroup) |
| { |
| out.addArc(new Arc(Arc.REPEAT, in, cmContent)); |
| } |
| } |
| |
| |
| public void visitCMGroup(CMGroup group) |
| { |
| Context prevContext = context; |
| GraphNode in = new GraphNode("(" + getGraphNodeName()); //$NON-NLS-1$ |
| GraphNode out = new GraphNode(")" + getGraphNodeName()); //$NON-NLS-1$ |
| |
| int groupOperator = group.getOperator(); |
| if (groupOperator == CMGroup.SEQUENCE) |
| { |
| context = new Context(in, null); |
| super.visitCMGroup(group); |
| context.from.addArc(new Arc(Arc.LINK, out, group)); |
| } |
| else if (groupOperator == CMGroup.CHOICE || |
| groupOperator == CMGroup.ALL) |
| { |
| context = new Context(in, out); |
| super.visitCMGroup(group); |
| } |
| |
| context = prevContext; |
| createArcs(in, out, group, groupOperator == CMGroup.ALL); |
| } |
| |
| |
| public void visitCMElementDeclaration(CMElementDeclaration ed) |
| { |
| GraphNode in = new GraphNode(getGraphNodeName()); |
| GraphNode out = new GraphNode(getGraphNodeName()); |
| createArcs(in, out, ed); |
| in.addArc(new Arc(Arc.ELEMENT, ed.getElementName(), out, ed)); |
| } |
| |
| |
| public void visitCMAnyElement(CMAnyElement anyElement) |
| { |
| GraphNode in = new GraphNode(getGraphNodeName()); |
| GraphNode out = new GraphNode(getGraphNodeName()); |
| createArcs(in, out, anyElement); |
| in.addArc(new Arc(Arc.ELEMENT, "any", out, anyElement)); //$NON-NLS-1$ |
| } |
| } |
| |
| // todo.. implement cache strategy hook, handle name spaces, locals etc. |
| // |
| public GraphNode lookupOrCreateGraph(CMElementDeclaration element) |
| { |
| Object key = element; |
| GraphNode node = (GraphNode)graphNodeTable.get(key); |
| if (node == null) |
| { |
| node = createGraph(element); |
| graphNodeTable.put(key, node); |
| } |
| return node; |
| } |
| |
| public GraphNode createGraph(CMElementDeclaration element) |
| { |
| GraphGenerator generator = new GraphGenerator(); |
| generator.generateGraph(element); |
| return generator.getStartGraphNode(); |
| } |
| |
| |
| public void printGraph(GraphNode node, Vector namedArcList, Vector unamedArcList, int indent) |
| { |
| //String decoration = node.isTerminal ? " *" : ""; |
| //printlnIndented(indent, "GraphNode:" + node.name + decoration); |
| |
| indent += 2; |
| for (Enumeration e = node.arcList.elements() ; e.hasMoreElements() ;) |
| { |
| Arc arc = (Arc)e.nextElement(); |
| //boolean visit = false; |
| //printlnIndented(indent, "Arc:" + arc.name + " (" + arc.kind + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| if (arc.kind == Arc.ELEMENT) |
| { |
| //table.add(currentGrammarObject, arc.grammarObject); |
| if (!namedArcList.contains(arc)) |
| { |
| namedArcList.add(arc); |
| unamedArcList = new Vector(); |
| printGraph(arc.node, namedArcList, unamedArcList, indent + 2); |
| } |
| } |
| else |
| { |
| if (!unamedArcList.contains(arc)) |
| { |
| unamedArcList.add(arc); |
| printGraph(arc.node, namedArcList, unamedArcList, indent + 2); |
| } |
| } |
| } |
| } |
| |
| public void printGraph(GraphNode node) |
| { |
| printGraph(node, new Vector(), new Vector(), 0); |
| } |
| |
| |
| public void validateElementList(ElementList initialList, GraphNode initialGraphNode, ElementContentComparator comparator, Result result, boolean initialLoopFlag) { |
| Stack arcStack = new Stack(); |
| arcStack.push(new ArcStackItem(null, false)); |
| boolean loopFlag = initialLoopFlag; |
| ElementList elementList = initialList; |
| GraphNode graphNode = initialGraphNode; |
| while(!arcStack.isEmpty() && !result.isValid) { |
| ArcStackItem stackElement = (ArcStackItem) arcStack.peek(); |
| if(stackElement.isVisited) { |
| arcStack.pop(); |
| if(stackElement.arc != null) { |
| result.pop(stackElement.arc); |
| continue; |
| } |
| } else { |
| stackElement.isVisited = true; |
| result.push(stackElement.arc); |
| graphNode = stackElement.arc.node; |
| loopFlag = stackElement.loopFlag; |
| } |
| if(elementList == null && graphNode.isTerminal) { |
| result.isValid = true; |
| } else { |
| for(Iterator arcIterator = graphNode.arcList.iterator(); arcIterator.hasNext();) { |
| Arc arc = (Arc)arcIterator.next(); |
| boolean traverseArc = false; |
| if (arc.kind == Arc.ELEMENT) { |
| if(elementList != null && comparator.matches(elementList.head, arc.cmNode)) { |
| loopFlag = false; |
| traverseArc = true; |
| elementList = elementList.tail; // increment our position in the list |
| } |
| } else if(arc.kind == Arc.REPEAT) { |
| if(!loopFlag) { |
| traverseArc = true; |
| } |
| loopFlag = true; |
| } else { |
| traverseArc = true; |
| } |
| if(traverseArc) { |
| if (result.canPush(arc)) { // test to see if we can push this arc due to facet constraints |
| arcStack.push(new ArcStackItem(arc, loopFlag)); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| |
| private class ArcStackItem { |
| |
| Arc arc; |
| boolean loopFlag; |
| boolean isVisited; |
| |
| public ArcStackItem(Arc arc, boolean loopflag) { |
| this.arc = arc; |
| this.loopFlag = loopflag; |
| this.isVisited = arc == null; |
| } |
| |
| } |
| |
| |
| /** |
| * |
| */ |
| protected static ElementList createElementList(int contentType, List v, ElementContentComparator comparator, Result result) |
| { |
| ElementList first = null; |
| ElementList prev = null; |
| |
| int size = v.size(); |
| for (int i = 0; i < size; i++) |
| { |
| Object o = v.get(i); |
| if (o != null && !comparator.isIgnorable(o)) |
| { |
| if (comparator.isElement(o)) |
| { |
| ElementList list = new ElementList(); |
| list.head = o; |
| |
| if (prev != null) |
| { |
| prev.tail = list; |
| } |
| else |
| { |
| first = list; |
| } |
| prev = list; |
| } |
| else if (contentType == CMElementDeclaration.ELEMENT) |
| { |
| result.isValid = false; |
| result.errorIndex = i; |
| result.errorMessage = "Element can not include PCDATA content"; //$NON-NLS-1$ |
| } |
| } |
| } |
| return first; |
| } |
| |
| /** |
| * |
| */ |
| public void validate(CMElementDeclaration ed, List elementContent, ElementContentComparator comparator, Result result) |
| { |
| int contentType = ed.getContentType(); |
| |
| if (contentType == CMElementDeclaration.MIXED || |
| contentType == CMElementDeclaration.ELEMENT) |
| { |
| ElementList elementList = createElementList(contentType, elementContent, comparator, result); |
| if (result.isValid == true) |
| { |
| boolean isGraphValidationNeeded = !(elementList == null && contentType == CMElementDeclaration.MIXED); |
| |
| // explicitly handle 'All' groups |
| // |
| CMContent content = ed.getContent(); |
| if (content != null && content.getNodeType() == CMNode.GROUP) |
| { |
| CMGroup group = (CMGroup)content; |
| if (group.getOperator() == CMGroup.ALL) |
| { |
| isGraphValidationNeeded = false; |
| validatAllGroupContent(elementContent, comparator, group, result); |
| } |
| } |
| |
| if (isGraphValidationNeeded) |
| { |
| // validate the elementList using a graph |
| // |
| result.isValid = false; |
| GraphNode node = lookupOrCreateGraph(ed); |
| validateElementList(elementList, node, comparator, result, false); |
| } |
| } |
| } |
| else if (contentType == CMElementDeclaration.PCDATA) |
| { |
| int size = elementContent.size(); |
| for (int i = 0; i < size; i++) |
| { |
| Object o = elementContent.get(i); |
| if (comparator.isElement(o)) |
| { |
| result.isValid = false; |
| result.errorIndex = i; |
| result.errorMessage = "Element may only include PCDATA content"; //$NON-NLS-1$ |
| break; |
| } |
| } |
| } |
| else if (contentType == CMElementDeclaration.EMPTY) |
| { |
| int size = elementContent.size(); |
| for (int i = 0; i < size; i++) |
| { |
| Object o = elementContent.get(i); |
| if (!comparator.isIgnorable(o)) |
| { |
| result.isValid = false; |
| result.errorIndex = i; |
| result.errorMessage = "Element may not contain PCDATA or Element content"; //$NON-NLS-1$ |
| break; |
| } |
| } |
| } |
| //else if (contentType == CMElementDeclaration.ANY) |
| // { |
| // assume elementContent will always be valid for this content type |
| // } |
| } |
| |
| static class ItemCount |
| { |
| int count = 0; |
| } |
| |
| private void validatAllGroupContent(List elementContent, ElementContentComparator comparator, CMGroup allGroup, Result result) |
| { |
| boolean isValid = true; |
| boolean isPartiallyValid = true; |
| HashMap map = new HashMap(); |
| CMNodeList list = allGroup.getChildNodes(); |
| for (int j = list.getLength() - 1; j >= 0; j--) |
| { |
| CMNode node = list.item(j); |
| if (map.get(node) == null) |
| { |
| map.put(node, new ItemCount()); |
| } |
| } |
| int validitionCount = 0; |
| for (Iterator i = elementContent.iterator(); i.hasNext(); validitionCount++) |
| { |
| Object o = i.next(); |
| if (comparator.isElement(o)) |
| { |
| // test to see if the element is listed in the all group |
| // |
| CMNode matchingCMNode = null; |
| for (int j = list.getLength() - 1; j >= 0; j--) |
| { |
| CMNode node = list.item(j); |
| if (comparator.matches(o, node)) |
| { |
| matchingCMNode = node; |
| break; |
| } |
| } |
| if (matchingCMNode == null) |
| { |
| isPartiallyValid = false; |
| isValid = false; |
| break; |
| } |
| else |
| { |
| // test to see that the element occurs only once |
| // |
| ItemCount itemCount = (ItemCount)map.get(matchingCMNode); |
| if (itemCount != null) |
| { |
| if (itemCount.count > 0) |
| { |
| // we don't want to allow too many elements! |
| // we consider 'not enough' to be partially valid... but not 'too many' |
| isPartiallyValid = false; |
| break; |
| } |
| else |
| { |
| itemCount.count++; |
| } |
| } |
| } |
| } |
| } |
| if (isValid) |
| { |
| for (int j = list.getLength() - 1; j >= 0; j--) |
| { |
| CMNode node = list.item(j); |
| if (node.getNodeType() == CMNode.ELEMENT_DECLARATION) |
| { |
| CMContent content = (CMContent)node; |
| ItemCount itemCount = (ItemCount)map.get(node); |
| // System.out.print("content " + content.getNodeName() + " " + content.getMinOccur()); |
| if (itemCount.count < content.getMinOccur()) |
| { |
| isValid = false; |
| break; |
| } |
| } |
| } |
| } |
| if (result instanceof ElementPathRecordingResult && isPartiallyValid) |
| { |
| ((ElementPathRecordingResult)result).setPartialValidationCount(validitionCount); |
| } |
| result.isValid = isValid; |
| } |
| |
| |
| public void getOriginArray(CMElementDeclaration ed, List elementContent, ElementContentComparator comparator, ElementPathRecordingResult result) |
| { |
| CMNode[] cmNodeArray = null; |
| validate(ed, elementContent, comparator, result); |
| if (result.isValid) |
| { |
| CMDataType dataType = ed.getDataType(); |
| int size = elementContent.size(); |
| cmNodeArray = new CMNode[size]; |
| Vector originList = result.getElementOriginList(); |
| int originListSize = originList.size(); |
| int originListIndex = 0; |
| for (int i = 0; i < size; i++) |
| { |
| Object o = elementContent.get(i); |
| if (comparator.isElement(o)) |
| { |
| if (originListIndex < originListSize) |
| { |
| cmNodeArray[i] = (CMNode)originList.get(originListIndex); |
| originListIndex++; |
| } |
| } |
| else if (comparator.isPCData(o)) |
| { |
| cmNodeArray[i] = dataType; |
| } |
| // else the CMNode at this index is null |
| } |
| result.setOriginArray(cmNodeArray); |
| } |
| } |
| |
| private void collectNamedArcs(GraphNode node, List namedArcList, int indent) |
| { |
| //printlnIndented(indent, "GraphNode:" + node.name + decoration); |
| indent += 2; |
| for (Iterator i = node.arcList.iterator(); i.hasNext() ;) |
| { |
| Arc arc = (Arc)i.next(); |
| //printlnIndented(indent, "Arc:" + arc.name + " (" + arc.kind + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| if (arc.kind == Arc.ELEMENT) |
| { |
| if (!namedArcList.contains(arc)) |
| { |
| namedArcList.add(arc); |
| collectNamedArcs(arc.node, namedArcList, indent + 2); |
| } |
| } |
| else if (arc.kind != Arc.REPEAT && arc.kind != Arc.OPTIONAL) |
| { |
| collectNamedArcs(arc.node, namedArcList, indent + 2); |
| } |
| } |
| } |
| |
| |
| private List getMatchingArcs(CMElementDeclaration ed, String elementName) |
| { |
| List arcList = new ArrayList(); |
| GraphNode graphNode = lookupOrCreateGraph(ed); |
| if (elementName == null) |
| { |
| // here we add the 'root' arc |
| for (Iterator i = graphNode.arcList.iterator(); i.hasNext() ;) |
| { |
| Arc arc = (Arc)i.next(); |
| if (arc.kind == Arc.PREV_IN) |
| { |
| arcList.add(arc); |
| break; |
| } |
| } |
| } |
| else |
| { |
| List namedArcs = new ArrayList(); |
| collectNamedArcs(graphNode, namedArcs, 0); |
| for (Iterator i = namedArcs.iterator(); i.hasNext(); ) |
| { |
| Arc arc = (Arc)i.next(); |
| if (arc.cmNode != null && elementName.equals(arc.cmNode.getNodeName())) |
| { |
| arcList.add(arc); |
| } |
| } |
| } |
| return arcList; |
| } |
| |
| |
| private void collectNextSiblings(GraphNode node, List nextSiblingList, List namedArcList, List unamedArcList, int indent) |
| { |
| //printlnIndented(indent, "GraphNode:" + node.name + decoration); |
| indent += 2; |
| for (Iterator i = node.arcList.iterator(); i.hasNext(); ) |
| { |
| Arc arc = (Arc)i.next(); |
| if (arc.kind == Arc.ELEMENT) |
| { |
| if (!namedArcList.contains(arc)) |
| { |
| if (arc.cmNode != null) |
| { |
| nextSiblingList.add(arc.cmNode); |
| if (arc.cmNode.getNodeType() == CMNode.ELEMENT_DECLARATION || |
| arc.cmNode.getNodeType() == CMNode.ANY_ELEMENT) |
| { |
| namedArcList.add(arc); |
| CMContent cmNode = (CMContent)arc.cmNode; |
| if (cmNode.getMinOccur() == 0) |
| { |
| unamedArcList = new ArrayList(); |
| collectNextSiblings(arc.node, nextSiblingList, namedArcList, unamedArcList, indent + 2); |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| if (!unamedArcList.contains(arc)) |
| { |
| unamedArcList.add(arc); |
| collectNextSiblings(arc.node, nextSiblingList, namedArcList, unamedArcList, indent + 2); |
| } |
| } |
| } |
| } |
| |
| public CMNode[] getNextSiblings(CMElementDeclaration ed, String elementName) |
| { |
| List arcList = getMatchingArcs(ed, elementName); |
| List nextSiblingList = new ArrayList(); |
| for (Iterator i = arcList.iterator(); i.hasNext(); ) |
| { |
| Arc arc = (Arc)i.next(); |
| collectNextSiblings(arc.node, nextSiblingList, new ArrayList(), new ArrayList(), 0); |
| } |
| CMNode[] result = new CMNode[nextSiblingList.size()]; |
| nextSiblingList.toArray(result); |
| //System.out.print("getNextSibling(" +elementName + ")"); |
| //for (int i = 0; i < result.length; i++) |
| //{ |
| // System.out.print("[" + result[i].getNodeName() + "]"); |
| //} |
| //System.out.println(); |
| return result; |
| } |
| |
| /** |
| * |
| */ |
| public static class Result |
| { |
| public boolean isValid = true; |
| public int errorIndex = -1; |
| public String errorMessage; |
| public boolean isRepeatTraversed; // detects if a repeat has been traversed |
| |
| public boolean canPush(Arc arc) |
| { |
| return true; |
| } |
| |
| public void push(Arc arc) |
| { |
| // overide this method to record traversed nodes |
| } |
| public void pop(Arc arc) |
| { |
| // overide this method to record traversed nodes |
| } |
| public CMNode[] getOriginArray() |
| { |
| return null; |
| } |
| } |
| |
| /** |
| * |
| */ |
| public static class ElementPathRecordingResult extends Result |
| { |
| protected List activeItemCountList = new ArrayList(); |
| protected List inactiveItemCountList = new ArrayList(); |
| protected Vector elementOriginStack = new Vector(); |
| protected CMNode[] originArray = null; |
| protected int partialValidationCount = 0; |
| |
| |
| // this method is used to support facet counts |
| // |
| public boolean canPush(Arc arc) |
| { |
| boolean result = true; |
| try |
| { |
| if (arc.kind == Arc.REPEAT) |
| { |
| if (arc.cmNode instanceof CMContent) |
| { |
| CMContent content = (CMContent)arc.cmNode; |
| |
| // we only need to do 'special' facet checking if the maxOccurs is > 1 |
| // values of '0' and '-1' (unbounded) work 'for free' without any special checking |
| // |
| if (content.getMaxOccur() > 1) |
| { |
| ItemCount itemCount = (ItemCount)activeItemCountList.get(activeItemCountList.size() - 1); |
| |
| // here we need to compute if we can do another repeat |
| // if we increase the repeat count by '1' will this violate the maxOccurs |
| // |
| if (itemCount.count + 1 >= content.getMaxOccur()) |
| { |
| result = false; |
| } |
| } |
| //System.out.println("canPush REPEAT (" + itemCount.count + ")" + content.getNodeName() + " result= " + result); |
| } |
| } |
| } |
| catch (Exception e) |
| { |
| e.printStackTrace(); |
| } |
| //System.out.flush(); |
| return result; |
| } |
| |
| public void push(Arc arc) |
| { |
| if (arc.kind == Arc.ELEMENT) |
| { |
| //System.out.println("[X]push(" + arc.kind + ")" + arc.cmNode.getNodeName()); |
| elementOriginStack.add(arc.cmNode); |
| partialValidationCount = Math.max(elementOriginStack.size(), partialValidationCount); |
| } |
| else if (arc.kind == Arc.PREV_IN) |
| { |
| //System.out.println("[X]push(" + arc.kind + ")" + arc.cmNode.getNodeName()); |
| activeItemCountList.add(new ItemCount()); |
| } |
| else if (arc.kind == Arc.OUT_NEXT) |
| { |
| //System.out.println("[X]push(" + arc.kind + ")" + arc.cmNode.getNodeName() + "[" + arc + "]"); |
| int size = activeItemCountList.size(); |
| ItemCount itemCount = (ItemCount)activeItemCountList.get(size - 1); |
| activeItemCountList.remove(size - 1); |
| inactiveItemCountList.add(itemCount); |
| } |
| else if (arc.kind == Arc.REPEAT) |
| { |
| //System.out.println("[X]push(" + arc.kind + ")" + arc.cmNode.getNodeName()); |
| ItemCount itemCount = (ItemCount)activeItemCountList.get(activeItemCountList.size() - 1); |
| itemCount.count++; |
| //System.out.println("repeat(" + itemCount.count + ")" + arc.cmNode.getNodeName()); |
| } |
| } |
| |
| public void pop(Arc arc) |
| { |
| if (arc.kind == Arc.ELEMENT) |
| { |
| //System.out.println("[X]pop(" + arc.kind + ")" + arc.cmNode.getNodeName()); |
| int size = elementOriginStack.size(); |
| elementOriginStack.remove(size - 1); |
| } |
| else if (arc.kind == Arc.PREV_IN) |
| { |
| //System.out.println("[X]pop(" + arc.kind + ")" + arc.cmNode.getNodeName()); |
| activeItemCountList.remove(activeItemCountList.size() - 1); |
| } |
| else if (arc.kind == Arc.OUT_NEXT) |
| { |
| //System.out.println("[X]pop(" + arc.kind + ")" + arc.cmNode.getNodeName()); |
| int size = inactiveItemCountList.size(); |
| ItemCount itemCount = (ItemCount)inactiveItemCountList.get(size - 1); |
| inactiveItemCountList.remove(size - 1); |
| activeItemCountList.add(itemCount); |
| } |
| else if (arc.kind == Arc.REPEAT) |
| { |
| //System.out.println("[X]pop(" + arc.kind + ")" + arc.cmNode.getNodeName()); |
| ItemCount itemCount = (ItemCount)activeItemCountList.get(activeItemCountList.size() - 1); |
| itemCount.count--; |
| } |
| } |
| |
| public Vector getElementOriginList() |
| { |
| return elementOriginStack; |
| } |
| |
| public CMNode[] getOriginArray() |
| { |
| return originArray; |
| } |
| |
| public void setOriginArray(CMNode[] originArray) |
| { |
| this.originArray = originArray; |
| } |
| |
| public int getPartialValidationCount() |
| { |
| return partialValidationCount; |
| } |
| |
| public void setPartialValidationCount(int partialValidationCount) |
| { |
| this.partialValidationCount = partialValidationCount; |
| } |
| } |
| |
| /** |
| * |
| */ |
| public static class PathRecordingResult extends Result |
| { |
| protected Vector arcList = new Vector(); |
| |
| public void push(Arc arc) |
| { |
| arcList.add(arc); |
| } |
| |
| public void pop(Arc arc) |
| { |
| int size = arcList.size(); |
| arcList.remove(size - 1); |
| } |
| |
| public List getArcList() |
| { |
| List list = new Vector(); |
| for (Iterator iterator = arcList.iterator(); iterator.hasNext(); ) |
| { |
| Arc arc = (Arc)iterator.next(); |
| if (arc.kind == Arc.ELEMENT) |
| { |
| list.add(arc); |
| } |
| } |
| return list; |
| } |
| |
| public MatchModelNode getMatchModel() |
| { |
| MatchModelNodeBuilder builder = new MatchModelNodeBuilder(arcList); |
| builder.buildMatchModel(); |
| return builder.getRoot(); |
| } |
| } |
| |
| /** |
| * |
| */ |
| public static class MatchModelNode |
| { |
| public CMNode cmNode; |
| public List children = new Vector(); |
| public Object data; |
| |
| public MatchModelNode(MatchModelNode parent, CMNode cmNode) |
| { |
| this.cmNode = cmNode; |
| } |
| |
| public void printModel(int indent) |
| { |
| //String cmNodeName = cmNode != null ? cmNode.getNodeName() : "null"; |
| //printlnIndented(indent, "MatchModelNode : " + cmNodeName); |
| for (Iterator iterator = children.iterator(); iterator.hasNext(); ) |
| { |
| MatchModelNode child = (MatchModelNode)iterator.next(); |
| child.printModel(indent + 2); |
| } |
| } |
| } |
| |
| public static class MatchModelNodeBuilder |
| { |
| protected List arcList; |
| protected List stack = new Vector(); |
| protected MatchModelNode root; |
| protected MatchModelNode current; |
| |
| public MatchModelNodeBuilder(List arcList) |
| { |
| this.arcList = arcList; |
| root = new MatchModelNode(null, null); |
| push(root); |
| } |
| |
| protected void push(MatchModelNode node) |
| { |
| current = node; |
| stack.add(node); |
| } |
| |
| protected void pop() |
| { |
| int size = stack.size(); |
| stack.remove(size - 1); |
| current = (MatchModelNode)stack.get(size - 2); |
| } |
| |
| public boolean isCMGroup(CMNode cmNode) |
| { |
| return cmNode != null && cmNode.getNodeType() == CMNode.GROUP; |
| } |
| |
| public void buildMatchModel() |
| { |
| for (Iterator iterator = arcList.iterator(); iterator.hasNext(); ) |
| { |
| Arc arc = (Arc)iterator.next(); |
| |
| if (arc.kind == Arc.ELEMENT) |
| { |
| current.children.add(new MatchModelNode(current, arc.cmNode)); |
| } |
| else if (arc.kind == Arc.PREV_IN) |
| { |
| if (isCMGroup(arc.cmNode)) |
| { |
| MatchModelNode newModelNode = new MatchModelNode(current, arc.cmNode); |
| current.children.add(newModelNode); |
| push(newModelNode); |
| } |
| } |
| else if (arc.kind == Arc.OUT_NEXT) |
| { |
| if (isCMGroup(arc.cmNode)) |
| { |
| pop(); |
| } |
| } |
| else if (arc.kind == Arc.REPEAT) |
| { |
| if (isCMGroup(arc.cmNode)) |
| { |
| pop(); |
| MatchModelNode newModelNode = new MatchModelNode(current, arc.cmNode); |
| current.children.add(newModelNode); |
| push(newModelNode); |
| } |
| } |
| } |
| } |
| |
| public MatchModelNode getRoot() |
| { |
| return root; |
| } |
| } |
| |
| |
| /** |
| * |
| */ |
| public interface ElementContentComparator |
| { |
| public boolean isIgnorable(Object o); |
| public boolean isPCData(Object o); |
| public boolean isElement(Object o); |
| public boolean matches(Object o, CMNode cmNode); |
| } |
| |
| /** |
| * A linked list |
| */ |
| public static class ElementList |
| { |
| protected Object head; |
| protected ElementList tail; |
| |
| public static ElementList create(List v) |
| { |
| ElementList first = null; |
| ElementList prev = null; |
| |
| for (Iterator iterator = v.iterator(); iterator.hasNext(); ) |
| { |
| Object o = iterator.next(); |
| if (o != null) |
| { |
| ElementList list = new ElementList(); |
| list.head = o; |
| |
| if (prev != null) |
| { |
| prev.tail = list; |
| } |
| else |
| { |
| first = list; |
| } |
| prev = list; |
| } |
| } |
| return first; |
| } |
| |
| |
| public String toString() |
| { |
| String string = "[" + head + "],"; //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| if (tail != null) |
| { |
| string += tail.toString(); |
| } |
| |
| return string; |
| } |
| } |
| |
| /** |
| * StringElementContentComparator |
| */ |
| public static class StringElementContentComparator implements ElementContentComparator |
| { |
| public boolean isIgnorable(Object o) |
| { |
| String string = o.toString(); |
| return string.startsWith("!") || string.startsWith("?"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| public boolean isPCData(Object o) |
| { |
| String string = o.toString(); |
| return string.startsWith("'") || string.startsWith("\""); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| public boolean isElement(Object o) |
| { |
| return !isIgnorable(o) && !isPCData(o); |
| } |
| |
| public boolean matches(Object o, CMNode cmNode) |
| { |
| boolean result = false; |
| if (cmNode.getNodeType() == CMNode.ELEMENT_DECLARATION) |
| { |
| CMElementDeclaration element = (CMElementDeclaration)cmNode; |
| String name = o.toString(); |
| int index = name.indexOf("]"); //$NON-NLS-1$ |
| if (index != -1) |
| { |
| name = name.substring(index + 1); |
| } |
| result = name.equalsIgnoreCase(element.getElementName()); |
| |
| // TODO... here's we consider substitution groups... revisit to see if this should be moved into validator code |
| if (!result) |
| { |
| CMNodeList cmNodeList = (CMNodeList)element.getProperty("SubstitutionGroup"); //$NON-NLS-1$ |
| if (cmNodeList != null) |
| { |
| int cmNodeListLength = cmNodeList.getLength(); |
| if (cmNodeListLength > 1) |
| { |
| for (int i = 0; i < cmNodeListLength; i++) |
| { |
| CMElementDeclaration alternativeCMElementDeclaration = (CMElementDeclaration)cmNodeList.item(i); |
| String altName = alternativeCMElementDeclaration.getElementName(); |
| result = name.equalsIgnoreCase(altName); |
| if (result) |
| { |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| else if (cmNode.getNodeType() == CMNode.ANY_ELEMENT) |
| { |
| String string = o.toString(); |
| if (string.equals("*")) //$NON-NLS-1$ |
| { |
| result = true; |
| } |
| else |
| { |
| CMAnyElement anyElement = (CMAnyElement)cmNode; |
| String anyElementURI = anyElement.getNamespaceURI(); |
| if (anyElementURI != null) |
| { |
| if (anyElementURI.equals("##any")) //$NON-NLS-1$ |
| { |
| result = true; |
| } |
| else if (anyElementURI.equals("##other")) //$NON-NLS-1$ |
| { |
| result = true; |
| CMDocument cmDocument = (CMDocument)anyElement.getProperty("CMDocument"); //$NON-NLS-1$ |
| if (cmDocument != null) |
| { |
| String excludedURI = (String)cmDocument.getProperty("http://org.eclipse.wst/cm/properties/targetNamespaceURI"); //$NON-NLS-1$ |
| if (excludedURI != null) |
| { |
| String specifiedURI = getURIForContentSpecification(string); |
| if (specifiedURI != null && excludedURI.equals(specifiedURI)) |
| { |
| result = false; |
| } |
| } |
| } |
| } |
| else if (anyElementURI.equals("##targetNamespace")) //$NON-NLS-1$ |
| { |
| result = true; |
| CMDocument cmDocument = (CMDocument)anyElement.getProperty("CMDocument"); //$NON-NLS-1$ |
| if (cmDocument != null) |
| { |
| String targetNamespaceURI = (String)cmDocument.getProperty("http://org.eclipse.wst/cm/properties/targetNamespaceURI"); //$NON-NLS-1$ |
| String specifiedURI = getURIForContentSpecification(string); |
| if (specifiedURI != null && !targetNamespaceURI.equals(specifiedURI)) |
| { |
| result = false; |
| } |
| } |
| } |
| else |
| { |
| result = true; |
| String specifiedURI = getURIForContentSpecification(string); |
| if (specifiedURI != null && !anyElementURI.equals(specifiedURI)) |
| { |
| result = false; |
| } |
| } |
| } |
| else |
| { |
| result = true; |
| } |
| } |
| } |
| return result; |
| } |
| |
| |
| protected String getURIForContentSpecification(String specification) |
| { |
| String result = null; |
| int index = specification.indexOf("]"); //$NON-NLS-1$ |
| if (index != -1) |
| { |
| result = specification.substring(1, index); |
| } |
| return result; |
| } |
| } |
| |
| public static List createStringList(String arg[], int startIndex) |
| { |
| Vector v = new Vector(); |
| for (int i = startIndex; i < arg.length; i++) |
| { |
| v.add(arg[i]); |
| } |
| return v; |
| } |
| } |