// // FormulaManager.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package visad.formula; import java.lang.reflect.Method; import java.rmi.RemoteException; import java.util.Vector; import visad.*; /** The FormulaManager class is the gateway into the visad.formula package, a general-purpose formula parser and evaluator. Variables update automatically when the variables upon which they depend change. For an example of usage, see the FormulaUtil.createStandardManager() method.<P> */ public class FormulaManager { /** binary operators */ String[] bOps; /** binary operator precedences */ int[] bPrec; /** binary method signatures */ Method[] bMethods; /** unary operators */ String[] uOps; /** unary operator precedences */ int[] uPrec; /** unary method signatures */ Method[] uMethods; /** function names */ String[] funcs; /** function method signatures */ Method[] fMethods; /** implicit function precedence */ int iPrec; /** implicit function methods */ Method[] iMethods; /** formula text pre-parsing method. For an application to support unusual non-standard syntaxes, such as brackets (e.g., VAR[0]), that application should supply a "pre-parsing" method in the preParseMethod argument, which converts non-standard syntax to standard syntax (e.g., VAR[0] could become getElement(VAR, 0)). The supplied method should be a static method taking a String argument (the formula) and a FormulaManager argument (this FormulaManager). If the application does not need such functionality, the preParseMethod argument can be null. */ Method ppMethod; /** construct a new FormulaManager object */ public FormulaManager(String[] binOps, int[] binPrec, String[] binMethods, String[] unaryOps, int[] unaryPrec, String[] unaryMethods, String[] functions, String[] funcMethods, int implicitPrec, String[] implicitMethods, String preParseMethod) throws FormulaException { bOps = binOps; bPrec = binPrec; bMethods = FormulaUtil.stringsToMethods(binMethods); uOps = unaryOps; uPrec = unaryPrec; uMethods = FormulaUtil.stringsToMethods(unaryMethods); funcs = functions; fMethods = FormulaUtil.stringsToMethods(funcMethods); iPrec = implicitPrec; iMethods = FormulaUtil.stringsToMethods(implicitMethods); String[] s = new String[1]; if (preParseMethod == null) ppMethod = null; else { String[] pps = new String[] {preParseMethod}; Method[] ppm = FormulaUtil.stringsToMethods(pps); ppMethod = ppm[0]; } // check that parallel arrays are really parallel int l1 = bOps.length; int l2 = bPrec.length; int l3 = bMethods.length; if (l1 != l2 || l1 != l3) { throw new FormulaException("Binary arrays must have equal lengths"); } l1 = uOps.length; l2 = uPrec.length; l3 = uMethods.length; if (l1 != l2 || l1 != l3) { throw new FormulaException("Unary arrays must have equal lengths"); } l1 = funcs.length; l2 = fMethods.length; if (l1 != l2) { throw new FormulaException("Function arrays must have equal lengths"); } // check that all operators are one character in length, all operators are // legal, and all duplicate operators have equal operator precedences for (int i=0; i<bOps.length; i++) { if (bOps[i].length() > 1) { throw new FormulaException("All operators must be one character " + "in length"); } char c = bOps[i].charAt(0); if (c == '(' || c == ')' || c == ',' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { throw new FormulaException("The character \"" + c + "\" cannot be " + "used as an operator"); } for (int j=i+1; j<bOps.length; j++) { if (bOps[i].charAt(0) == bOps[j].charAt(0) && bPrec[i] != bPrec[j]) { throw new FormulaException("Duplicate operators must have equal " + "operator precedences"); } } } for (int i=0; i<uOps.length; i++) { if (uOps[i].length() > 1) { throw new FormulaException("All operators must be one character " + "in length"); } char c = uOps[i].charAt(0); if (c == '(' || c == ')' || c == ',' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { throw new FormulaException("The character \"" + c + "\" cannot be " + "used as an operator"); } for (int j=i+1; j<uOps.length; j++) { if (uOps[i].charAt(0) == uOps[j].charAt(0) && uPrec[i] != uPrec[j]) { throw new FormulaException("Duplicate operators must have equal " + "operator precedences"); } } } // check that all function names start with a letter for (int i=0; i<functions.length; i++) { char c = functions[i].charAt(0); if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) { throw new FormulaException("All functions must begin with a letter"); } } // check that all methods are legal for (int i=0; i<bMethods.length; i++) { if (bMethods[i] == null) { throw new FormulaException("The method \"" + binMethods[i] + "\" is not valid"); } } for (int i=0; i<uMethods.length; i++) { if (uMethods[i] == null) { throw new FormulaException("The method \"" + unaryMethods[i] + "\" is not valid"); } } for (int i=0; i<fMethods.length; i++) { if (fMethods[i] == null) { throw new FormulaException("The method \"" + funcMethods[i] + "\" is not valid"); } } for (int i=0; i<iMethods.length; i++) { if (iMethods[i] == null) { throw new FormulaException("The method \"" + implicitMethods[i] + "\" is not valid"); } } } /** add a variable to the database that uses tr as its ThingReference */ public void createVar(String name, ThingReference tr) throws VisADException { FormulaVar v; try { v = getVarByName(name); } catch (FormulaException exc) { v = null; } if (v != null) { throw new FormulaException("The variable " + name + " already exists."); } Vars.add(new FormulaVar(name, this, tr)); } /** assign a formula to a variable */ public void assignFormula(String name, String formula) throws VisADException { FormulaVar v = getVarByNameOrCreate(name); v.setFormula(formula); } /** blocks until this variable's formula is finished computing */ public void waitForFormula(String name) throws VisADException { FormulaVar v = getVarByName(name); v.waitForFormula(); } /** set a variable to auto-update its formula based on a Text object referenced by a ThingReference (useful for remote formula updates) */ public void setTextRef(String name, ThingReference textRef) throws VisADException, RemoteException { FormulaVar v = getVarByNameOrCreate(name); v.setTextRef(textRef); } /** get the current list of errors that occurred when evaluating "name" and clear the list */ public String[] getErrors(String name) { try { FormulaVar v = getVarByNameOrCreate(name); String[] s = v.getErrors(); v.clearErrors(); return s; } catch (FormulaException exc) { return null; } catch (VisADException exc) { return null; } } /** check whether it is safe to remove a variable from the database */ public boolean canBeRemoved(String name) throws FormulaException { FormulaVar v = getVarByName(name); return !v.othersDepend(); } /** check whether a given variable is currently in the database */ public boolean exists(String name) { boolean exists = false; try { FormulaVar v = getVarByName(name); exists = true; } catch (FormulaException exc) { } return exists; } /** remove a variable from the database */ public void remove(String name) throws FormulaException { if (canBeRemoved(name)) Vars.remove(getVarByName(name)); else { throw new FormulaException("Cannot remove variable " + name + " " + "because other variables depend on it!"); } } /** set a variable's value directly */ public void setThing(String name, Thing t) throws VisADException, RemoteException { FormulaVar v = getVarByNameOrCreate(name); v.setThing(t); } /** set a variable's ThingReference */ public void setReference(String name, ThingReference tr) throws VisADException { FormulaVar v = getVarByNameOrCreate(name); v.setReference(tr); } /** get a variable's current value */ public Thing getThing(String name) throws FormulaException { FormulaVar v = getVarByName(name); return v.getThing(); } /** get a variable's associated ThingReference */ public ThingReference getReference(String name) throws FormulaException { FormulaVar v = getVarByName(name); return v.getReference(); } /** get a variable's current formula */ public String getFormula(String name) throws FormulaException { FormulaVar v = getVarByName(name); return v.getFormula(); } /** list of all variables in this FormulaManager object */ private Vector Vars = new Vector(); /** return the variable "name" */ FormulaVar getVarByName(String name) throws FormulaException { for (int i=0; i<Vars.size(); i++) { FormulaVar v = (FormulaVar) Vars.elementAt(i); if (v.name.equalsIgnoreCase(name)) return v; } throw new FormulaException("The variable " + name + " does not exist."); } /** return the variable "name", creating it if necessary */ FormulaVar getVarByNameOrCreate(String name) throws VisADException { FormulaVar v; try { v = getVarByName(name); } catch (FormulaException exc) { v = new FormulaVar(name, this); Vars.add(v); } return v; } /** identify whether a given token is a unary operator */ boolean isUnaryOp(String op) { for (int i=0; i<uOps.length; i++) { if (uOps[i].equals(op)) return true; } return false; } /** identify whether a given token is a binary operator */ boolean isBinaryOp(String op) { for (int i=0; i<bOps.length; i++) { if (bOps[i].equals(op)) return true; } return false; } /** identify whether a given token is a defined function */ boolean isFunction(String token) { for (int i=0; i<funcs.length; i++) { if (funcs[i].equalsIgnoreCase(token)) return true; } return false; } /** returns a unary operator's level of precedence */ int getUnaryPrec(String op) { for (int i=0; i<uOps.length; i++) { if (uOps[i].equals(op)) return uPrec[i]; } return -1; } /** returns a binary operator's level of precedence */ int getBinaryPrec(String op) { if (op.equals("(")) return Integer.MAX_VALUE; if (op.equals(",")) return Integer.MAX_VALUE - 1; for (int i=0; i<bOps.length; i++) { if (bOps[i].equals(op)) return bPrec[i]; } return -1; } }