package com.accountingenhancements.formula;
import com.accountingenhancements.common.SupportParameters;
import java.math.*;
import java.text.ParseException;
import java.util.Locale;
/*
* FormulaVariable.java
*
* Created on April 27, 2006, 10:53 AM
*
* Copyright 2006 Lee Lofgern and Accounting Enhancements Inc Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the License.
*/
/**
*FormulaVariable is used to store variables and mathematical operators. An instance can only be one or the other.
*It is used be FormulaVariableList and FormulaVariableStack.
* A formula variable that may, or may not, be set.
* The variable is stored as a String. and supports return types of String, Long, Double, and Boolean.
* If the variable type is boolean and it is being set as a long or boolean type, then 0(+/-.49) = false, non-zero = true.
* If the variable type is long then a double is truncated.
* If the variable type is Formula then the string should be processed with Formula.solve() to retrieve result which may be String, Long, Double, or Boolean.
* @author Lee lofgren lofgren_opensource@accountingenhancements.com
* @version 0.1009102006
*/
public class FormulaVariable implements Cloneable {
protected String variableName;
protected String variableValue;
protected int variableType=TYPE_NOTHING;
protected int level;
protected int scale;
protected int precision;
protected boolean treatUnquotedTextAsFormulas=false;
protected boolean stripSurroundingQuotesFromStrings=false;
/**If this is TYPE_FORMULA then this is the stack of the formula as processed by FormulaVariableSack*/
protected FormulaVariableStack stack=null;
/**If this is TYPE_VARIABLE then this is the value of the resultant value.*/
protected FormulaVariable resolvedVariable=null;
/**If this is TYPE_FORMULA, TYPE_FUNCTION, or TYPE_VARIABLE then this reflects the highest level of variable used in solving for resolvedVariable*/
protected int highestLevel=-1;
/**If this is TYPE_FUNCTION then this is the function that was created when first solving this variable. It has it's own stack that can be checked for whether the function needs to be re-solved.*/
protected FormulaFunction formulaFunction=null;
/**Undefined variable. Not yet a value or an operator.*/
public static final int TYPE_NOTHING=-1;
/**Value is of type String*/
public static final int TYPE_STRING=0;
/**Value is of type Long*/
public static final int TYPE_LONG=1;
/**Value is of type Double*/
public static final int TYPE_DOUBLE=2;
/**Value is of type Boolean*/
public static final int TYPE_BOOLEAN=3;
/**Value is of type Formula where formula should be run through Formula.solve()*/
public static final int TYPE_FORMULA=4;
/**Value is of type Variable where this variable references another variable and should be resolved through a FormulaVariableList*/
public static final int TYPE_VARIABLE=5;
/**Value is of type Date*/
public static final int TYPE_DATE=6;
/**Value is of type Function where this should be solved through the function processing methods*/
public static final int TYPE_FUNCTION=7;
/**Unary Bitwise Not (~) to be applied to next object. Bit patterns are reversed*/
public static final int TYPE_OPERATOR_UNARY_BIT_NOT=100;
/**Unary Not (!) logical Not to be applied to next object (0=1, non-zero=0)*/
public static final int TYPE_OPERATOR_UNARY_NOT=105;
/**Compare previous object to next object for identical values, return 1 if true, 0 if false*/
public static final int TYPE_OPERATOR_EQUALS=110;
/**Compare previous object to next object for non-identical values, return 1 if true, 0 if false*/
public static final int TYPE_OPERATOR_NOT_EQUAL=115;
/**Compare previous object to next object for whether previous object is less than or equal to next object, return 1 if true, 0 if false*/
public static final int TYPE_OPERATOR_LESS_THAN_OR_EQUAL=120;
/**Compare previous object to next object for whether previous object is less than next object, return 1 if true, 0 if false*/
public static final int TYPE_OPERATOR_LESS_THAN=125;
/**Compare previous object to next object for whether previous object is greater than or equal to next object, return 1 if true, 0 if false*/
public static final int TYPE_OPERATOR_GREATER_THAN_OR_EQUAL=130;
/**Compare previous object to next object for whether previous object is greater than next object, return 1 if true, 0 if false*/
public static final int TYPE_OPERATOR_GREATER_THAN=135;
/**Test previous object for non-zero versus zero. If zero then remove the previous object and this operator. If non-zero then remove this operator and all following objects and OR operators until the operator is no inter an OR. Ex: 0||2||0||0||3+1 becomes 2||0|0||3+1 which then becomes 2||0||0||3+1 becomes 2+1 */
public static final int TYPE_OPERATOR_OR=140;
/**
* Test previous object for non-zero versus zero. If non-zero then If this is the first And, track position of the previous object and move forward with testing until all concurrent Ands are tested. If all are true then remove everything from the object following the last And to the first And leaving the object preceeding the first And in place. Ex: 2&&1&&5+1 become 2+1.
* If the previous object is zero then remove every object going forward while the And operators continue until removal of the last object following the last And, then work backwards until all objects are removed up to and including the object preceeding the first And. Then add a Zero object. Ex: 3+2&&4&&1&&0&&1&&2+4&&2 becomes 3+0+4&&2 which then becomes 3+0+4 which then becomes 7.
*/
public static final int TYPE_OPERATOR_AND=145;
/**Multiply next object by previous object*/
public static final int TYPE_OPERATOR_MULTIPLY=150;
/**Divide previous object by next object*/
public static final int TYPE_OPERATOR_DIVIDE=155;
/**Return Remainder of division of previous object by next object.*/
public static final int TYPE_OPERATOR_MODULUS=160;
/**Apply a bitwise And of previous object against the next object*/
public static final int TYPE_OPERATOR_BIT_AND=165;
/**Apply a bitwise Or of previous object against the next object*/
public static final int TYPE_OPERATOR_BIT_OR=170;
/**Apply a bitwise Xor of previous object against the next object*/
public static final int TYPE_OPERATOR_BIT_XOR=175;
/**Add next object to previous object. New object will be Double if either of the objects is Double, Boolean if both objects are Boolean, and in all other cases, Long*/
public static final int TYPE_OPERATOR_PLUS=180;
/**subtract next object from previous object. New object will be Double if either of the objects is Double, Boolean if both objects are Boolean, and in all other cases, Long*/
public static final int TYPE_OPERATOR_MINUS=185;
/**Return the String form of TYPE_, Ex: TYPE_TO_STRING_ARRAY[TYPE_STRING]==String, TO_STRING_ARRAY[TYPE_OPERATOR_PLUS]=="+", etc...*/
public static final String[] TYPE_TO_STRING_ARRAY= {
"String","Long","Double","Boolean","Formula","Variable","Date","Function","","","","","","","","","","","","",
"","","","","","","","","","","","","","","","","","","","",
"","","","","","","","","","","","","","","","","","","","",
"","","","","","","","","","","","","","","","","","","","",
"","","","","","","","","","","","","","","","","","","","",
"~","","","","","!","","","","","=","","","","","!=","","","","",
"<=","","","","","<","","","","",">=","","","","",">","","","","",
"||","","","","","&&","","","","","*","","","","","/","","","","",
"%","","","","","&","","","","","|","","","","","^","","","","",
"+","","","","","-","","","",""
};
//A list of symbols that are operators for quickly verifying whether a character is an operator. Usage: if(OPERATOR_STRING.indexOf(testChar)>=0){System.out.println("Character is an operator or part of an operator");} //Note: This won't detect OR or AND but these should be surrounded by spaces and easier for your code to detect. In these clases, this is only used in detectVariableType to help determine whether any particular character shouldn't be part of a variable name and therefore the variableType should be of TYPE_FORMULA or TYPE_FUNCTION. Spaces are detected separately in that method but also result in variableType==TYPE_FORMULA or TYPE_FUNCTION.
public static final String OPERATOR_STRING = "~!=<>|&*/%^+-";
/**boolean array used to determine whether a TYPE is a unary operator.*/
public static final boolean[] IS_UNARY_OPERATOR={
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
true,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false
};
/**boolean array used to determine whether a TYPE is a comparator operator (!=, >, >=, =, <=, <).*/
public static final boolean[] IS_COMPARATOR_OPERATOR={
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,true,false,false,false,false,
true,false,false,false,false,true,false,false,false,false,true,false,false,false,false,true,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
false,false,false,false,false,false,false,false,false,false
};
/**
*Blank FormulaVariable. Used in Clone
*/
protected FormulaVariable(){
}
/**
*This constructor pulls the variable name from the first part of the String and the value from the last part. There must be an Equals sign separating the name from the value.
*@param variable the variable name, an equals sign, the value. Example: new FormulaVariable("testVar=\"Hi There\"). Unquoted text is treated as a formula. "testVar=A+B" is formula, "testVar=\"A+B\"" is text string.
*@throws ParseException is equal sign is missing
*/
public FormulaVariable(String variable)throws ParseException{
int index;
String name;
String value;
if (variable==null||(index=variable.indexOf('='))<0)throw new ParseException("variable is null or missing an equal sign",0);
name=variable.substring(0,index);
value=variable.substring(index+1);
this.level=0;
this.variableName=name;
treatUnquotedTextAsFormulas=true;
scale=0;
precision=17;
stripSurroundingQuotesFromStrings=true;
variableType=detectVariableType(value,true);
if (variableType==TYPE_DOUBLE)scale=findScaleOfDouble(value);
setValue(value);
}
/**
*This constructor pulls the variable name from the first part of the String and the value from the last part. There must be an Equals sign separating the name from the value.
*@param variable the variable name, an equals sign, the value. Example: new FormulaVariable("testVar=\"Hi There\",1). Unquoted text is treated as a formula. "testVar=A+B" is formula, "testVar=\"A+B\"" is text string.
*@param level the level to be assigned to the variable.
*@throws ParseException is equal sign is missing
*/
public FormulaVariable(String variable, int level)throws ParseException{
int index;
String name;
String value;
if (variable==null||(index=variable.indexOf('='))<0)throw new ParseException("variable is null or missing an equal sign",0);
name=variable.substring(0,index);
value=variable.substring(index+1);
this.level=level;
this.variableName=name;
treatUnquotedTextAsFormulas=true;
scale=0;
precision=17;
stripSurroundingQuotesFromStrings=true;
variableType=detectVariableType(value,true);
if (variableType==TYPE_DOUBLE)scale=findScaleOfDouble(value);
setValue(value);
}
/**
* Creates a new instance of FormulaVariable
*@param variableName is used by the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@param variableType is the type of operator. In this instance is intended for easy creation of operators and scaleIfDouble is set to 0.
*@param level is used when purging variables from the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*/
public FormulaVariable(String variableName, int variableType, int level){
this.variableName=variableName;
variableValue=null;
scale=0;
precision=0;
stripSurroundingQuotesFromStrings=false;
this.variableType=variableType;
this.level=level;
}
/**
* Creates a new instance of FormulaVariable
*@param variableName is used by the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@param variableType is the type of operator. In this instance is intended for easy creation of operators and scaleIfDouble is set to 0.
*@param level is used when purging variables from the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@param stripSurroundingQuotesFromStrings causes setValue(String value) to strip surrounding quotes
*/
public FormulaVariable(String variableName, int variableType, int level, boolean stripSurroundingQuotesFromStrings){
this.variableName=variableName;
variableValue=null;
scale=0;
precision=0;
this.stripSurroundingQuotesFromStrings=stripSurroundingQuotesFromStrings;
this.variableType=variableType;
this.level=level;
}
/**
* Creates a new instance of FormulaVariable
*@param variableType is the type of operator. In this instance is intended for easy creation of operators and scaleIfDouble is set to 0.
*@param level is used when purging variables from the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*/
public FormulaVariable(int variableType, int level){
variableName="";
variableValue=null;
scale=0;
precision=0;
stripSurroundingQuotesFromStrings=false;
this.variableType=variableType;
this.level=level;
}
/**
* Creates a new instance of FormulaVariable
*@param variableType is the type of operator. In this instance is intended for easy creation of operators and scaleIfDouble is set to 0.
*@param level is used when purging variables from the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@param stripSurroundingQuotesFromStrings causes setValue(String value) to strip surrounding quotes
*/
public FormulaVariable(int variableType, int level, boolean stripSurroundingQuotesFromStrings){
variableName="";
variableValue=null;
scale=0;
precision=0;
this.stripSurroundingQuotesFromStrings=stripSurroundingQuotesFromStrings;
this.variableType=variableType;
this.level=level;
}
/**
* Creates a new instance of FormulaVariable
*@param variableType is the type of operator. In this instance is intended for easy creation of operators. ScaleIfDouble and level are set to 0.
*/
public FormulaVariable(int variableType){
variableName="";
variableValue=null;
scale=0;
precision=0;
stripSurroundingQuotesFromStrings=false;
this.variableType=variableType;
this.level=0;
}
/**
* Creates a new instance of FormulaVariable
*@param variableName is used by the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@param variableType is the type of variable or operator (TYPE_STRING, TYPE_OPERATOR_PLUS, etc..). If not an operator type then the value is null until setValue() is used.
*@param scaleIfDouble If variableType==TYPE_DOUBLE then this is the number of digits kept to the right of the decimal point before rounding. If Boolean and setValue(Double value) is used and this value sets the rounding sensitivity when determining true from false. newValue=Round(value*pow(10,scaleIfDouble).intValue)/pow(10,scaleIfDouble).intValue.
*@param level is used when purging variables from the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*/
public FormulaVariable(String variableName, int variableType, int scaleIfDouble, int level) {
this.variableName=variableName;
variableValue=null;
scale=scaleIfDouble;
precision=17;
stripSurroundingQuotesFromStrings=false;
this.variableType=variableType;
this.level=level;
}
/**
* Creates a new instance of FormulaVariable
*@param variableName is used by the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@param variableValue the value of the varaible. It should match the variableType.
*@param variableType is the type of variable (TYPE_STRING, etc...). shouldn't be a TYPE_OPERATOR such as TYPE_OPERATOR_ADD, etc...
*@param scaleIfDouble If variableType==TYPE_DOUBLE then this is the number of digits kept to the right of the decimal point before rounding. If Boolean and setValue(Double value) is used and this value sets the rounding sensitivity when determining true from false. newValue=Round(value*pow(10,scaleIfDouble).intValue)/pow(10,scaleIfDouble).intValue.
*@param level is used when purging variables from the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@throws NumberFormatException if variableType wrong compared to variable value (As in variableType being of the TYPE_OPERATOR set).
*/
public FormulaVariable(String variableName, String variableValue, int variableType, int scaleIfDouble, int level)throws java.lang.NumberFormatException {
boolean boolValue;
long longValue;
if (variableValue!=null && variableType>=100)throw new java.lang.NumberFormatException("Invalid VariableType. Should be TYPE_STRING, TYPE_LONG, etc... Not the TYPE_OPERATOR... group.");
this.variableName=variableName;
this.variableValue=null;
this.variableType=variableType;
scale=scaleIfDouble;
precision=17;
stripSurroundingQuotesFromStrings=false;
if(testIsOperator(variableType)==false)setValue(variableValue);
//Calculate the default precision of a Double. (Number of digits to the left of decimal plus scale.
if(variableType==TYPE_DOUBLE&&variableValue!=null&&variableValue.length()>0){
longValue=getLong();
if(longValue<0)longValue*=-1;
precision=Long.toString(longValue).length()+scale;
}
this.level=level;
}
/**
* Creates a new instance of FormulaVariable
*@param variableName is used by the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@param variableValue the value of the varaible. It should match the variableType.
*@param variableType is the type of variable (TYPE_STRING, etc...). shouldn't be a TYPE_OPERATOR such as TYPE_OPERATOR_ADD, etc...
*@param scaleIfDouble If variableType==TYPE_DOUBLE then this is the number of digits kept to the right of the decimal point before rounding. If Boolean and setValue(Double value) is used and this value sets the rounding sensitivity when determining true from false. newValue=Round(value*pow(10,scaleIfDouble).intValue)/pow(10,scaleIfDouble).intValue.
*@param level is used when purging variables from the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@throws NumberFormatException if variableType wrong compared to variable value (As in variableType being of the TYPE_OPERATOR set).
*/
public FormulaVariable(String variableName, long variableValue, int variableType, int scaleIfDouble, int level)throws java.lang.NumberFormatException {
boolean boolValue;
long longValue;
if (variableType>=100)throw new java.lang.NumberFormatException("Invalid VariableType. Should be TYPE_STRING, TYPE_LONG, etc... Not the TYPE_OPERATOR... group.");
this.variableName=variableName;
this.variableValue=null;
this.variableType=variableType;
scale=scaleIfDouble;
precision=17;
stripSurroundingQuotesFromStrings=false;
if(testIsOperator(variableType)==false)setValue(variableValue);
//Calculate the default precision of a Double. (Number of digits to the left of decimal plus scale.
if(variableType==TYPE_DOUBLE){
longValue=variableValue;
if(longValue<0)longValue*=-1;
precision=Long.toString(longValue).length()+scale;
}
this.level=level;
}
/**
* Creates a new instance of FormulaVariable
*@param variableName is used by the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@param variableValue the value of the varaible. It should match the variableType.
*@param variableType is the type of variable (TYPE_STRING, etc...). shouldn't be a TYPE_OPERATOR such as TYPE_OPERATOR_ADD, etc...
*@param scaleIfDouble If variableType==TYPE_DOUBLE then this is the number of digits kept to the right of the decimal point before rounding. If Boolean and setValue(Double value) is used and this value sets the rounding sensitivity when determining true from false. newValue=Round(value*pow(10,scaleIfDouble).intValue)/pow(10,scaleIfDouble).intValue.
*@param level is used when purging variables from the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@throws NumberFormatException if variableType wrong compared to variable value (As in variableType being of the TYPE_OPERATOR set).
*/
public FormulaVariable(String variableName, double variableValue, int variableType, int scaleIfDouble, int level)throws java.lang.NumberFormatException {
boolean boolValue;
long longValue;
if (variableType>=100)throw new java.lang.NumberFormatException("Invalid VariableType. Should be TYPE_STRING, TYPE_LONG, etc... Not the TYPE_OPERATOR... group.");
this.variableName=variableName;
this.variableValue=null;
this.variableType=variableType;
scale=scaleIfDouble;
precision=17;
stripSurroundingQuotesFromStrings=false;
if(testIsOperator(variableType)==false)setValue(variableValue);
//Calculate the default precision of a Double. (Number of digits to the left of decimal plus scale.
if(variableType==TYPE_DOUBLE){
longValue=Double.valueOf(variableValue).longValue();
if(longValue<0)longValue*=-1;
precision=Long.toString(longValue).length()+scale;
}
this.level=level;
}
/**
* Creates a new instance of FormulaVariable
*@param variableName is used by the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@param variableValue the value of the varaible. It should match the variableType.
*@param variableType is the type of variable (TYPE_STRING, etc...). shouldn't be a TYPE_OPERATOR such as TYPE_OPERATOR_ADD, etc...
*@param scaleIfDouble If variableType==TYPE_DOUBLE then this is the number of digits kept to the right of the decimal point before rounding. If Boolean and setValue(Double value) is used and this value sets the rounding sensitivity when determining true from false. newValue=Round(value*pow(10,scaleIfDouble).intValue)/pow(10,scaleIfDouble).intValue.
*@param level is used when purging variables from the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@throws NumberFormatException if variableType wrong compared to variable value (As in variableType being of the TYPE_OPERATOR set).
*/
public FormulaVariable(String variableName, boolean variableValue, int variableType, int scaleIfDouble, int level)throws java.lang.NumberFormatException {
boolean boolValue;
if (variableType>=100)throw new java.lang.NumberFormatException("Invalid VariableType. Should be TYPE_STRING, TYPE_LONG, etc... Not the TYPE_OPERATOR... group.");
this.variableName=variableName;
this.variableValue=null;
this.variableType=variableType;
scale=scaleIfDouble;
stripSurroundingQuotesFromStrings=false;
precision=1+scaleIfDouble;
if(testIsOperator(variableType)==false)setValue(variableValue);
this.level=level;
}
/**
* Creates a new instance of FormulaVariable
*@param variableName is used by the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@param variableValue the value of the varaible. It should match the variableType.
*@param variableType is the type of variable (TYPE_STRING, etc...). shouldn't be a TYPE_OPERATOR such as TYPE_OPERATOR_ADD, etc...
*@param scaleIfDouble If variableType==TYPE_DOUBLE then this is the number of digits kept to the right of the decimal point before rounding. If Boolean and setValue(Double value) is used and this value sets the rounding sensitivity when determining true from false. newValue=Round(value*pow(10,scaleIfDouble).intValue)/pow(10,scaleIfDouble).intValue.
*@param level is used when purging variables from the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@throws NumberFormatException if variableType wrong compared to variable value (As in variableType being of the TYPE_OPERATOR set).
*/
public FormulaVariable(String variableName, java.util.Date variableValue, int variableType, int scaleIfDouble, int level)throws java.lang.NumberFormatException {
boolean boolValue;
if (variableValue!=null && variableType>=100)throw new java.lang.NumberFormatException("Invalid VariableType. Should be TYPE_STRING, TYPE_LONG, etc... Not the TYPE_OPERATOR... group.");
this.variableName=variableName;
this.variableValue=null;
this.variableType=variableType;
scale=scaleIfDouble;
precision=17;
stripSurroundingQuotesFromStrings=false;
if(testIsOperator(variableType)==false)setValue(variableValue);
this.level=level;
}
/**
* Creates a new instance of FormulaVariable
*@param variableName is used by the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@param variableValue the value of the varaible. It should match the variableType.
*@param variableType is the type of variable (TYPE_STRING, etc...). shouldn't be a TYPE_OPERATOR such as TYPE_OPERATOR_ADD, etc...
*@param scaleIfDouble If variableType==TYPE_DOUBLE then this is the number of digits kept to the right of the decimal point before rounding. If Boolean and setValue(Double value) is used and this value sets the rounding sensitivity when determining true from false. newValue=Round(value*pow(10,scaleIfDouble).intValue)/pow(10,scaleIfDouble).intValue.
*@param level is used when purging variables from the FormulaVariableList and by the FormulaVariableStack to determine whether various variables need to be resolved.
*@throws NumberFormatException if variableType wrong compared to variable value (As in variableType being of the TYPE_OPERATOR set).
*/
public FormulaVariable(String variableName, BigDecimal variableValue, int variableType, int scaleIfDouble, int level)throws java.lang.NumberFormatException {
boolean boolValue;
long longValue;
if (variableValue!=null && variableType>=100)throw new java.lang.NumberFormatException("Invalid VariableType. Should be TYPE_STRING, TYPE_LONG, etc... Not the TYPE_OPERATOR... group.");
this.variableName=variableName;
this.variableValue=null;
this.variableType=variableType;
scale=scaleIfDouble;
precision=17;
stripSurroundingQuotesFromStrings=false;
if(testIsOperator(variableType)==false)setValue(variableValue);
//Calculate the default precision of a Double. (Number of digits to the left of decimal plus scale.
if(variableType==TYPE_DOUBLE&&variableValue!=null){
longValue=variableValue.longValue();
if(longValue<0)longValue*=-1;
precision=Long.toString(longValue).length()+scale;
}
this.level=level;
}
/**
* Creates a new instance of FormulaVariable.
*Tries to determine type of variable based on contents of value. If Double, then scale is set to the number of digits to the right of the decimal point. If a string is not surrounded by quotes, then this will assume that it is a formula.
*@param variableName Needed by FormulaVariableList, but not by MathStack.
*@param variableValue the value of the variable. if numeric with a decimal point then Double, if numeric then Integer, if true or alse then boolean, else string.
*@param level Used by FormulaVariableList to determine when variable can be desposed. Not used by MathStack.
*
*/
public FormulaVariable(String variableName, String variableValue, int level){
this.level=level;
this.variableName=variableName;
treatUnquotedTextAsFormulas=true;
scale=0;
precision=17;
stripSurroundingQuotesFromStrings=true;
variableType=detectVariableType(variableValue,true);
if (variableType==TYPE_DOUBLE)scale=findScaleOfDouble(variableValue);
setValue(variableValue);
}
/**
* Creates a new instance of FormulaVariable
*Tries to determine type of variable based on contents of value. If Double, then scale is set to the number of digits to the right of the decimal point.
*@param variableName Needed by FormulaVariableList, but not by MathStack.
*@param variableValue the value of the variable.
*@param level Used by FormulaVariableList to determine when variable can be desposed. Not used by MathStack.
*
*/
public FormulaVariable(String variableName, long variableValue, int level){
this.level=level;
this.variableName=variableName;
treatUnquotedTextAsFormulas=false;
scale=0;
precision=17;
stripSurroundingQuotesFromStrings=false;
variableType=TYPE_LONG;
setValue(variableValue);
}
/**
* Creates a new instance of FormulaVariable. scale defaults to 4 in this constructor.
*Tries to determine type of variable based on contents of value. If Double, then scale is set to the number of digits to the right of the decimal point.
*@param variableName Needed by FormulaVariableList, but not by MathStack.
*@param variableValue the value of the variable.
*@param level Used by FormulaVariableList to determine when variable can be desposed. Not used by MathStack.
*
*/
public FormulaVariable(String variableName, double variableValue, int level){
long longValue;
this.level=level;
this.variableName=variableName;
treatUnquotedTextAsFormulas=false;
variableType=TYPE_DOUBLE;
scale=4;
stripSurroundingQuotesFromStrings=false;
setValue(variableValue);
//Calculate the default precision of a Double. (Number of digits to the left of decimal plus scale.
longValue=Double.valueOf(variableValue).longValue();
if(longValue<0)longValue*=-1;
precision=Long.toString(longValue).length()+scale;
}
/**
* Creates a new instance of FormulaVariable. scale defaults to 4 in this constructor.
*Tries to determine type of variable based on contents of value. If Double, then scale is set to the number of digits to the right of the decimal point.
*@param variableName Needed by FormulaVariableList, but not by MathStack.
*@param variableValue the value of the variable.
*@param scale This is the number of digits kept to the right of the decimal point before rounding.
*@param level Used by FormulaVariableList to determine when variable can be desposed. Not used by MathStack.
*
*/
public FormulaVariable(String variableName, double variableValue, int scale, int level){
long longValue;
this.level=level;
this.variableName=variableName;
treatUnquotedTextAsFormulas=false;
stripSurroundingQuotesFromStrings=false;
variableType=TYPE_DOUBLE;
this.scale=scale;
setValue(variableValue);
//Calculate the default precision of a Double. (Number of digits to the left of decimal plus scale.
longValue=Double.valueOf(variableValue).longValue();
if(longValue<0)longValue*=-1;
precision=Long.toString(longValue).length()+scale;
}
/**
* Creates a new instance of FormulaVariable. Scale defaults to 4 in this constructor
*Tries to determine type of variable based on contents of value. If Double, then scale is set to the number of digits to the right of the decimal point.
*@param variableName Needed by FormulaVariableList, but not by MathStack.
*@param variableValue the value of the variable.
*@param level Used by FormulaVariableList to determine when variable can be desposed. Not used by MathStack.
*
*/
public FormulaVariable(String variableName, boolean variableValue, int level){
this.level=level;
this.variableName=variableName;
treatUnquotedTextAsFormulas=false;
stripSurroundingQuotesFromStrings=false;
variableType=TYPE_BOOLEAN;
scale=4;
precision=17;
setValue(variableValue);
}
/**
* Creates a new instance of FormulaVariable. Scale defaults to 4 in this constructor
*Tries to determine type of variable based on contents of value. If Double, then scale is set to the number of digits to the right of the decimal point.
*@param variableName Needed by FormulaVariableList, but not by MathStack.
*@param variableValue the value of the variable.
*@param scale When Boolean and setValue(Double value) is used and this value sets the rounding sensitivity when determining true from false. newValue=Round(value*pow(10,scaleIfDouble).intValue)/pow(10,scaleIfDouble).intValue.
*@param level Used by FormulaVariableList to determine when variable can be desposed. Not used by MathStack.
*
*/
public FormulaVariable(String variableName, boolean variableValue, int scale, int level){
this.level=level;
this.variableName=variableName;
treatUnquotedTextAsFormulas=false;
stripSurroundingQuotesFromStrings=false;
this.scale=scale;
precision=1+scale;
variableType=TYPE_BOOLEAN;
setValue(variableValue);
}
/**
* Creates a new instance of FormulaVariable
*Tries to determine type of variable based on contents of value. If Double, then scale is set to the number of digits to the right of the decimal point.
*@param variableName Needed by FormulaVariableList, but not by MathStack.
*@param variableValue the value of the variable. if numeric with a decimal point then Double, if numeric then Integer, if true or alse then boolean, else string.
*@param level Used by FormulaVariableList to determine when variable can be desposed. Not used by MathStack.
*
*/
public FormulaVariable(String variableName, java.util.Date variableValue, int level){
this.level=level;
this.variableName=variableName;
treatUnquotedTextAsFormulas=false;
stripSurroundingQuotesFromStrings=false;
scale=0;
precision=17;
this.variableValue=null;
variableType=TYPE_DATE;
if(variableValue!=null)setValue(variableValue);
}
/**
* Creates a new instance of FormulaVariable
*Tries to determine type of variable based on contents of value. If Double, then scale is set to the number of digits to the right of the decimal point.
*@param variableName Needed by FormulaVariableList, but not by MathStack.
*@param variableValue the value of the variable. if numeric with a decimal point then Double, if numeric then Integer, if true or alse then boolean, else string.
*@param level Used by FormulaVariableList to determine when variable can be desposed. Not used by MathStack.
*
*/
public FormulaVariable(String variableName, BigDecimal variableValue, int level){
long longValue;
this.level=level;
this.variableName=variableName;
treatUnquotedTextAsFormulas=false;
stripSurroundingQuotesFromStrings=false;
scale=4;
precision=17;
this.variableValue=null;
variableType=TYPE_DOUBLE;
if(variableValue!=null){
scale=variableValue.scale();
setValue(variableValue);
//Calculate the default precision of a Double. (Number of digits to the left of decimal plus scale.
longValue=variableValue.longValue();
if(longValue<0)longValue*=-1;
precision=Long.toString(longValue).length()+scale;
}
}
/**
* Creates a new instance of FormulaVariable
*Tries to determine type of variable based on contents of value. If Double, then scale is set to the number of digits to the right of the decimal point.
*@param variableName Needed by FormulaVariableList, but not by MathStack.
*@param variableValue the value of the variable. if numeric with a decimal point then Double, if numeric then Integer, if true or alse then boolean, else string.
*@param scale This is the number of digits kept to the right of the decimal point before rounding.
*@param level Used by FormulaVariableList to determine when variable can be desposed. Not used by MathStack.
*
*/
public FormulaVariable(String variableName, BigDecimal variableValue, int scale, int level){
long longValue;
this.level=level;
this.variableName=variableName;
treatUnquotedTextAsFormulas=false;
stripSurroundingQuotesFromStrings=false;
variableType=TYPE_DOUBLE;
this.variableValue=null;
this.scale=scale;
precision=17;
if(variableValue!=null){
setValue(variableValue);
scale=variableValue.scale();
setValue(variableValue);
//Calculate the default precision of a Double. (Number of digits to the left of decimal plus scale.
longValue=variableValue.longValue();
if(longValue<0)longValue*=-1;
precision=Long.toString(longValue).length()+scale;
}
}
/**
* Creates a new instance of FormulaVariable
*Tries to determine type of variable based on contents of value. If Double, then scale is set to the number of digits to the right of the decimal point.
*@param variableName Needed by FormulaVariableList, but not by MathStack.
*@param variableValue the value of the variable. if numeric with a decimal point then Double, if numeric then Integer, if true or alse then boolean, else string.
*@param level Used by FormulaVariableList to determine when variable can be desposed. Not used by MathStack.
*@param treatUnquotedTextAsFormulas If true then if the variable value has beginning and ending quotes, they will be removed and the variable will be of TYPE_STRING, If unquoted only made up of letters, numbers, and underscores with no spaces, then the variable type will be TYPE_VARIABLE, else the variable type will be TYPE_FORMULA or TYPE_FUNCTION.
*If treatUnquotedTextAsFormula is false then the variable type will be TYPE_STRING if it is determined not to be TYPE_LONG, TYPE_BOOLEAN, or TYPE_DOUBLE and the value will be left as-is (no quotes removed).
*/
public FormulaVariable(String variableName, String variableValue, int level, boolean treatUnquotedTextAsFormulas){
long longValue;
this.level=level;
this.variableName=variableName;
this.treatUnquotedTextAsFormulas=treatUnquotedTextAsFormulas;
if(treatUnquotedTextAsFormulas)stripSurroundingQuotesFromStrings=true;
else stripSurroundingQuotesFromStrings=false;
scale=0;
precision=17;
variableType=detectVariableType(variableValue,treatUnquotedTextAsFormulas);
if (variableType==TYPE_DOUBLE)scale=findScaleOfDouble(variableValue);
setValue(variableValue);
//Calculate the default precision of a Double. (Number of digits to the left of decimal plus scale.
if(variableType==TYPE_DOUBLE&&variableValue!=null&&variableValue.length()>0){
longValue=getLong();
if(longValue<0)longValue*=-1;
precision=Long.toString(longValue).length()+scale;
}
}
/**
* Creates a new instance of FormulaVariable
*Tries to determine type of variable based on contents of value. If Double, then scale is set to the number of digits to the right of the decimal point.
*@param variableName Needed by FormulaVariableList, but not by MathStack.
*@param variableValue the value of the variable. if numeric with a decimal point then Double, if numeric then Integer, if true or alse then boolean, else string.
*@param scaleIfDouble If variableType==TYPE_DOUBLE then this is the number of digits kept to the right of the decimal point before rounding. If Boolean and setValue(Double value) is used and this value sets the rounding sensitivity when determining true from false. newValue=Round(value*pow(10,scaleIfDouble).intValue)/pow(10,scaleIfDouble).intValue.
*@param level Used by FormulaVariableList to determine when variable can be desposed. Not used by MathStack.
*@param treatUnquotedTextAsFormulas If true then if the variable value has beginning and ending quotes, they will be removed and the variable will be of TYPE_STRING, If unquoted only made up of letters, numbers, and underscores with no spaces, then the variable type will be TYPE_VARIABLE, else the variable type will be TYPE_FORMULA or TYPE_FUNCTION.
*If treatUnquotedTextAsFormula is false then the variable type will be TYPE_STRING if it is determined not to be TYPE_LONG, TYPE_BOOLEAN, or TYPE_DOUBLE and the value will be left as-is (no quotes removed).
*/
public FormulaVariable(String variableName, String variableValue, int scaleIfDouble, int level, boolean treatUnquotedTextAsFormulas){
long longValue;
this.level=level;
this.variableName=variableName;
this.treatUnquotedTextAsFormulas=treatUnquotedTextAsFormulas;
if(treatUnquotedTextAsFormulas)stripSurroundingQuotesFromStrings=true;
else stripSurroundingQuotesFromStrings=false;
variableType=detectVariableType(variableValue,treatUnquotedTextAsFormulas);
this.scale=scaleIfDouble;
precision=17;
setValue(variableValue);
//Calculate the default precision of a Double. (Number of digits to the left of decimal plus scale.
if(variableType==TYPE_DOUBLE&&variableValue!=null&&variableValue.length()>0){
longValue=getLong();
if(longValue<0)longValue*=-1;
precision=Long.toString(longValue).length()+scale;
}
}
/**
*If variableValue is known to contain an integer or double then what is the scale (integer would be scale=0).
*@param variableValue value tested to figure out scale.
*@return scale the number of characters to the right of the decimal point.
*/
public static int findScaleOfDouble(String variableValue){
int scale=0;
int index=0;
int varLen=0;
char curChar;
if(variableValue!=null){
index=variableValue.indexOf('.');
if(index>=0){
varLen=variableValue.length();
index++;
while(index='0'&&curChar<='9'){
scale++;
index++;
}
}
}
return scale;
}
public static int detectVariableType(String variableValue, boolean treatUnquotedTextAsFormulas){
int index;
int subIndex;
int varLen;
int startPos;
int endPos;
boolean hasDecimal;
boolean done;
boolean isString;
boolean hasSpaces;
char curChar;
int variableType=-1;
int month;
int day;
int year;
int curYear;
int daysInMonth;
java.util.Calendar cal;
int parenCount;
int slashCount;
if(treatUnquotedTextAsFormulas)variableValue=variableValue.trim(); // If we are to treat unquoted text as formulas then beginning and ending spaces don't make sense, since TYPE_STRING should be surrounded by quotes.
if(variableValue.length()==0)variableType=TYPE_STRING;//Assume empty string is of type string.
else if (variableValue!=null){
variableType=TYPE_STRING;
if (variableValue.equalsIgnoreCase("F")||variableValue.equalsIgnoreCase("FALSE")){
variableValue="0";
variableType=TYPE_BOOLEAN;
}else if (variableValue.equalsIgnoreCase("T")||variableValue.equalsIgnoreCase("TRUE")){
variableValue="1";
variableType=TYPE_BOOLEAN;
} else {
variableType=TYPE_STRING;
try{
index=variableValue.indexOf('/');
if(index>0){
month=Integer.parseInt(variableValue.substring(0,index));
subIndex=variableValue.indexOf('/',index+1);
if(subIndex>=0){
day=Integer.parseInt(variableValue.substring(index+1,subIndex));
year=Integer.parseInt(variableValue.substring(subIndex+1));
cal = java.util.Calendar.getInstance(java.util.TimeZone.getDefault());
cal.get(java.util.Calendar.YEAR);
if(year<100){
if(year+2000>cal.get(java.util.Calendar.YEAR))year+=1900; else year+=2000;
}
switch (month){
case 1:case 3: case 5: case 7: case 8: case 10: case 12:
daysInMonth=31;
break;
case 2:
if(year%4!=0)daysInMonth=28;
else if(year%400==0)daysInMonth=29;
else if (year%100==0)daysInMonth=28;
else daysInMonth=29;
break;
default:
daysInMonth=30;
}
if (month>0&&month<=12&&day>0&&day<=daysInMonth){
variableType=TYPE_DATE;
}
}
}
} catch(java.lang.NumberFormatException ex){}
if(variableType!=TYPE_DATE){ //IF not date are we a number?
hasDecimal=false;
index=0;
isString=false;
done=false;
hasSpaces=false; //Only trim a numeric if spaces actually exist to save memory and avoid creating an extra object.
varLen=variableValue.length();
while(index=varLen)isString=true; //If were are done then it was a space filled string. Leave as a string
if (index'9'){ //If there is anything other than numbers, and up to 1 decimal point (possibly followed by spaces) then this isn't a number
while(index1&&variableValue.charAt(index)=='"'){
done=false;
startPos=index;
index++;
while(index=0&&variableValue.charAt(subIndex)=='\\')slashCount++; //Slashed quotes are to be ignored.
if (slashCount%2==0)done=true; //Found an unslashed quote so we are at end of quoted string. Are we at end of data?
}
done=false; //Will now mean that we aren't a quoted string
variableType=TYPE_FORMULA; //Unless the next segment proves that we are a quoted string, we must be a formula since a function can't begin with a quote but a formula can. ie: "Hi " + " there" is a formula.
if(index=varLen){ //The end quote we found was at the end of our data so this is a quoted string. We are done!!!
variableType=TYPE_STRING;
done=true;
variableValue=variableValue.substring(startPos,endPos); //Remove quotes and extraneous spaces outside of quotes.
}
}
}
//If variableType!=TYPE_STRING then we determined that we are a formula with the above code and are done, otherwise if done==false then we aren't a quoted string so lets analyze further.
if(variableType==TYPE_STRING&&done==false&&(varLen=variableValue.length())>0){
done=false;
index=0;
while(index='0'&&curChar<='9')variableType=TYPE_FORMULA; //If beginning with number then must be formula because variables can't begin with numbers
else {
variableType=TYPE_VARIABLE;
while(done==false&&index=0){
done=true;
variableType=TYPE_FORMULA;
}
index++;
}
}
//Find out if this is a lone function instead of a formula. Example: test(a,b,c). However test(a,b,c) + test2() is to be treated as a formula since it isn't a lone function.
if(variableType==TYPE_FORMULA){
index=0;
variableType=TYPE_FUNCTION; //Assume TYPE_FUNCTION until proven as not.
while(index'Z'))variableType=TYPE_FORMULA; //Functions must begin with an alpha character or underscore, anything else means it is not a function
else if((variableValue.charAt(index)&0x5F)=='O'&&index+2'Z')&&(curChar<'0'||curChar>'9'))variableType=TYPE_FORMULA; //Function name can't be OR because that is an operator.
else if((variableValue.charAt(index)&0x5F)=='A'&&index+3'Z')&&(curChar<'0'||curChar>'9'))variableType=TYPE_FORMULA; //Function name can't be AND because that is an operator.
while(variableType==TYPE_FUNCTION&&index0){
//Find end parenthesis
if((curChar=variableValue.charAt(index))=='"'){ //Find end of quoted text
done=false;
index++;
while(done==false){
if(index>=varLen)done=true;
else if((curChar=variableValue.charAt(index))=='"'){ //Is this slashed?
slashCount=0;
subIndex=index-1;
while(subIndex>=0&&variableValue.charAt(subIndex)=='\\'){subIndex--;slashCount++;}
if(slashCount%2==0)done=true;
index++;
} else index++;
}
} else if(curChar=='('){
parenCount++;
index++;
} else if(curChar==')'){
parenCount--;
if(parenCount!=0)index++;
} else index++;
}
//If we aren't on closing parenthesis then this wasn't a function, so go back to assuming that it is a formula
if(parenCount!=0||index>=varLen||variableValue.charAt(index)!=')')variableType=TYPE_FORMULA;
else {
//Test whether there is anything past the closing parenthesis besides spaces, if there is then this isn't a (lone) function, so assume that it is a formula.
index++;
while(index0)
} // Of if(treatUnquotedTextAsFormulas&&variableType==TYPE_STRING)
}
return variableType;
}
/**
*@param value a string value that is to be the new value. If this is TYPE_DOUBLE, then it will be rounded to the specified scale as defined when this was created.
*@throws NumberFormatException if the variableType is of the TYPE_OPERATOR set or if variableType is TYPE_INTEGER or TYPE_DOUBLE and the value can't be converted.
*/
public void setValue(String value)throws java.lang.NumberFormatException{
boolean boolValue;
int month;
int daysInMonth;
int day;
int year;
int index;
int subIndex;
java.util.Calendar cal;
if (variableType>=100)throw new java.lang.NumberFormatException("Can't set a value for an entry that is of TYPE_OPERATOR");
if(variableType==TYPE_STRING&&value!=null&&value.length()>0&&stripSurroundingQuotesFromStrings&&value!=null&&value.charAt(0)=='"'&&value.charAt(value.length()-1)=='"')value=value.substring(1,value.length()-1);
if(value==null)variableValue=null;
else if (value.length()==0&&variableType==TYPE_STRING)variableValue="";
else if(variableType==TYPE_LONG){
if(value.length()==0)variableValue="0";
else if(value.indexOf('.')>=0)variableValue=new BigDecimal(value).setScale(0,BigDecimal.ROUND_HALF_UP).toPlainString(); // If a decimal then convert to
else variableValue=Long.valueOf(Long.parseLong(value)).toString();
} else if(variableType==TYPE_DOUBLE){
if(value.length()==0)variableValue="0";
else variableValue = new BigDecimal(value).setScale(scale,BigDecimal.ROUND_HALF_UP).toPlainString();
} else if(variableType==TYPE_BOOLEAN){
boolValue=booleanValue(value);
if (boolValue)variableValue="1"; else variableValue="0";
} else if (variableType==TYPE_DATE){
index=value.indexOf('/');
if(index>=0){
month=Integer.parseInt(value.substring(0,index));
subIndex=value.indexOf('/',index+1);
if(subIndex>=0){
day=Integer.parseInt(value.substring(index+1,subIndex));
year=Integer.parseInt(value.substring(subIndex+1));
cal = java.util.Calendar.getInstance(java.util.TimeZone.getDefault());
if(year<100){
if(year+2000>cal.get(java.util.Calendar.YEAR))year+=1900; else year+=2000;
}
switch (month){
case 1:case 3: case 5: case 7: case 8: case 10: case 12:
daysInMonth=31;
break;
case 2:
if(year%4!=0)daysInMonth=28;
else if(year%400==0)daysInMonth=29;
else if (year%100==0)daysInMonth=28;
else daysInMonth=29;
break;
default:
daysInMonth=30;
}
if (month>0&&month<=12&&day>0&&day<=daysInMonth){
variableValue=padString(Integer.toString(month),2,'0',true)+"/"+padString(Integer.toString(day),2,'0',true)+"/"+year;
} else throw new java.lang.NumberFormatException("Invalid date");
}
}
} else variableValue=value;
}
/**
* @param value the new long value. If TYPE_BOOLEAN then long value will be converted to 1 or 0 meaning that setValue((long)103) then getLong()==1. If TYPE_DATE then java.util.Date(long value) is used to get the date.
*@throws NumberFormatException if variableType is of the TYPE_OPERATOR_ set.
*/
public void setValue(long value)throws java.lang.NumberFormatException{
java.util.Date date;
if (variableType>=100)throw new java.lang.NumberFormatException("Can't set a value for an entry that is of TYPE_OPERATOR");
if(variableType==TYPE_FORMULA||variableType==TYPE_FUNCTION||variableType==TYPE_VARIABLE)throw new java.lang.NumberFormatException("Can't use setValue(long value). Must use setValue(String value) when setting values of TYPE_FORMULA, TYPE_FUNCTION, or TYPE_VARIABLE");
if(variableType==TYPE_BOOLEAN){
if (value==0)setValue(false); else setValue(true);
} else if(variableType==TYPE_DATE){
date = new java.util.Date(value);
setValue(date);
} else setValue(Long.valueOf(value).toString());
}
/**
* @param value the new double value. If TYPE_BOOLEAN then double value. scale will be used to set the sensitivity of true versus false. scale==2 means false is 0 +-.0049. If TYPE_DATE then java.util.Date(long value) is used to get the date.
*@throws NumberFormatException if variableType is of the TYPE_OPERATOR_ set.
*/
public void setValue(double value)throws java.lang.NumberFormatException{
if (variableType>=100)throw new java.lang.NumberFormatException("Can't set a value for an entry that is of TYPE_OPERATOR");
if(variableType==TYPE_FORMULA||variableType==TYPE_FUNCTION||variableType==TYPE_VARIABLE)throw new java.lang.NumberFormatException("Can't use setValue(double value). Must use setValue(String value) when setting values of TYPE_FORMULA, TYPE_FUNCTION, or TYPE_VARIABLE");
if(variableType==TYPE_STRING) {
//Since String, resolve double to 4 decimal places. If string ends with zeros, then remove them.
StringBuilder tmpString;
tmpString = new StringBuilder(new BigDecimal(value).setScale(4,BigDecimal.ROUND_HALF_UP).toPlainString());
while(tmpString.charAt(tmpString.length()-1)=='0')tmpString.deleteCharAt(tmpString.length()-1);
if(tmpString.charAt(tmpString.length()-1)=='.')tmpString.deleteCharAt(tmpString.length()-1);
variableValue=tmpString.toString();
} else if(variableType==TYPE_LONG||variableType==TYPE_DATE){
if (value>Long.MAX_VALUE)setValue(Long.MAX_VALUE);
else if (valuevariableTypes.
*@throws NumberFormatException if variableType is of the TYPE_OPERATOR_ set.
*/
public void setValue(double value, int scale)throws java.lang.NumberFormatException{
if (variableType>=100)throw new java.lang.NumberFormatException("Can't set a value for an entry that is of TYPE_OPERATOR");
this.scale=scale;
setValue(value);
}
/**
* @param value the new BigDecimal value. If TYPE_BOOLEAN then double value. scale will be used to set the sensitivity of true versus false. scale==2 means false is 0 +-.0049. If TYPE_DATE then java.util.Date(long value) is used to get the date.
*@throws NumberFormatException if variableType is of the TYPE_OPERATOR_ set.
*/
public void setValue(BigDecimal value) throws java.lang.NumberFormatException{
if(variableType==TYPE_FORMULA||variableType==TYPE_FUNCTION||variableType==TYPE_VARIABLE)throw new java.lang.NumberFormatException("Can't use setValue(BigDecimal value). Must use setValue(String value) when setting values of TYPE_FORMULA, TYPE_FUNCTION, or TYPE_VARIABLE");
if(variableType==TYPE_DATE)setValue(value.doubleValue());
else setValue(value.toPlainString());
}
/**
*This method updates the scale value then executes setValue(BigDecimal value)
*@param value the new BigDecimal value for this variable. If TYPE_DATE then java.util.Date(long value) is used to get the date.
*@param scale rounding scale where 1 = Tenths, 2 = OneHundreths, etc. Setting this value only makes sense for TYPE_DOUBLE and TYPE_BOOLEAN variableTypes.
*@throws NumberFormatException if variableType is of the TYPE_OPERATOR_ set.
*/
public void setValue(BigDecimal value, int scale) throws java.lang.NumberFormatException{
this.scale=scale;
setValue(value);
}
/**
*@param value the Date value for this variable. If TYPE_LONG or TYPE_DOUBLE then value.toTime() is used.
*@throws NumberFormatException if TYPE_BOOLEAN, TYPE_FORMULA, TYPE_FUNCTION, or TYPE_VARIABLE.
*/
public void setValue(java.util.Date value)throws java.lang.NumberFormatException{
java.text.SimpleDateFormat formatter;
if(variableType==TYPE_BOOLEAN)throw new java.lang.NumberFormatException("Setting a TYPE_BOOLEAN field to a date is not permitted");
if(variableType==TYPE_FORMULA||variableType==TYPE_FUNCTION||variableType==TYPE_VARIABLE)throw new java.lang.NumberFormatException("Can't use setValue(Date value). Must use setValue(String value) when setting values of TYPE_FORMULA, TYPE_FUNCTION, or TYPE_VARIABLE");
else if(variableType==TYPE_FORMULA||variableType==TYPE_FUNCTION||variableType==TYPE_VARIABLE)throw new java.lang.NumberFormatException("Setting a TYPE_FORMULA, TYPE_FUNCTION, or TYPE_VARIABLE to a date is not permitted");
else if(variableType==TYPE_LONG||variableType==TYPE_DOUBLE)setValue(value.getTime());
else {
formatter = new java.text.SimpleDateFormat("MM/dd/yyyy",Locale.US);
setValue(formatter.format(value));
}
}
/**
*@param value becomes a 0 if false, 1 if true if variableType is TYPE_BOOLEAN, TYPE_LONG, or TYPE_DOUBLE. TYPE_STRING will be "true" or "false"
*@throws NumberFormatException if variableType is of the TYPE_OPERATOR_ set.
*/
public void setValue(boolean value)throws java.lang.NumberFormatException{
if (variableType>=100)throw new java.lang.NumberFormatException("Can't set a value for an entry that is of TYPE_OPERATOR");
if(variableType==TYPE_FORMULA||variableType==TYPE_FUNCTION||variableType==TYPE_VARIABLE)throw new java.lang.NumberFormatException("Can't use setValue(boolean value). Must use setValue(String value) when setting values of TYPE_FORMULA, TYPE_FUNCTION, or TYPE_VARIABLE");
if(variableType==TYPE_DATE)throw new java.lang.NumberFormatException("setValue(boolean value) can't be applied to a TYPE_DATE variable");
if(variableType==TYPE_BOOLEAN||variableType==TYPE_LONG||variableType==TYPE_DOUBLE){
if (value)setValue("1"); else setValue("0");
} else setValue(Boolean.valueOf(value).toString());
}
/**@return the string form of the value. If variableType is an Operator type then the symbol for the operator is returned (ie: variableType=TYPE_OPERATOR_PLUS then "+" is returned).*/
public String getString(){
if (variableType>=100)return TYPE_TO_STRING_ARRAY[variableType];
else if(variableType==TYPE_BOOLEAN){
if(getBoolean())return "true"; else return "false";
} else return variableValue;
}
/**
* @return Long value. Round Half Up is used if of Type Double. If Type Boolean then False = 0, True = 1. If this is TYPE_DATE, the Date.getTime() method is used to retrieve a value.
*@throws NumberFormatException if variableType is of the TYPE_OPERATOR_ set.
*/
public Long getLong()throws java.lang.NumberFormatException{
java.util.Date date;
java.text.SimpleDateFormat sdf;
if (variableType>=100)throw new java.lang.NumberFormatException("Can't retrieve value for an entry that is of TYPE_OPERATOR");
Long result=null;
if (variableType==TYPE_DOUBLE){
result=java.lang.Math.round(Double.parseDouble(variableValue));
} else if(variableType==TYPE_DATE){
try {
sdf = new java.text.SimpleDateFormat("MM/dd/yyyy");
date = sdf.parse(variableValue);
result = date.getTime();
} catch (ParseException ex) {}
} else if(variableValue!=null)result=Long.parseLong(variableValue);
return result;
}
/**
* @return Double value. If Type Boolean then False = 0, True = 1. If TYPE_DATE then Date value.getTime() is used.
*@throws NumberFormatException if variableType is of the TYPE_OPERATOR_ set.
*/
public Double getDouble()throws java.lang.NumberFormatException{
if (variableType>=100)throw new java.lang.NumberFormatException("Can't retrieve value for an entry that is of TYPE_OPERATOR");
Double result=null;
java.text.SimpleDateFormat sdf;
if(variableType==TYPE_DATE){
try {
sdf = new java.text.SimpleDateFormat("MM/dd/yyyy");
result = new Double(sdf.parse(variableValue).getTime());
} catch (ParseException ex) {}
} else if(variableValue!=null)result = Double.parseDouble(variableValue);
return result;
}
/**
* @return BigDecimal representation of value unscaled with rounding unset. If TYPE_DATE then Date value.getTime() is used.
*@throws NumberFormatException if variableType is of the TYPE_OPERATOR_ set.
*/
public BigDecimal getBigDecimal()throws java.lang.NumberFormatException{
if (variableType>=100)throw new java.lang.NumberFormatException("Can't retrieve value for an entry that is of TYPE_OPERATOR");
BigDecimal result=null;
java.text.SimpleDateFormat sdf;
if(variableType==TYPE_DATE){
try {
sdf = new java.text.SimpleDateFormat("MM/dd/yyyy");
result = new BigDecimal(sdf.parse(variableValue).getTime());
} catch (ParseException ex) {}
} else if(variableValue!=null)result = new BigDecimal(variableValue);
return result;
}
/**
* @return BigDecimal representation of value scaled as appropriate with Round Half Up such that returnValue.toPlainString() will return correct value. If TYPE_DATE then Date value.getTime() is used.
*@throws NumberFormatException if variableType is of the TYPE_OPERATOR_ set.
*/
public BigDecimal getBigDecimalScaled()throws java.lang.NumberFormatException{
if (variableType>=100)throw new java.lang.NumberFormatException("Can't retrieve value for an entry that is of TYPE_OPERATOR");
BigDecimal result=null;
java.text.SimpleDateFormat sdf;
if(variableType==TYPE_DATE){
try {
sdf = new java.text.SimpleDateFormat("MM/dd/yyyy");
result = new BigDecimal(sdf.parse(variableValue).getTime());
} catch (ParseException ex) {}
} else if(variableValue!=null)result = new BigDecimal(variableValue).setScale(scale,BigDecimal.ROUND_HALF_UP);
return result;
}
/**
* @return Boolean value, if value is zero (within specified scale), or a string matching the case-insensitive words f, false, n, or no then return false, else return true.
*@throws NumberFormatException if variableType is of the TYPE_OPERATOR_ set.
*/
public boolean getBoolean()throws java.lang.NumberFormatException{
if (variableType>=100)throw new java.lang.NumberFormatException("Can't retrieve value for an entry that is of TYPE_OPERATOR");
boolean result=false;
if(variableValue!=null){
if (variableType==TYPE_DOUBLE){
if(java.lang.Math.round(Double.parseDouble(variableValue)*Math.pow(10,scale))==0)result=false; else result=true;
} else if(variableType==TYPE_LONG||variableType==TYPE_BOOLEAN){
result=true;
if (Long.parseLong(variableValue)==0)result=false;
} else if (variableType==TYPE_DATE)result=true;
else result=booleanValue(variableValue);
}
return result;
}
/**
*@return Date value, if value is TYPE_DOUBLE or TYPE_LONG then returns new Date(long value).
*@throws ParseException if date can't be created from variableValue
*/
public java.util.Date getDate() throws ParseException{
java.text.SimpleDateFormat sdf;
if (variableType>=100)throw new java.lang.NumberFormatException("Can't retrieve value for an entry that is of TYPE_OPERATOR");
java.util.Date result=null;
if(variableValue!=null){
if(variableType==TYPE_LONG||variableType==TYPE_DOUBLE){
result= new java.util.Date(getLong());
} else if(variableType==TYPE_BOOLEAN){
throw new java.text.ParseException("Can't create a Date object from TYPE_BOOLEAN",0);
} else {
sdf = new java.text.SimpleDateFormat("MM/dd/yyyy");
result=sdf.parse(variableValue);
}
}
return result;
}
/**
*@param value a string that considered true unless the value is null, an empty string, or matches (case-insensitive) the words false, f, no, n, or is zero when converted to long (If this is of TYPE_BOOLEAN or TYPE_DOUBLE, and the value can be converted into a Double, then it is rounded based on the scale).
*@return boolean value of result.
*/
protected boolean booleanValue(String value){
boolean returnValue=false;
long longValue;
if(value!=null&&value.length()>0){
//Since we are defaulting to false test wheather value is not f, false, n, or no
if (value.equalsIgnoreCase("f")==false&&value.equalsIgnoreCase("false")==false&&value.equalsIgnoreCase("n")==false&&value.equalsIgnoreCase("no")==false) {
longValue=1; //If we can't convert this thing to a number then default to true
try{ //We are testing whether we can convert this to a number by letting the Long class determine it. Throwing an exception means it isn't a number so we'll return True (because longValue was previously set to 1).
longValue=java.lang.Math.round(Double.valueOf(value)*java.lang.Math.pow(10,scale));
} catch (Exception e){}
if (longValue!=0)returnValue=true;
}
}
return returnValue;
}
/**@return the type of variable or operator. Matches to the values of TYPE_INTEGER, TYPE_STRING, TYPE_OPERATOR_PLUS, etc...*/
public int getVariableType(){return variableType;}
/**@return the value in the appropriate class as specified in variableType()*/
public Object getValue(){
if(variableType==TYPE_BOOLEAN)return getBoolean();
else if(variableType==TYPE_DOUBLE)return getDouble();
else if(variableType==TYPE_LONG)return getLong();
else if (variableType==TYPE_DATE){
try {return getDate();} catch (ParseException ex) {return null;}
}
else return getString();
}
/**@return the string representation of the object. Same as getString()*/
public String toString(){
if (variableValue==null&&variableType<100)return "null";
else return getString();}
/**@return the name of the variable as defined when it was created*/
public String getVariableName(){if (variableName==null)return ""; else return variableName;}
/**@return the level of the variable as defined when it was created*/
public int getLevel(){return level;}
/**@return scale of number. Returns zero for everything except Double or Boolean. This reflects number of digits to right of decimal point*/
public int getScale(){
return scale;
}
/**@param scale the number of digits to the right of the decimal point.*/
public void setScale(int scale){
this.scale=scale;
if (variableType==TYPE_DOUBLE&&variableValue!=null){
variableValue=(new BigDecimal(Double.parseDouble(variableValue))).setScale(scale,BigDecimal.ROUND_HALF_UP).toPlainString();
}
}
/**@param highestLevel the highest level used when solving this variable. It gets set automatically, however, you may want to update it if you determined it's use based on a variable with a higher level than the one recorded here.*/
public void setHighestLevel(int highestLevel){this.highestLevel=highestLevel;}
/**@return true if this is an operator instead of a value (belonging to the TYPE_OPERATOR_<...> set)*/
public boolean isOperator(){
return testIsOperator(variableType);
}
/**@return true if this is an operator instead of a value (belonging to the TYPE_OPERATOR_<...> set), but not a unary operator. Used while processing stack to determine variables that are operators that operate on two operands*/
public boolean isNonUnaryOperator(){
boolean result=false;
if (testIsOperator(variableType)&&testIsUnaryOperator(variableType)==false)result=true;
return result;
}
/**@return true if this is a unary operator*/
public boolean isUnaryOperator(){
return testIsUnaryOperator(variableType);
}
/**@return true if this is a comparator operator (!=, <, <=, =, >=, or >).*/
public boolean isComparatorOperator(){
return testIsComparatorOperator(variableType);
}
/**
*Test whether a string segment is an operator.
*@param operator String containing suspected operator (<, <=, =, >=, >, *, /, etc...).
*@return true if it matches a valid operator or false if it doesn't match.
*/
public static boolean testIsOperator(String operator){
int op;
op=findOperatorPrecedence(operator);
return testIsOperator(op);
}
/**
*Test whether a character is part of an operator.
*@param operator String containing suspected operator (<, <=, =, >=, >, *, /, etc...).
*@return true if it matches a valid operator or false if it doesn't match.
*/
public static boolean testIsOperator(char operator){
int op;
boolean result=false;
for(op=100;result==false&&op=0)result=true;
}
return result;
}
/**
*Test whether a specified TYPE is an operator.
*@param operator the operator number.
*@return true if it is an operator. TYPE_STRING, TYPE_DOUBLE, etc... aren't operators.
*/
public static boolean testIsOperator(int operator){
boolean result=false;
if(operator>=100&&operator=, >, *, /, etc...).
*@return int value of operator. The lower the precedence the higher the priority. * is lower than +.
*/
public static int findOperatorPrecedence(String operator){
int op=-1;
int index=100;
if (operator.equals("=>"))operator=">=";
else if (operator.equals("=<"))operator="<=";
else if (operator.equals("<>"))operator="!=";
else if (operator.equalsIgnoreCase("AND"))operator="&&";
else if (operator.equalsIgnoreCase("OR"))operator="||";
while(op<0&&index=, and >.
*@return true if operator is a Comparator Operator.
*/
public static boolean testIsComparatorOperator(int operator){
boolean result=false;
if(operator=0&&highestLevel1000)throw new java.lang.ArithmeticException("Solve iterated over 1000 times. Something is wrong with the way this method is being used");
iteration++;
FormulaVariable variable=this;
if (variableType==TYPE_VARIABLE||variableType==TYPE_FORMULA||variableType==TYPE_FUNCTION){
variable=resolvedVariable;
if(resolveEverythingAboveLevelsize long.
Examples:
padString("12",5,'0',true).equals("00012")==true
padString("Hi",4,' ',false).equals("Hi ")==true
*@param orgString the original String
*@param size the final size of the returned String
*@param padChar the fill character used when padding the String
*@param padOnLeft If true the pad string from left else pad string from right.
*@return padded String
*/
protected static String padString(String orgString, int size, char padChar, boolean padOnLeft){
if(orgString==null)orgString="";
if(size<0)size=-1*size;
if(orgString.length()>=size){
if (padOnLeft)return orgString.substring(orgString.length()-size);
else return orgString.substring(0,size);
}
StringBuffer buffer = new StringBuffer(orgString);
int len = size - orgString.length();
for(int i=0;i
*This does not delete the formulas them-selves. It just forces the variables to be re-solved during the next solve regardless of the resolveEverythingAboveLevel value. It is important to use this when a lower level value has changed and you wouldn't have run across these re-calculations during the lower level process. An example would be, while processing deduction codes, and employee number changed. The deduction code formulas are performed at a level 2 and the employee data is performed at a level 1. Once the level 1 stuff was processed, the level two routines wouldn't know that a level 1 value has changed and would continue to use old values.
*@param specifiedLevel the level above which all results are to be purged.
*/
public void purgeResultsGreaterThanSpecifiedLevel(int specifiedLevel){
if (specifiedLevel