/*
* Copyright 2007-2013
* Licensed under GNU Lesser General Public License
*
* This file is part of EpochX: genetic programming software for research
*
* EpochX is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EpochX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with EpochX. If not, see <http://www.gnu.org/licenses/>.
*
* The latest version is available from: http://www.epochx.org
*/
package org.epochx.grammar;
import java.util.*;
import org.apache.commons.lang.ObjectUtils;
/**
* A non-terminal node of a parse tree, that was constructed to satisfy a
* specific rule of a grammar. The underlying <code>GrammarRule</code> is
* provided at construction time. An instance's children are those
* <code>Symbol</code> objects that the non-terminal resolves to, as supported
* by the grammar rule.
*
* @see TerminalSymbol
* @see GrammarRule
*/
public class NonTerminalSymbol implements Symbol {
// The child nodes in the parse tree.
private List<Symbol> children;
// The associated grammar node.
private GrammarRule grammarRule;
/**
* Constructs a <code>NonTerminalSymbol</code> for the given
* <code>GrammarRule</code>.
*
* @param grammarRule the <code>GrammarRule</code> which this new object is
* representing an instance of.
*/
public NonTerminalSymbol(GrammarRule grammarRule) {
this(grammarRule, new ArrayList<Symbol>());
}
/**
* Constructs a <code>NonTerminalSymbol</code> for the given
* <code>GrammarRule</code> and with a list of child parse tree symbols.
*
* @param grammarRule the <code>GrammarRule</code> which this new object is
* representing an instance of
* @param children a list of <code>Symbol</code> instances which this
* <code>NonTerminalSymbol</code> resolves to, as supported by
* the grammar
* rule.
*/
public NonTerminalSymbol(GrammarRule grammarRule, List<Symbol> children) {
this.grammarRule = grammarRule;
this.children = children;
}
/**
* Overwrites the <code>Symbol</code> at the specified index. Note that the
* index must be a currently valid index.
*
* @param index the index of the <code>Symbol</code> to change.
* @param child the <code>Symbol</code> to set at the specified index.
*/
public void setChild(int index, Symbol child) {
// Make the change.
children.set(index, child);
}
/**
* Returns the <code>Symbol</code> at the specified index.
*
* @param index the index of the <code>Symbol</code> to return.
* @return the <code>Symbol</code> found at the given index.
*/
public Symbol getChild(int index) {
return children.get(index);
}
/**
* Appends the given <code>Symbol</code> to the list of child nodes.
*
* @param child the <code>Symbol</code> instance to append.
*/
public void addChild(Symbol child) {
// Make the change.
children.add(child);
}
/**
* Removes all currently set child <code>Symbols</code>.
*
* @return a <code>List</code> of the child <code>Symbol</code> instances
* that were removed.
*/
public List<Symbol> removeChildren() {
// Make the change.
children.clear();
return children;
}
/**
* Removes a the <code>Symbol</code> that is at the specified index.
*
* @param index the index of the <code>Symbol</code> to remove.
* @return the <code>Symbol</code> instance that was removed.
*/
public Symbol removeChild(int index) {
return children.remove(index);
}
/**
* Returns a reference to the underlying <code>List</code> of child
* <code>Symbol</code> instances for this non-terminal. Any changes to the
* returned list will be reflected in this symbol.
*
* @return a <code>List</code> of the child <code>Symbol</code> instances.
*/
public List<Symbol> getChildren() {
return children;
}
/**
* Overwrites this non-terminal's <code>List</code> of child
* <code>Symbols</code>.
*
* @param newChildren the <code>List</code> of child <code>Symbol</code>
* instances to set.
*/
public void setChildren(List<Symbol> newChildren) {
// Make the change.
children = newChildren;
}
/**
* Calculates and returns the number of non-terminal symbols that exist
* within the tree rooted at this non-terminal symbol, including this
* <code>Symbol</code>. The result should always be equal or greater than 1.
*
* @return a positive integer which is the count of the number of
* <code>NonTerminalSymbol</code> instances in the parse tree rooted
* at this <code>Symbol</code>, inclusive of this symbol.
*/
public int getNoNonTerminalSymbols() {
// Start by adding self.
int noNonTerminals = 1;
// Count all the non-terminals below each child.
for (Symbol child: children) {
if (child instanceof NonTerminalSymbol) {
noNonTerminals += ((NonTerminalSymbol) child).getNoNonTerminalSymbols();
}
}
assert (noNonTerminals >= 1);
return noNonTerminals;
}
/**
* Calculates and returns the number of non-terminal symbols that exist
* within the tree rooted at this non-terminal symbol which have the
* specified underlying <code>GrammarRule</code>. The count is inclusive of
* this <code>NonTerminalSymbol</code>. A <code>NonTerminalSymbol</code>
* <code>x</code>is included in the count if the following expression is
* <code>true</code>.
*
* <blockquote><code>
* this.getGrammarRule().equals(x.getGrammarRule())
* </code></blockquote>
*
* @param rule the <code>GrammarRule</code> that should be matched in all
* <code>NonTerminalSymbols</code> included in the count.
* @return the total number of non-terminal symbols that have a matching
* <code>GrammarRule</code>.
*/
public int getNoNonTerminalSymbols(GrammarRule rule) {
int noNonTerminals = 0;
// Start by adding self.
if (getGrammarRule().equals(rule)) {
noNonTerminals++;
}
// Count all the non-terminals below each child.
for (Symbol child: children) {
if (child instanceof NonTerminalSymbol) {
noNonTerminals += ((NonTerminalSymbol) child).getNoNonTerminalSymbols(rule);
}
}
return noNonTerminals;
}
/**
* Calculates and returns the number of terminal symbols that exist at the
* leaves of the parse tree rooted at this non-terminal symbol. The count
* should always be positive, and will only ever be zero in the case of an
* incomplete parse tree.
*
* @return an <code>int</code> which is the total number of terminal symbols
* in this parse tree.
*/
public int getNoTerminalSymbols() {
int noTerminals = 0;
// Count all the terminals below each child.
for (Symbol child: children) {
if (child instanceof TerminalSymbol) {
noTerminals++;
} else if (child instanceof NonTerminalSymbol) {
noTerminals += ((NonTerminalSymbol) child).getNoTerminalSymbols();
}
}
return noTerminals;
}
/**
* Calculates and returns the total number of <code>Symbol</code> instances
* that exist in the parse tree rooted at this non-terminal symbol,
* including this <code>Symbol</code> itself. The result should be equal to
* the sum of the results from the <code>getNoNonTerminalSymbols()</code>
* and <code>getNoTerminalSymbols()</code> methods.
*
* @return the total number of symbols that exist in the parse tree rooted
* at this <code>Symbol</code>.
*/
public int getNoSymbols() {
// Start by adding self.
int noSymbols = 1;
// Count all the symbols below each child.
for (Symbol child: children) {
if (child instanceof TerminalSymbol) {
noSymbols++;
} else if (child instanceof NonTerminalSymbol) {
noSymbols += ((NonTerminalSymbol) child).getNoSymbols();
}
}
return noSymbols;
}
/**
* Returns the number of direct child <code>Symbols</code> this non-terminal
* symbol has.
*
* @return an <code>int</code> which is the number of child symbols this
* non-terminal has.
*/
public int getNoChildren() {
return children.size();
}
/**
* Removes the nth non-terminal from the parse tree rooted at this
* <code>NonTerminalSymbol</code> instance, as counted using a pre-order
* traversal. Indexing starts at zero for this non-terminal symbol. As such,
* valid values of n must be greater than or equal to 1, since it is
* impossible to remove a symbol from itself.
*
* @param n an <code>int</code> with a value of 1 or greater which is the
* index of the <code>NonTerminalSymbol</code> that should be
* removed.
* @return the <code>NonTerminalSymbol</code> that was removed, or
* <code>null</code> if none were removed.
*/
public NonTerminalSymbol removeNthNonTerminal(int n) {
return removeNthNonTerminal(n, 0, null);
}
/*
* Recursive helper method for the removeNthNonTerminal method.
*/
private NonTerminalSymbol removeNthNonTerminal(int n, int current, GrammarRule rule) {
for (int i = 0; i < children.size(); i++) {
Symbol child = children.get(i);
if (child instanceof NonTerminalSymbol) {
NonTerminalSymbol nt = (NonTerminalSymbol) child;
boolean valid = false;
if ((rule == null) || rule.equals(nt.getGrammarRule())) {
valid = true;
}
if (valid && (n == current + 1)) {
// It is this child.
return (NonTerminalSymbol) removeChild(i);
} else {
final NonTerminalSymbol nth = nt.removeNthNonTerminal(n, (valid ? current + 1 : current), rule);
if (nth != null) {
return nth;
}
current += nt.getNoNonTerminalSymbols(rule);
}
}
}
return null;
}
/**
* Removes the nth non-terminal with the given <code>GrammarRule</code> from
* the parse tree rooted at this <code>NonTerminalSymbol</code> instance, as
* counted using a pre-order traversal. Indexing starts at zero and from
* this non-terminal symbol itself. It does not make sense to remove this
* <code>Symbol</code> from itself however so a value for <code>n</code> of
* <code>0</code> is only possible if the given <code>GrammarRule</code>
* does not match this <code>Symbol</code>.
*
* @param n an <code>int</code> which is the index of the
* <code>NonTerminalSymbol</code> with the given grammar rule that
* should be
* removed.
* @param grammarRule the <code>GrammarRule</code> that the symbol to be
* removed should have.
* @return the <code>NonTerminalSymbol</code> that was removed, or
* <code>null</code> if none were removed.
*/
public NonTerminalSymbol removeNthNonTerminal(int n, GrammarRule grammarRule) {
return removeNthNonTerminal(n, 0, grammarRule);
}
/**
* Returns the nth non-terminal from the parse tree rooted at this
* <code>NonTerminalSymbol</code>. Indexing starts at zero for this symbol
* as the root, and proceeds in a pre-order traversal of the tree.
*
* @param n the index of the non-terminal to return.
* @return the <code>NonTerminalSymbol</code> which was the nth in the parse
* tree.
*/
public NonTerminalSymbol getNthNonTerminal(int n) {
return getNthNonTerminal(n, 0);
}
/*
* Recursive helper method for the getNthNonTerminal method.
*/
private NonTerminalSymbol getNthNonTerminal(int n, int current) {
// Is this the one we're looking for?
if (n == current) {
return this;
}
for (Symbol child: children) {
if (child instanceof NonTerminalSymbol) {
NonTerminalSymbol nt = (NonTerminalSymbol) child;
NonTerminalSymbol nth = nt.getNthNonTerminal(n, current + 1);
if (nth != null) {
return nth;
}
current += nt.getNoNonTerminalSymbols();
}
}
return null;
}
/**
* Returns the nth terminal from the parse tree rooted at this
* <code>NonTerminalSymbol</code>. Indexing starts at zero and proceeds
* according to the order that terminals are met while performing a
* pre-order traversal of the tree from this symbol.
*
* @param n the index of the terminal to return.
* @return the <code>TerminalSymbol</code> which was the nth in the parse
* tree.
*/
public TerminalSymbol getNthTerminal(int n) {
final List<TerminalSymbol> terminals = getTerminalSymbols();
return terminals.get(n);
}
/**
* Returns the nth symbol from the parse tree rooted at this symbol.
* Indexing starts at zero for this, the root, and proceeds in a pre-order
* traversal of the tree until the nth symbol is found.
*
* @param n the index of the symbol to be returned.
* @return the nth symbol from this parse tree.
*/
public Symbol getNthSymbol(int n) {
return getNthSymbol(n, 0);
}
/*
* Recursive helper method for the getNthSymbol method.
*/
private Symbol getNthSymbol(int n, int current) {
// Is this the one we're looking for?
if (n == current) {
return this;
}
for (Symbol child: children) {
if (child instanceof NonTerminalSymbol) {
NonTerminalSymbol nt = (NonTerminalSymbol) child;
Symbol nth = nt.getNthSymbol(n, current + 1);
if (nth != null) {
return nth;
}
current += nt.getNoSymbols();
} else {
if (n == ++current) {
return child;
}
}
}
return null;
}
/**
* Overwrites the nth symbol in the parse tree rooted at this symbol.
* Indexing starts at zero for this, the root and proceeds in a pre-order
* traversal of the tree until the nth symbol is found. However, it is not
* possible to set the zeroth symbol since that would mean replacing this
* instance itself. To replace it, the <code>setNthSymbol</code> method
* should be called upon any parent <code>NonTerminalSymbol</code> or if it
* is the root of the whole tree then by using the replacement directly as
* the new parse tree.
*
* @param n the index of where to set the new symbol.
* @param newSymbol the replacement <code>Symbol</code> to set at the nth
* position.
*/
public void setNthSymbol(int n, Symbol newSymbol) {
setNthSymbol(n, newSymbol, 0);
}
/*
* Recursive helper method for the setNthSymbol method.
*/
private void setNthSymbol(int n, Symbol symbol, int current) {
int noChildren = getNoChildren();
for (int i = 0; i < noChildren; i++) {
if (current + 1 == n) {
setChild(i, symbol);
break;
}
if (children.get(i) instanceof NonTerminalSymbol) {
NonTerminalSymbol child = (NonTerminalSymbol) children.get(i);
int noChildSymbols = child.getNoSymbols();
// Only look at the subtree if it contains the right range of
// nodes.
if (n <= current + noChildSymbols) {
child.setNthSymbol(n, symbol, current + 1);
}
current += noChildSymbols;
} else {
// It's a terminal so just increment 1.
current++;
}
}
}
/**
* Returns a <code>List</code> of all the non-terminal symbols in the parse
* tree below this symbol, including this symbol itself.
*
* @return a <code>List</code> of <code>NonTerminalSymbol</code> instances
* from the parse tree rooted at this symbol.
*/
public List<NonTerminalSymbol> getNonTerminalSymbols() {
List<NonTerminalSymbol> nonTerminals = new ArrayList<NonTerminalSymbol>();
// Start by adding self.
nonTerminals.add(this);
// Add all the non-terminals below each child.
for (Symbol child: children) {
if (child instanceof NonTerminalSymbol) {
nonTerminals.addAll(((NonTerminalSymbol) child).getNonTerminalSymbols());
}
}
return nonTerminals;
}
/**
* Returns a <code>List</code> of the indexes of all the symbols in the
* parse tree rooted at this symbol that are instances of
* <code>NonTerminalSymbol</code>.
*
* @return a <code>List</code> of <code>Integers</code> which are the
* indexes of the non-terminal symbols in the parse tree rooted at
* this symbol.
*/
public List<Integer> getNonTerminalIndexes() {
return getNonTerminalIndexes(0);
}
/*
* Recursive helper method for the getNonTerminalIndexes method.
*/
private List<Integer> getNonTerminalIndexes(int index) {
List<Integer> nonTerminals = new ArrayList<Integer>();
// Start by adding self.
nonTerminals.add(index);
// Add all the non-terminals below each child.
for (Symbol child: children) {
if (child instanceof NonTerminalSymbol) {
NonTerminalSymbol nt = (NonTerminalSymbol) child;
nonTerminals.addAll(nt.getNonTerminalIndexes(index + 1));
index += nt.getNoSymbols();
} else {
index++;
}
}
return nonTerminals;
}
/**
* Returns a <code>List</code> of all the terminal symbols in the parse
* tree below this non-terminal symbol.
*
* @return a <code>List</code> of <code>TerminalSymbol</code> instances
* from the parse tree rooted at this symbol.
*/
public List<TerminalSymbol> getTerminalSymbols() {
List<TerminalSymbol> terminals = new ArrayList<TerminalSymbol>();
// Add all terminal children and terminals below a non-terminal child.
for (Symbol child: children) {
if (child instanceof TerminalSymbol) {
terminals.add((TerminalSymbol) child);
} else if (child instanceof NonTerminalSymbol) {
terminals.addAll(((NonTerminalSymbol) child).getTerminalSymbols());
}
}
return terminals;
}
/**
* Returns a <code>List</code> of all <code>Symbol</code> instances from the
* parse tree rooted at this symbol.
*
* @return a <code>List</code> of <code>Symbol</code> instances from the
* parse tree rooted at this symbol.
*/
public List<Symbol> getAllSymbols() {
List<Symbol> symbols = new ArrayList<Symbol>();
symbols.add(this);
for (Symbol child: children) {
if (child instanceof TerminalSymbol) {
symbols.add(child);
} else if (child instanceof NonTerminalSymbol) {
symbols.addAll(((NonTerminalSymbol) child).getAllSymbols());
}
}
return symbols;
}
/**
* Returns this non-terminal symbol's grammar rule.
*
* @return the underlying grammar rule this non-terminal symbol is defined
* by.
*/
public GrammarRule getGrammarRule() {
return grammarRule;
}
/**
* Returns the depth of the parse tree rooted at this
* <code>NonTerminalSymbol</code>. The depth is considered to be the maximum
* number of steps down the tree from this symbol to a terminal symbol. A
* tree made up of one non-terminal symbol with all terminal children will
* have a depth of <code>1</code>.
*
* @return the depth of the parse tree rooted at this symbol.
*/
public int getDepth() {
int maxChildDepth = 0;
for (Symbol child: children) {
int childDepth;
if (child instanceof NonTerminalSymbol) {
childDepth = ((NonTerminalSymbol) child).getDepth() + 1;
} else {
childDepth = 1;
}
if (childDepth > maxChildDepth) {
maxChildDepth = childDepth;
}
}
return maxChildDepth;
}
/**
* Returns a string representation of this non-terminal symbol, which is a
* conjunction of the string representations of each child symbol.
*
* @return a <code>String</code> representation of this object.
*/
@Override
public String toString() {
StringBuilder buffer = new StringBuilder(children.size());
for (Symbol c: children) {
buffer.append(c.toString());
}
return buffer.toString();
}
/**
* Constructs and returns a copy of this non-terminal symbol. Each child
* <code>Symbol</code> is itself cloned, but the grammar rule is shallow
* copied.
*
* @return a copy of this non-terminal symbol.
*/
@Override
public NonTerminalSymbol clone() {
NonTerminalSymbol clone = null;
try {
clone = (NonTerminalSymbol) super.clone();
} catch (CloneNotSupportedException e) {
// This shouldn't ever happen - if it does then everything is
// going to blow up anyway.
assert false;
}
// Copy cloned child symbols.
clone.children = new ArrayList<Symbol>();
for (Symbol c: children) {
clone.children.add(c.clone());
}
// Shallow copy the grammar rules.
clone.grammarRule = grammarRule;
return clone;
}
/**
* Tests the given <code>Object</code> for equality with this non-terminal
* symbol. They will be considered equal if the given <code>Object</code> is
* an instance of <code>NonTerminalSymbol</code>, all their child symbols
* are equal according to the contract of their <code>equals</code> method,
* in the same order, and their grammar rules refer to the same instance.
*
* @param obj the <code>Object</code> to test for equality.
* @return <code>true</code> if the given <code>Object</code> is equal to
* this non-terminal according to the contract outlined above and
* <code>false</code> otherwise.
*/
@Override
public boolean equals(Object obj) {
boolean equal = true;
if ((obj != null) && (obj instanceof NonTerminalSymbol)) {
NonTerminalSymbol otherSymbol = (NonTerminalSymbol) obj;
if (getGrammarRule() == otherSymbol.getGrammarRule()) {
for (int i = 0; i < children.size(); i++) {
Symbol thatChild = otherSymbol.getChild(i);
Symbol thisChild = getChild(i);
if (!ObjectUtils.equals(thisChild, thatChild)) {
equal = false;
break;
}
}
} else {
equal = false;
}
} else {
equal = false;
}
return equal;
}
}