//Dstl (c) Crown Copyright 2017
package uk.gov.dstl.baleen.uima.grammar;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.gov.dstl.baleen.types.language.PhraseChunk;
import uk.gov.dstl.baleen.types.language.WordToken;
/**
* A node in the parse tree.
*/
public final class ParseTreeNode {
private static final Logger LOGGER = LoggerFactory.getLogger(ParseTreeNode.class);
private final PhraseChunk chunk;
private ParseTreeNode parent;
private final List<ParseTreeNode> children = new LinkedList<>();
private final List<WordToken> words = new LinkedList<>();
/**
* Instantiates a node from a chunk
*
* @param chunk
* the chunk
*/
public ParseTreeNode(PhraseChunk chunk) {
this.chunk = chunk;
}
/**
* Instantiates a new node based on a set of children.
*
* @param children
* the children
*/
public ParseTreeNode(List<ParseTreeNode> children) {
this.chunk = null;
addAllChildren(children);
}
/**
* Traverse children (down)
*
* @param consumer
* the consumer
*/
public void traverseChildren(Consumer<List<ParseTreeNode>> consumer) {
if (children != null && !children.isEmpty()) {
consumer.accept(children);
children.forEach(c -> c.traverseChildren(consumer));
}
}
/**
* Traverse parent (up the tree)
*
* @param consumer
* the consumer
*/
public void traverseParent(BiPredicate<ParseTreeNode, ParseTreeNode> consumer) {
if (parent != null) {
final boolean test = consumer.test(parent, this);
if (test) {
parent.traverseParent(consumer);
}
}
}
/**
* Checks if is root.
*
* @return true, if is root
*/
public boolean isRoot() {
return chunk == null;
}
/**
* Gets the chunk.
*
* @return the chunk
*/
public PhraseChunk getChunk() {
return chunk;
}
/**
* Sets the parent.
*
* @param parent
* the new parent
*/
public void setParent(ParseTreeNode parent) {
this.parent = parent;
}
/**
* Gets the children.
*
* @return the children
*/
public List<ParseTreeNode> getChildren() {
return children;
}
/**
* Checks for children.
*
* @return true, if successful
*/
public boolean hasChildren() {
return !children.isEmpty();
}
/**
* Gets the parent.
*
* @return the parent
*/
public ParseTreeNode getParent() {
return parent;
}
/**
* Adds a child.
*
* @param child
* the child
*/
public void addChild(ParseTreeNode child) {
children.add(child);
child.setParent(this);
}
/**
* Adds all children.
*
* @param children
* the children
*/
private void addAllChildren(List<ParseTreeNode> children) {
children.forEach(this::addChild);
}
/**
* Adds the words which are at this node (not its children)
*
* @param word
* the word
*/
public void addWords(Collection<WordToken> word) {
if (word != null) {
words.addAll(word);
}
}
/**
* Gets the words.
*
* @return the words
*/
public List<WordToken> getWords() {
return words;
}
@Override
public String toString() {
if (chunk != null) {
return String.format("%s [%s]", chunk.getCoveredText(), chunk.getChunkType());
} else {
return "No chunk";
}
}
/**
* Contains word.
*
* @param filter
* the filter
* @return true, if successful
*/
public boolean containsWord(Predicate<WordToken> filter) {
if (words != null && !words.isEmpty()) {
final boolean result = words.stream().anyMatch(filter);
if (result) {
return true;
}
}
if (children != null) {
return children.stream().anyMatch(c -> c.containsWord(filter));
}
return false;
}
/**
* Log the tree in an indent (pretty) fashion
*
* @param indent
* the indent
*/
public void log(String indent) {
String wordString = "";
if (words != null) {
wordString = getWords().stream().map(WordToken::getCoveredText).collect(Collectors.joining(","));
}
String chunkString = "";
if (chunk != null) {
chunkString = chunk.getChunkType();
}
String parentString = "";
if (parent != null) {
parentString = getParent().toString();
}
LOGGER.info("{}{}: {}: {}", indent, chunkString, wordString, parentString);
if (children != null) {
String childIndent = "\t" + indent;
for (ParseTreeNode c : children) {
c.log(childIndent);
}
}
}
}