package edu.stanford.nlp.trees.tregex.tsurgeon;
import java.util.List;
import java.util.Map;
import edu.stanford.nlp.trees.Tree;
import edu.stanford.nlp.trees.tregex.TregexMatcher;
import edu.stanford.nlp.util.Generics;
/**
* Given the start and end children of a particular node, takes all
* children between start and end (including the endpoints) and
* combines them in a new node with the given label.
*
* @author John Bauer
*/
public class CreateSubtreeNode extends TsurgeonPattern {
private AuxiliaryTree auxTree;
public CreateSubtreeNode(TsurgeonPattern start, AuxiliaryTree tree) {
this(start, null, tree);
}
public CreateSubtreeNode(TsurgeonPattern start, TsurgeonPattern end, AuxiliaryTree tree) {
super("combineSubtrees",
(end == null) ? new TsurgeonPattern[] { start } : new TsurgeonPattern[] { start, end });
this.auxTree = tree;
findFoot();
}
/**
* We want to support a command syntax where a simple node label can
* be given (i.e., without using a tree literal).
*
* Check if this syntax is being used, and simulate a foot if so.
*/
private void findFoot() {
if (auxTree.foot == null) {
if (!auxTree.tree.isLeaf())
throw new TsurgeonParseException("No foot node found for " + auxTree);
// Pretend this leaf is a foot node
auxTree.foot = auxTree.tree;
}
}
@Override
public TsurgeonMatcher matcher(Map<String,Tree> newNodeNames, CoindexationGenerator coindexer) {
return new Matcher(newNodeNames, coindexer);
}
private class Matcher extends TsurgeonMatcher {
public Matcher(Map<String,Tree> newNodeNames, CoindexationGenerator coindexer) {
super(CreateSubtreeNode.this, newNodeNames, coindexer);
}
/**
* Combines all nodes between start and end into one subtree, then
* replaces those nodes with the new subtree in the corresponding
* location under parent
*/
@Override
public Tree evaluate(Tree tree, TregexMatcher tregex) {
Tree startChild = childMatcher[0].evaluate(tree, tregex);
Tree endChild = (childMatcher.length == 2) ? childMatcher[1].evaluate(tree, tregex) : startChild;
Tree parent = startChild.parent(tree);
// sanity check
if (parent != endChild.parent(tree)) {
throw new TsurgeonRuntimeException("Parents did not match for trees when applied to " + this);
}
AuxiliaryTree treeCopy = auxTree.copy(this);
// Collect all the children of the parent of the node we care
// about. If the child is one of the nodes we care about, or
// between those two nodes, we add it to a list of inner children.
// When we reach the second endpoint, we turn that list of inner
// children into a new node using the newly created label. All
// other children are kept in an outer list, with the new node
// added at the appropriate location.
List<Tree> children = Generics.newArrayList();
List<Tree> innerChildren = Generics.newArrayList();
boolean insideSpan = false;
for (Tree child : parent.children()) {
if (child == startChild || child == endChild) {
if (!insideSpan && startChild != endChild) {
insideSpan = true;
innerChildren.add(child);
} else {
insideSpan = false;
innerChildren.add(child);
// All children have been collected; place these beneath the foot of the auxiliary tree
treeCopy.foot.setChildren(innerChildren);
children.add(treeCopy.tree);
}
} else if (insideSpan) {
innerChildren.add(child);
} else {
children.add(child);
}
}
parent.setChildren(children);
return tree;
}
}
}