package edu.stanford.nlp.trees.tregex; import edu.stanford.nlp.trees.Tree; import java.util.Iterator; import java.util.List; import java.util.Map; class CoordinationPattern extends TregexPattern { private boolean isConj; private List<TregexPattern> children; /* if isConj is true, then it is an "AND" ; if it is false, it is an "OR".*/ public CoordinationPattern(List<TregexPattern> children, boolean isConj) { if (children.size() < 2) { throw new RuntimeException("Coordination node must have at least 2 children."); } this.children = children; this.isConj = isConj; // System.out.println("Made " + (isConj ? "and " : "or ") + "node with " + children.size() + " children."); } @Override public List<TregexPattern> getChildren() { return children; } @Override public String localString() { return (isConj ? "and" : "or"); } @Override public String toString() { StringBuilder sb = new StringBuilder(); if (isConj) { for (TregexPattern node : children) { sb.append(node.toString()); } } else { sb.append('['); for (Iterator<TregexPattern> iter = children.iterator(); iter.hasNext();) { TregexPattern node = iter.next(); sb.append(node.toString()); if (iter.hasNext()) { sb.append(" |"); } } sb.append(']'); } return sb.toString(); } @Override public TregexMatcher matcher(Tree root, Tree tree, Map<String, Tree> namesToNodes, VariableStrings variableStrings) { return new CoordinationMatcher(this, root, tree, namesToNodes,variableStrings); } private static class CoordinationMatcher extends TregexMatcher { private TregexMatcher[] children; private final CoordinationPattern myNode; private int currChild; private final boolean considerAll; // do all con/dis-juncts have to be considered to determine a match? // i.e. true if conj and not negated or disj and negated public CoordinationMatcher(CoordinationPattern n, Tree root, Tree tree, Map<String, Tree> namesToNodes, VariableStrings variableStrings) { super(root, tree, namesToNodes,variableStrings); myNode = n; children = new TregexMatcher[myNode.children.size()]; for (int i = 0; i < children.length; i++) { TregexPattern node = myNode.children.get(i); children[i] = node.matcher(root, tree, namesToNodes,variableStrings); } currChild = 0; considerAll = myNode.isConj ^ myNode.isNegated(); } @Override void resetChildIter() { currChild = 0; for (int i = 0; i < children.length; i++) { children[i].resetChildIter(); } } @Override void resetChildIter(Tree tree) { this.tree = tree; currChild = 0; for (int i = 0; i < children.length; i++) { children[i].resetChildIter(tree); } } // find the next local match @Override public boolean matches() { // also known as "FUN WITH LOGIC" if (considerAll) { // these are the cases where all children must be considered to match // first iterate backward (if necessary) to get beginning // of next possible match for (; currChild >= 0; currChild--) { if (myNode.isNegated() != children[currChild].matches()) { break; } else { children[currChild].resetChildIter(); } } if (currChild < 0) { return myNode.isOptional(); } // now try to satisfy the rest (if any) while (currChild + 1 < children.length) { currChild++; if (myNode.isNegated() == children[currChild].matches()) { currChild = -1; // this node will not match (unless reset) return myNode.isOptional(); } } // a negated node should only match once (before being reset) if (myNode.isNegated()) { currChild = -1; } return true; } else { // these are the cases where a single child node can make you match for (; currChild < children.length; currChild++) { if (myNode.isNegated() != children[currChild].matches()) { // a negated node should only match once (before being reset) if (myNode.isNegated()) { currChild = children.length; } return true; } } if (myNode.isNegated()) { currChild = children.length; } return myNode.isOptional(); } } @Override public Tree getMatch() { // only DescriptionNodes can match throw new UnsupportedOperationException(); } } // end private class CoordinationMatcher private static final long serialVersionUID = -7797084959452603087L; }