/*
* Copyright 2006, United States Government as represented by the Administrator
* for the National Aeronautics and Space Administration. No copyright is
* claimed in the United States under Title 17, U.S. Code. All Other Rights
* Reserved.
*/
package gov.nasa.ial.mde.solver.symbolic;
import gov.nasa.ial.mde.util.Comparison;
import gov.nasa.ial.mde.util.StringSplitter;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* A parse node.
*
* @author Dr. Robert Shelton
* @version 1.0
* @since 1.0
*/
public class ParseNode implements Comparison {
/** The operator. */
int operator;
/** The value. */
Double value = null;
/** The string associated string. */
String theString = null;
/** The parent node. */
ParseNode parent = null;
/** The children of this node. */
ParseNode[] children = null;
/** The associated action. */
static Action theAction = new Action();
/** Flag to indicate the node is bad. */
public boolean badFlag = false;
/**
* Constructs a <code>ParseNode</code> with no operator and no children.
*/
public ParseNode() {
operator = Action.NO_OP;
children = null;
} // end ParseNode (empty leaf)
/**
* Constructs a <code>ParseNode</code> with the specified number of children
* and operator.
*
* @param nChildren number of children to this node.
* @param op the operator.
*/
public ParseNode(int nChildren, int op) {
children = new ParseNode[nChildren];
operator = op;
theString = null;
} // end ParseNode (internal node)
/**
* Constructs a <code>ParseNode</code> with the specified quantity as a
* String.
*
* @param s the string Quantity.
*/
public ParseNode(String s) {
ParseNode p = buildTree(new Quantity(s));
if (p == null) {
badFlag = true;
theString = s;
children = null;
value = null;
operator = Action.CORRUPTED;
return;
} // end if
theString = p.theString;
children = p.children;
operator = p.operator;
value = p.value;
badFlag = p.badFlag;
} // end ParseNode (general case)
/* (non-Javadoc)
* @see gov.nasa.ial.mde.util.Comparison#compare(java.lang.Object, java.lang.Object)
*/
public int compare(Object d1, Object d2) {
StringSplitter s1 = (StringSplitter)d1;
StringSplitter s2 = (StringSplitter)d2;
return s1.pieces[0].length() - s2.pieces[0].length();
} // end compare
/**
* Evaluate the node.
*
* @return the value of the evaluation.
*/
double eval() {
if (value != null)
return value.doubleValue();
if (operator == Action.NO_OP || operator == Action.CORRUPTED)
throw new RuntimeException(theString + " is undefined");
double t = Action.EVALUATOR[operator].eval(children);
for (int i = 0; i < children.length; i++)
if (children[i].badFlag) {
badFlag = true;
return 0;
} // end if
return t;
} // end eval
/**
* Creates a leaf node.
*
* @param s the string for the leaf node.
* @return the leaf parse node.
*/
ParseNode leaf(String s) {
ParseNode p = new ParseNode();
p.theString = s;
return p;
} // end leaf
/**
* Builds the tree given the specified quantity.
*
* @param q the quantity.
* @return the root node of the tree.
*/
public ParseNode buildTree(Quantity q) {
if (q == null)
return null;
if (q.children == null)
return null;
int i, n = q.children.length;
if ((n & 1) == 0)
return null;
if (n == 1)
return doSum((String)q.children[0]);
String s = "", t;
Hashtable<String, Object> ht = new Hashtable<String, Object>();
for (i = 0; i < n; i++) {
if (q.children[i] == null)
return null;
if (q.children[i] instanceof Quantity) {
s = s + (t = "Quantity" + i);
ht.put(t, q.children[i]);
} // end if
else if (q.children[i] instanceof String)
s = s + ((String)q.children[i]).trim();
else
return null;
} // end for i
return replaceSubExpressions(ht, doSum(s));
} // end buildTree
/**
* Replaces the sub-expression for the given node.
*
* @param h the hashtable of quantities.
* @param p the parse node.
* @return the root node with the sub-expression replaced.
*/
public ParseNode replaceSubExpressions(Hashtable<String, Object> h, ParseNode p) {
if (p.children == null) {
if (h.isEmpty())
return p;
Quantity q = (Quantity)h.get(p.theString);
if (q != null)
return buildTree(q);
String r[] = new StringSplitter(this).multiSplit(h.keys(), p.theString);
if (r.length == 1)
return p;
Vector<String> v = new Vector<String>();
for (int i = 0; i < r.length; i++) {
String s = r[i].trim();
if (s.length() == 0)
continue;
v.addElement(s);
} // end for i
ParseNode prod = new ParseNode(v.size(), Action.PRODUCT);
Enumeration<String> e = v.elements();
for (int i = 0; e.hasMoreElements(); i++) {
String s = e.nextElement();
if ((q = (Quantity)h.get(s)) == null)
prod.children[i] = leaf(s);
else
prod.children[i] = buildTree(q);
} // end for i
return prod;
} // end if
for (int i = 0; i < p.children.length; i++)
if ((p.children[i] = replaceSubExpressions(h, p.children[i])) == null)
return null;
return p;
} // end replaceSubExpressions
/**
* Returns the sum of this node with the specified expression.
*
* @param theLocalString the string expression.
* @return the sum of this node with the specified expression.
*/
public ParseNode doSum(String theLocalString) {
int i, n;
String[] operators;
String[] operands;
ParseNode p;
StringTokenizer st;
theLocalString = theLocalString.trim();
// System.out.println ("doSum: "+theLocalString);
if ((theLocalString.indexOf('+') >= 0) || (theLocalString.indexOf('-') >= 0)) {
if (!((theLocalString.startsWith("-")) || (theLocalString.startsWith("+"))))
theLocalString = "+" + theLocalString;
st = new StringTokenizer(theLocalString, "+-", true);
if (((i = st.countTokens()) & 1) == 1)
return null;
operators = new String[n = (i >> 1)];
operands = new String[n];
for (i = 0; i < n; i++) {
operators[i] = st.nextToken().trim();
operands[i] = st.nextToken().trim();
} // end for i
for (i = 0; i < n; i++) {
if (!((operators[i].equals("+")) || (operators[i].equals("-"))))
return null;
if ((operands[i].indexOf('+') >= 0) || (operands[i].indexOf('-') >= 0))
return null;
} // end for i
p = new ParseNode(n, Action.SUM); // sum node
for (i = 0; i < n; i++)
if (operators[i].equals("-")) {
p.children[i] = new ParseNode(1, Action.U_MINUS); // unary minus
p.children[i].children[0] = leaf(operands[i]);
} // end if
else
p.children[i] = leaf(operands[i]);
for (i = 0; i < n; i++)
if (p.children[i].operator == Action.U_MINUS) {
if ((p.children[i].children[0] = doProd(p.children[i].children[0].theString)) == null)
return null;
} // end if
else if (p.children[i].operator == Action.NO_OP) {
if ((p.children[i] = doProd(p.children[i].theString)) == null)
return null;
} // end if
else
return null;
} // end if
else
p = doProd(theLocalString);
return p;
} // end doSum
/**
* Returns the product of this node and the specified expression.
*
* @param theLocalString the string expression.
* @return the product of this node and the specified expression.
*/
public ParseNode doProd(String theLocalString) {
if (theLocalString == null)
return null;
int i, n;
String[] operators;
String[] operands;
ParseNode p;
StringTokenizer st;
// System.out.println ("doProd: "+theLocalString);
if ((theLocalString.indexOf('*') >= 0) || (theLocalString.indexOf('/') >= 0)) {
theLocalString = "*" + theLocalString.trim();
st = new StringTokenizer(theLocalString, "*/", true);
if (((i = st.countTokens()) & 1) != 0)
return null;
operators = new String[n = (i >> 1)];
operands = new String[n];
for (i = 0; i < n; i++) {
operators[i] = st.nextToken().trim();
operands[i] = st.nextToken().trim();
} // end for i
for (i = 0; i < n; i++) {
if (!((operators[i].equals("*")) || (operators[i].equals("/"))))
return null;
if ((operands[i].indexOf('*') >= 0) || (operands[i].indexOf('/') >= 0))
return null;
} // end for i
p = new ParseNode(n, Action.PRODUCT); // product node
for (i = 0; i < n; i++)
if (operators[i].equals("/")) {
p.children[i] = new ParseNode(1, Action.RECIPROCAL); // reciprocal
if ((p.children[i].children[0] = doPowers(operands[i])) == null)
return null;
} // end if
else {
if ((p.children[i] = doPowers(operands[i])) == null)
return null;
} // end else
} // end if
else {
if ((p = doPowers(theLocalString)) == null)
return null;
} // end else
return p;
} // end doProd
private ParseNode doPowers(String theLocalString) {
theLocalString = theLocalString.trim();
// System.out.println ("doPowers: "+theLocalString);
int c = theLocalString.indexOf("^");
ParseNode p;
if (c >= 0) {
int n;
char[] chars = new char[n = theLocalString.length()];
theLocalString.getChars(0, n, chars, 0);
p = new ParseNode(2, Action.POWER);
if ((p.children[0] = doFunctions(new String(chars, 0, c))) == null)
return null;
c++;
if ((p.children[1] = doFunctions(new String(chars, c, n - c))) == null)
return null;
} // end if
else if ((p = doFunctions(theLocalString)) == null)
return null;
return p;
} // end doPowers
private ParseNode doFunctions(String theLocalString) {
theLocalString = theLocalString.trim();
// System.out.println ("doFunctions: "+theLocalString);
String[] fNames = Action.FNAMES;
int i = theAction.findFirst(theLocalString, fNames);
// if (i >= 0) System.out.println ("i = "+i+" String = "+FNAMES[i]);
ParseNode p;
if (i == Action.NO_OP)
return leaf(theLocalString);
if (i < Action.FIRST_FUNCTION)
return null;
int c = fNames[i].length();
int m = theLocalString.length() - c;
char[] chars = new char[m];
theLocalString.getChars(c, c + m, chars, 0);
p = new ParseNode(1, i);
if ((p.children[0] = doFunctions(new String(chars, 0, m))) == null)
return null;
return p;
} // end doFunctions
/**
* Process the parent node (this node).
*/
void doParent() {
doParent(this);
} // end doParent
private void doParent(ParseNode p) {
if (p.children == null)
return;
int i, n = p.children.length;
for (i = 0; i < n; i++) {
p.children[i].parent = p;
doParent(p.children[i]);
} // end for i
} // end doParent
/**
* Returns a string representation of the node.
*
* @return a string representation of the node.
*/
public String toString() {
if (operator == Action.NO_OP)
return theString;
String r = "<" + operator;
for (int i = 0; i < children.length; i++)
r = r + " " + children[i].toString();
r = r + ">";
return r;
} // end doString
} // end class ParseNode