All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.tsl2.nano.logictable.EquationSolver Maven / Gradle / Ivy

/*
 * 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 - 2024 Weber Informatics LLC | Privacy Policy