![JAR search and dependency download from the Maven repository](/logo.png)
de.tsl2.nano.logictable.EquationSolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tsl2.nano.logicstructure Show documentation
Show all versions of tsl2.nano.logicstructure Show documentation
TSL2 Framework LogicTable/Tree/Vector (Excel-like table logic with equation solving through tsl2.nano.operation, mathemical Vector/Matrix-calculation)
The newest version!
/*
* File: $HeadURL$
* Id : $Id$
*
* created by: Thomas Schneider, Thomas Schneider
* created on: Dec 8, 2012
*
* Copyright: (c) Thomas Schneider 2012, all rights reserved
*/
package de.tsl2.nano.logictable;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
import de.tsl2.nano.core.ENV;
import de.tsl2.nano.core.util.StringUtil;
import de.tsl2.nano.core.util.Util;
import de.tsl2.nano.execution.IPRunnable;
import de.tsl2.nano.specification.AbstractRunnable;
import de.tsl2.nano.specification.Pool;
import de.tsl2.nano.util.operation.NumericOperator;
/**
* Tries to solve a given string expression as action or function (specification rule, or query) in a cell of a logictable.
* to have a rule, the expression starts with '=', actions are surrounded with '<<' and '>>'
*
* @author Thomas Schneider
* @version $Revision$
*/
public class EquationSolver extends NumericOperator {
static final String B_OPEN = "\\(";
static final String B_CLOSE = "\\)";
static final String BETWEEN = ":";
static final String SEPARATOR = ",";
static final String CONCAT = "\\s*[" + SEPARATOR + BETWEEN + "]\\s*";
static final String OPERATION = "([+*/%-])";
static final String EQUATION = "=";
static final String ACTION = "<<";
static final String TERM = B_OPEN + "[^)(]*" + B_CLOSE;
static final String OPERAND = (ENV.get(Pool.class).getExpressionPattern().length() > 2
? ENV.get(Pool.class).getExpressionPattern() + "?" : "") + "([a-zA-Z0-9_]+)";
static final String SET = "(" + OPERAND + "(" + CONCAT + OPERAND + ")*)*";
static final String FUNC = OPERAND + B_OPEN + SET + B_CLOSE;
/** a map containing any values. values found by this solver must be of right type */
Map values;
NumberFormat formatter;
/**
* constructor
*/
public EquationSolver() {
this(null, null);
}
/**
* constructor
*
* @param values
*/
public EquationSolver(NumberFormat formatter, Map values) {
super();
if (formatter == null) {
this.formatter = NumberFormat.getInstance();
} else {
this.formatter = formatter;
}
if (values == null) {
this.values = new Hashtable();
} else {
this.values = values;
}
}
public Object eval(String expression) {
return eval(new StringBuilder(expression));
}
public Object eval(StringBuilder expression) {
//extract all functions
evalActions(expression);
evalFunctions(expression);
//extract all terms
String term;
StringBuilder t;
while (true) {
term = extract(expression, TERM);
if (isEmpty(term)) {
break;
}
t = new StringBuilder(term.substring(1, term.length() - 1));
StringUtil.replace(expression, term, String.valueOf(operate(t, values)));
}
return !isEmpty(expression) && expression.toString().matches(".*" + OPERATION + ".*")
? operate(expression, values) : expression.toString();
}
/**
* isEmpty
* @param term
* @return
*/
boolean isEmpty(String term) {
return term.isEmpty() || term.equals("()");
}
protected void evalActions(StringBuilder expression) {
eval(expression, "\\<\\<" + FUNC + "\\>\\>", true);
}
/**
* replaces all found functions/rule inside the expression with their calculation results.
*
* @param expression
*/
protected void evalFunctions(StringBuilder expression) {
eval(expression, FUNC, true);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void eval(StringBuilder expression, String regex, boolean replace) {
String expr = expression.toString();
Object result;
Map args; //TODO: use extra args?
StringBuilder func, tempFunc;
String sfunc, fname, sresult;
int i = 0;
do {
sfunc = StringUtil.findRegExp(expr, regex, i);
if (Util.isEmpty(sfunc)) {
break;
}
func = new StringBuilder(sfunc);
fname = StringUtil.extract(tempFunc=new StringBuilder(func), OPERAND, "");
args = getFunctionArgs(tempFunc, values);
IPRunnable runner = ENV.get(Pool.class).get(fname);
result = runner.run(args);
if (replace) {
sresult = StringUtil.toString(result, 255);
StringUtil.replace(expression, func.toString(), sresult, i);
}
i += func.length();
} while (true);
}
private Map getFunctionArgs(StringBuilder func, Map values) {
Map args = new LinkedHashMap();
AbstractRunnable.markArgumentsAsSequence(args);
String a;
do {
a = StringUtil.extract(func, OPERAND, "");
if (Util.isEmpty(a))
break;
args.put(a, values.get(a));
} while (true);
return args;
}
private BigDecimal operate(StringBuilder term, Map values) {
String o1 = extract(term, OPERAND, "");
String op = extract(term, OPERATION, "");
String o2 = extract(term, OPERAND, "");
String rest = StringUtil.substring(term, o2, null).trim();
/*
* the value map may contain any values - but the found value must have the right type!
*/
Object v1 = values.containsKey(o1) ? values.get(o1) : o1;
BigDecimal n1 = (BigDecimal) (v1 instanceof BigDecimal ? v1 : new BigDecimal(String.valueOf(v1)));
Object v2 = values.containsKey(o2) ? values.get(o2) : o2;
BigDecimal n2 = (BigDecimal) (v2 instanceof BigDecimal ? v2 : new BigDecimal(String.valueOf(v2)));
BigDecimal result;
switch (op.charAt(0)) {
case '+':
result = n1.add(n2);
break;
case '-':
result = n1.subtract(n2);
break;
case '*':
result = n1.multiply(n2);
break;
case '/':
result = n1.divide(n2);
break;
case '%':
result = new BigDecimal(n1.intValueExact() % n2.intValueExact());
break;
case '^':
result = n1.pow(n2.intValueExact());
break;
default:
throw new IllegalArgumentException(term.toString());
}
if (!rest.isEmpty()) {
term = new StringBuilder(result + rest);
result = operate(term, values);
}
return result;
}
private String extract(CharSequence source, String regexp) {
return extract(source, regexp, null);
}
private String extract(CharSequence source, String regexp, String replacement) {
return StringUtil.extract(source, regexp, replacement);
}
private Object func(String name, Number... values) {
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy