|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectcom.accountingenhancements.formula.Formula
public class Formula
The Formula class is the easiest way to interact with this package.
The formula package is a macro language written in Java.
It can be used as a math engine and to concatinate strings.
The language supports long, double, String, and Date type variables as well as code fragments and variable references.
All of the variables and code segments are in string form.
The language has some built in functions and you can add your own functions (in java) to extend the language.
The language goals are as follows.
Be able to solve various math problems dynamically based on string based code.
Support variables that are handed to the various routines as strings.
Automatically determine data types in a string based on the data's characteristics
(4/5/06 is a date, 543.2 is a decimal, 4 is a long, etc).
Keep re-calculations down by having a method to determine when a result is still valid.
Be able to assign levels of volatility to variables to help determine when a
result should be recalculated and when the last calculated value is still pertinent.
Keep the language very simple.
Allow easy creation of new functions.
Have a way to easily hand objects to functions that can't come from strings, such as
database connections.
The core of this package is variables. A variable can be a long integer, a double decimal,
a date, a reference to another variable, a reference to a function, a code fragment,
or a text string.
The FormulaVariable
can detect the type of data it is holding
(You can also declare the data type). Numbers with no decimal point are long,
numbers with a decimal point is a double, numbers with slashes such as 4/5/06 are dates,
the words yes, no, true, and false are boolean, text strings starting with a letter
and with no special characters or spaces (except underscore) is a reference to
another variable, a text string that with no special characters or spaces
(except underscore) followed by something enclosed in parentheses is a reference
to a function. Text surrounded by double-quotes is a string. Everything else is a formula.
The mathematical operators supported are as follows:
Unary Bit Not (~), Unary Not (!), Equals(=), Not Equal (!=, <>), Less Than or Equal (<=, =<),
Less Than (<), Greater Than Or Equal (>=, =>), Greater Than (>), Or (||, Or), And (&&, And),
Multiply (*), Divide (/), Modulus (%), Bit And (&), Bit Or (|),
Plus (+), Minus (-), and Parentheses. And and Or are not case sensitive.
Data types with special treatment:
Dates:
Dates are can be subtracted to determine the number of days between them.
"1/2/06 - 12/12/05" will return 21. Dates can't be added. "1/2/06 + 12/12/05" will
cause an ArithmeticException. Days can be added and subtracted from dates.
"12/12/05 + 21" will return "01/02/2006". "1/2/06 - 21" will return "12/12/2005".
Dates can be compared. "12/2/2006<1/2/07" returns true.
Strings:
Text strings can be added (concatinated) and compared and nothing more.
"Hi" + " There" returns "Hi There", "Hi" - " There" throws an ArithmeticException.
Comparitors are case sensitive. "\"AA\"<\"aa\"" returns true.
Formulas:
Formulas are parsed down into there constituent parts, then solved,
from left to right, except according to the precedence of the various
operators. Operator precedence follows standard math principles and,
therefore, is in the same order as they are in java.
12+5*6 will solve 5*6 before adding the result to 12.
The Or operator will stop resolving any other OR separated math operations
following a true statement. The And operator will stop resolving any other And
separated math operations following a false statement.
"5>4 Or (8+2*3<0)" will not solve (8+2*3<0) since 5>4 is already true.
"4>5 And (8+2*3>10)" will not solve (8+2*3>10) since 4>5 is already false.
Variables:
Everything is stored in variables except operational objects such as database connections.
This includes Formulas, references to other variables, references to functions,
and data, and the operators.
Variables are usually kept in a FormulaVariableList. This can retrieve the variable
values by variable name. Varible names are not case-sensitive.
The FormulaVariable
has a method (The solve()
method) that
resolves the value based on the content of the variable.
While creating variables, you can assign a level of volatility,
the higher the number, the less stable the value. This number is used when
resolving a formula or function to determine whether the previous result
is still accurate or whether the calculation has to be re-performed.
Variables are substituted into the equations only while solving a formula.
This means that a formula can be executed multiple times with nothing changed
except the contents of one or more variables and it will return a new solution.
When solving a formula, the formula tracks the highest level variable used in the solution.
When re-solving the formula you specify the highest level variable that is still
considered accurate and the solve()
method will only re-solve the
equation if the highest level used is higher than the level specified, otherwise
it will return the previous solution.
Example:
FormulaVariableList variableList = new FormulaVariableList();
variableList.addValue("CurrentDate","8/14/06",0); //Level 0
variableList.addValue("BirthDate","1/3/2001",2); //Level 2
FormulaVariable formula = new FormulaVariable("","/"You are /"+(currentDate-birthdate)+/" days old!/"",true); //true means treat unquoted text as formulas
System.out.println(formula.solve(variableList,0,null,null,10) //Resolve everything that has used a level higher than level 10.
Since this hasn't been solved before, it will solve the problem anyway and print
"You Are 2048 days old.".
variableList.getVariable("birthdate").setValue("1/4/2001"); //Update the birthdate
System.out.println(formula.solve(variableList,0,null,null,10) //Resolve everything that has used a level higher than level 10.
"You Are 2048 days old." will still be returned since even though we changed the birthdate,
we told solve to only re-calculate if a variable with a level higher than 10 was used
in the previous solution.
System.out.println(formula.solve(variableList,0,null,null,1) //Resolve everything that has used a level higher than level 1.
"You Are 2047 days old." is now returned since the solution required at least 1
variable higher than level 1. Note: If we had changed current date, we still would have
received the correct answer since the method only required that a variable with a level
higher than our specified level was used in the calculation, it does not try to figure out
whether it was the one to change.
Note: In re-solving the above example when currentDate changes, the following is a logic example where the old value would still have been returned.
If currentDate referenced another variable (varibleList.addValue("CurrentDate","TodaysDate",0))
and that variable was also less than or equal to 1 (variableLsit.addValue("TodaysDate","1/1/2006",0)),
then the currentDate will still report the old date since level 1 or lower variables are not supposed to be re-calculated.
The intent of levels is to speed up re-calculations in your loops.
Example: You are processing Earning Codes for employees, You could assign todays
date as level 0, employees as level 1, and earning codes as level 2.
Then when you know only earning codes have changed, you could solve as level 1 and
no employee specific data, such as hourly wage, would be solved more than once. However, if the
employee number changed, then you could perform a level 0 solve and the formula's relating
to the employee would be recalculated as well.
However, if an employee number did change and you stuck to level 1 when using earning formulas
in your earning code loop, they would return the wrong answer.
To prevent this problem suggest you use a FormulaVariableStack
to keep
track of all of your important formulas and when a lower level event occurs, such as an employee change, you use
the purgeResultsInStackGreaterThanSpecifiedLevel(int)
method to purge all of the
pertinent results from all of your formulas. Otherwise you risk accidently using old data.
The mistake could occur as follows. You are adding wages by taking an employee's earning
hours times their pay rate, this is a level 1 event because the employee isn't changing,
the employee now changes so you process some formulas at level 0 but you aren't using the
routine that calculates wages, once you are again at wage calculations, you are again using
level 1, however these formulas never knew about the level 0 stuff so would still be using
info from the previous employee. If you keep your formula variables in a FormulaVariableStack
you can purge the results of all formulas, functions and references to variables, myStack.purgeResultsInStackGreaterThanSpecifiedLevel(0)
,
above a specified level, which will force them to do the calculation the first time through no matter the level.
Functions:
This package has some built in functions such as IIF(testData,trueResult,falseResult), UCase(aString), Len(aString), and Mid(aString,Start,length).
You can also create your own functions by extending the FormulaFunction
class.
Functions are referenced from a FormulaFunctionList and are access through their names. These names are not case-sensitive.
You can add your own functions to this list and this list is handed to the solve(...)
methods. This is the only way the solve(...)
routines have to solve functions.
You can use these functions in your formulas.
Example:
"\"You Are \"+IIF(marStat=\"M\",\"married\",\"single\")+\".\""
will return "You are married." if the variable MarStat=="M" otherwise it will return "You are single.".
A function gets handed the contents between the parentheses as arguments. They also get
an array of Objects called SupportParameters. These SupportParameters are used to hand custom
functions Objects such as Database Connections or any other Object that you may need to accomplish
the function's goals. None of the built-in functions should ever need anything from the SupportPrameters
class.
The following example uses most of the principles described above:
Create an SQL Function:
public class Sql_Function extends FormulaFunction{
protected static String[][] requiredArguments = {{"ARG1: An SQL query","TYPE_STRING"}};
protected static String[][] requiredSupportParameters = {{"SQL_Connection: The sql connection","java.sql.Connection"}};
public Sql_Function(FormulaVariableStack functionArgumentStack){super(functionArgumentStack);}
public Sql_Function(String functionArgumentString) throws ParseException{super(functionArgumentString);}
public Sql_Function(String functionArgumentString, int level) throws ParseException{super(functionArgumentString,level);}
public Sql_Function(FormulaVariable functionVariable) throws ParseException{super(functionVariable);}
protected FormulaVariable solve(FormulaVariableList variableList, int iteration, SupportParameters supportParameters, FormulaFunctionList functionList, int resolveEverythingAboveLevel) throws java.text.ParseException, java.lang.ArithmeticException, ClassNotFoundException{
FormulaVariable result=null;String resultString;int length=0;int highestLevel=0;String field="";
if(functionArgumentStack==null)throw new java.lang.ArithmeticException("functionArgumentStack is null");
result=functionArgumentStack.get("ARG1"); //Get the only argument this function takes. Any other aruments are ignored.
if(result==null)throw new java.lang.ArithmeticException("ARG1 is missing from functionArgumentStack");
highestLevel=result.getHighestLevel(); //Keep track of the highest level used so our result reflects this level.
result=result.solve(variableList,iteration,supportParameters,functionList,resolveEverythingAboveLevel);
if(result!=null){
highestLevel=result.getHighestLevel(); //Get the new highest level
try{
java.sql.Connection conn = (java.sql.Connection)supportParameters.get("SQL_Connection"); //The programmer using this function must supply SQL_Connection
if(conn!=null){
java.sql.ResultSet rs=conn.createStatement().executeQuery(result.toString());
if(rs.first())field=rs.getString(1);
}
} catch (Exception ex){} //Ignore all errors.
}
result=new FormulaVariable("",field,highestLevel);
return result;
}
public static String getName(){return "Sql";}
public static String[][] getRequiredArguments(){return requiredArguments;}
public static String[][] getRequiredSupportParameters(){return requiredSupportParameters;}
}
Use the SQL Function:
Formula formulaEngine=new Formula();
formulaEngine.add(Sql_Function.class); //Added to functionList
formulaEngine.add("SQL_Connection",(Object)myPreviouslyConstructedSqlConnection); //Added to supportParametersList
formulaEngine.add("CustNum=\"000000001\"",1); \\Force to be string by putting quotes around value.
formulaEngine.add("GetName","\"Your name is \"+SQL(\"select CustName from tblCustomer where Customer='\"+CustNum+\"'\")+\".\"",0); //Automatically added to formulaStack.
String name = formulaEngine.solve("GetName",1).toString();
formulaEngine.get("custnum").setValue("000000002"); //Don't need to reuse slashed quotes since this variable is already defined as a TYPE_STRING.
name = formulaEngine.solve("GetName",1).toString(); //Returns same name as before since we specified not to resolve any calculations using level 1 variables or lower.
name = formulaEngine.solve("GetName",0).toString(); //Now gets new name since it is told to recalculate any value that was originally calculated involving a level higher that level 0.
formulaEngine.purgeDownToSpecifiedLevel(0);
try{
name = formulaEngine.solve("GetName",1).toString(); //Now we get an error because the purge deleted the CustNum variable from the variableList and when the engine tries to solve, it can't find custNum.
}catch(Exception ex){}
formulaEngine.add("CustNum=CustomerNumber",0);
formulaEngine.add("CustomerNumber=\"000000001\"",2);
name = formulaEngine.solve("GetName",2).toString();
formulaEngine.get("CUSTOMERNUMBER").setValue("000000002");
name = formulaEngine.solve("GetName",2).toString(); //Wrong name since it is again told not to recalcuate previous results using level 2 variables or below.
name = formulaEngine.solve("getname",1).toString(); //Right answer now.
formulaEngine.purgeDownToSpecifiedLevel(1); //Clear out previous calculations involving variables of levels above level 1 and purge all variables with a level above level 1.
formulaEngine.add("CUSTOMERnumber=\"000000003\"",2); //Need to re-add variable since we dumped it in the last line.
name = formulaEngine.solve("GETNAME",2).toString(); //Still right answer because, even though we specified not to resolve any calculations using level 2 variables or below, we cleared out all solutions involving levels above level 1 two lines earlier.
More Examples:
System.out.println(Formula.getQuickValue("A+2","A=10")); //Prints 12. The returned class is String.
System.out.println(Formula.getQuickValue("A+2","A=10.50")); //Prints 12.50. The returned class is Stirng.
System.out.println(Formula.getQuickValue("iif(Birthdate>\"8/22/2006\",\"You were born after 8/22/2006\",\"You were born on or before 8/22/2006\")","BirthDate=9/28/2000")); //Prints "You were born on or before 8/22/2006"
System.out.println(Formula.getQuickValue("iif(Birthdate>\"8/22/2006\",\"You were born after 8/22/2006\",\"You were born on or before 8/22/2006\")","BirthDate=9/28/2006")); //Prints "You were born after 8/22/2006"
System.out.println(Formula.getQuickValue("8/2/3",null); //Prints "08/02/2003" because it interpreted this as a date
System.out.println(Formula.getQuickValue("8/2/ 3",null); //Prints "1" because it now recognizes this is not a date
Field Summary | |
---|---|
protected FormulaVariableStack |
formulaStack
|
protected FormulaFunctionList |
functionList
|
protected SupportParameters |
supportParameters
|
protected FormulaVariableList |
variableList
|
Constructor Summary | |
---|---|
Formula()
Creates a new instance of Formula |
Method Summary | |
---|---|
void |
add(java.lang.Class<? extends FormulaFunction> functionClass)
Add a class to the FormulaFunction list. |
void |
add(java.lang.String variables)
Add one or more variables, separated by semi-colons. |
void |
add(java.lang.String variables,
int level)
Add one or more variables, separated by semi-colons. |
void |
add(java.lang.String supportParameterName,
java.lang.Object supportParameterObject)
Add a support parameter. |
void |
add(java.lang.String variableName,
java.lang.String value,
int scaleIfDouble,
int level)
Add a variable Unquoted text is treated as a formula. |
FormulaVariable |
get(java.lang.String variableName)
Get a variable from the variable list. |
FormulaVariableStack |
getFormulaStack()
Get the internal formula stack |
FormulaFunctionList |
getFunctionList()
Get internal the function list. |
static java.lang.String |
getQuickValue(java.lang.String formula,
java.lang.String variables)
A static method to Quickly get the value from a formula. |
static java.lang.String |
getQuickValueNoErrors(java.lang.String formula,
java.lang.String variables)
A static method to Quickly get the value from a formula. |
SupportParameters |
getSupportParameters()
Get the internal support parameter list |
java.lang.String |
getValue(java.lang.String variableName)
Get the value of a variable of formula. |
java.lang.String |
getValue(java.lang.String variableName,
int resolveEverythingAboveLevel)
Get the value of a variable of formula. |
java.lang.String |
getValueNoErrors(java.lang.String variableName)
Get the value of a variable of formula. |
java.lang.String |
getValueNoErrors(java.lang.String variableName,
int resolveEverythingAboveLevel)
Get the value of a variable of formula. |
FormulaVariableList |
getVariableList()
Get the internal variable list |
void |
purgeDownToSpecifiedLevel(int specifiedLevel)
Purge all variables above the specified level from the variable list and clear out previously solved results from formulas where a level higher than this one was used in the solution. |
FormulaVariable |
solve(FormulaVariable formulaVariable)
Solve the specified variable. |
FormulaVariable |
solve(FormulaVariable formulaVariable,
int resolveEverythingAboveLevel)
Solve the specified variable. |
FormulaVariable |
solve(java.lang.String variableName)
Solve the specified variable. |
FormulaVariable |
solve(java.lang.String variableName,
int resolveEverythingAboveLevel)
Solve the specified variable. |
FormulaVariable |
solveFormula(java.lang.String formula)
You can use this to quickly solve a formula, however it won't keep the formula around so, to speed up execution, use the add(...) method to add the formula, as a variable, and assign a name. |
FormulaVariable |
solveFormula(java.lang.String formula,
int resolveEverythingAboveLevel)
You can use this to quickly solve a formula, however it won't keep the formula around so, to speed up execution, use the add(...) method to add the formula, as a variable, and assign a name. |
static FormulaVariable |
solveQuickFormula(java.lang.String formula,
java.lang.String variables)
A static method for quick solutions. |
Methods inherited from class java.lang.Object |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Field Detail |
---|
protected FormulaFunctionList functionList
protected FormulaVariableList variableList
protected FormulaVariableStack formulaStack
protected SupportParameters supportParameters
Constructor Detail |
---|
public Formula()
Method Detail |
---|
public FormulaFunctionList getFunctionList()
public FormulaVariableList getVariableList()
public FormulaVariableStack getFormulaStack()
public SupportParameters getSupportParameters()
public void purgeDownToSpecifiedLevel(int specifiedLevel)
specifiedLevel
- the level, above which, all information
should be discarded to prevent new calculations from being corrupted by old data.public void add(java.lang.String variableName, java.lang.String value, int scaleIfDouble, int level)
variableName
- the name of the new variablevalue
- the value of this variablescaleIfDouble
- the scale of this value if it is a double, this the rounding of the result. 4.345 scale 2 becomes 4.35.level
- the level of volatility.public void add(java.lang.Class<? extends FormulaFunction> functionClass)
functionClass
- a class of a user defined function that should be added to the function listpublic void add(java.lang.String supportParameterName, java.lang.Object supportParameterObject)
supportParameterName
- the name of a parameter object that is needed by one of the user defined functionssupportParameterObject
- the object that is returned from the supportParameterList when requested with the supportParameterName.public void add(java.lang.String variables, int level) throws java.text.ParseException
variables
- a semi-colon separated list of variable names and their values. Example: add("Var1=2;Var2=A+B; VAR_3=\"Hi there\"")level
- the level of volatility
java.text.ParseException
- is equal sign is missingpublic void add(java.lang.String variables) throws java.text.ParseException
variables
- a semi-colon separated list of variable names and their values. Example: add("Var1=2;Var2=A+B; VAR_3=\"Hi there\"")
java.text.ParseException
- is equal sign is missingpublic FormulaVariable solve(java.lang.String variableName) throws java.text.ParseException, java.lang.ClassNotFoundException
variableName
- the name of a variable that is already saved on to the variableList.
ParseException,
- ClassNotFoundException
java.text.ParseException
java.lang.ClassNotFoundException
public FormulaVariable solve(java.lang.String variableName, int resolveEverythingAboveLevel) throws java.text.ParseException, java.lang.ClassNotFoundException
variableName
- the name of a variable that is already saved on to the variableList.resolveEverythingAboveLevel
- the level, above which, values should be re-solved.
ParseException,
- ClassNotFoundException
java.text.ParseException
java.lang.ClassNotFoundException
public FormulaVariable solve(FormulaVariable formulaVariable) throws java.text.ParseException, java.lang.ClassNotFoundException
formulaVariable
- an already defined variable to be solved. resolve level of 0 is used.
ParseException,
- ClassNotFoundException
java.text.ParseException
java.lang.ClassNotFoundException
public FormulaVariable solve(FormulaVariable formulaVariable, int resolveEverythingAboveLevel) throws java.text.ParseException, java.lang.ClassNotFoundException
formulaVariable
- an already defined variable to be solved.resolveEverythingAboveLevel
- the level above which variables in the variableList should be re-solved.
ParseException,
- ClassNotFoundException
java.text.ParseException
java.lang.ClassNotFoundException
public FormulaVariable solveFormula(java.lang.String formula) throws java.text.ParseException, java.lang.ClassNotFoundException
formula
- a formula string that requires solving. resolveEverythingAboveLevel is 0.
ParseException,
- ClassNotFoundException
java.text.ParseException
java.lang.ClassNotFoundException
public FormulaVariable solveFormula(java.lang.String formula, int resolveEverythingAboveLevel) throws java.text.ParseException, java.lang.ClassNotFoundException
formula
- a formula string that requires solving.resolveEverythingAboveLevel
- the level, above which, everything should be re-solved in the variableList.
ParseException,
- ClassNotFoundException
java.text.ParseException
java.lang.ClassNotFoundException
public static FormulaVariable solveQuickFormula(java.lang.String formula, java.lang.String variables) throws java.text.ParseException, java.lang.ClassNotFoundException
formula
- the formula that requires solving.variables
- an optional semi-colon separated list of variables, where the variables structured as name, equal symbol, value. Example: SolveSimpleFormula("A+B","A=1;B=2").getLong().longValue()==3L;
ParseException,
- ClassNotFoundException
java.text.ParseException
java.lang.ClassNotFoundException
public FormulaVariable get(java.lang.String variableName)
variableName
- the name of the variable to be retrieved.
public java.lang.String getValue(java.lang.String variableName) throws java.text.ParseException, java.lang.ClassNotFoundException
variableName
- the name of the variable or formula
solve(String variableName)
is used to get the result so the return string will be the answer, if this is a formula or function.
ParseException,
- ClassNotFoundException
java.text.ParseException
java.lang.ClassNotFoundException
public java.lang.String getValue(java.lang.String variableName, int resolveEverythingAboveLevel) throws java.text.ParseException, java.lang.ClassNotFoundException
variableName
- the name of the variable or formularesolveEverythingAboveLevel
- the level, above which, values should be re-solved.
solve(String variableName)
is used to get the result so the return string will be the answer, if this is a formula or function.
ParseException,
- ClassNotFoundException
java.text.ParseException
java.lang.ClassNotFoundException
public java.lang.String getValueNoErrors(java.lang.String variableName)
variableName
- the name of the variable or formula
solve(String variableName)
is used to get the result so the return string will be the answer, if this is a formula or function.public java.lang.String getValueNoErrors(java.lang.String variableName, int resolveEverythingAboveLevel)
variableName
- the name of the variable or formularesolveEverythingAboveLevel
- the level, above which, values should be re-solved.
solve(String variableName)
is used to get the result so the return string will be the answer, if this is a formula or function.public static java.lang.String getQuickValue(java.lang.String formula, java.lang.String variables) throws java.text.ParseException, java.lang.ClassNotFoundException
formula
- the formula that requires solving.variables
- an optional semi-colon separated list of variables, where the variables structured as name, equal symbol, value. Example: getQuickValue("A+B","A=1;B=2")=="3";
solve(String variableName)
is used to get the result so the return string will be the answer, if this is a formula or function.
ParseException,
- ClassNotFoundException
java.text.ParseException
java.lang.ClassNotFoundException
public static java.lang.String getQuickValueNoErrors(java.lang.String formula, java.lang.String variables)
formula
- the formula that requires solving.variables
- an optional semi-colon separated list of variables, where the variables structured as name, equal symbol, value. Example: getQuickValueNoErrors("A+B","A=1;B=2")=="3";
solve(String variableName)
is used to get the result so the return string will be the answer, if this is a formula or function.
|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |