001package com.Lomikel.DB; 002 003import com.Lomikel.Utils.LomikelException; 004import com.Lomikel.Utils.StringResource; 005 006// Bean Shell 007import bsh.Interpreter; 008import bsh.EvalError; 009 010// Java 011import java.util.Set; 012import java.util.TreeSet; 013import java.util.Map; 014import java.util.stream.Stream; 015 016// Log4J 017import org.apache.logging.log4j.Logger; 018import org.apache.logging.log4j.LogManager; 019 020/** <code>Evaluator</code> evaluates formulas. 021 * @opt attributes 022 * @opt operations 023 * @opt types 024 * @opt visibility 025 * @author <a href="mailto:Julius.Hrivnac@cern.ch">J.Hrivnac</a> */ 026public class Evaluator { 027 028 /** Create. 029 * @param schema The {@link Schema} to use to interpret types. 030 * @throws CommonpException If can't be initiated. */ 031 public Evaluator(Schema schema) throws LomikelException { 032 log.info("Creating Evaluator"); 033 _schema = schema; 034 _interpreter = new Interpreter(); 035 } 036 037 /** Set Java and BeanShell Evaluator Functions. 038 * @param javaEF The Java Evaluatior Functions class name. May be <tt>null</tt>. 039 * @param bshEF The BeanShell Evaluatior Functions script name. May be <tt>null</tt>. 040 * @throws CommonpException If can't be set. */ 041 public void setEvaluatorFunctions(String javaEF, 042 String bshEF) throws LomikelException { 043 try { 044 if (javaEF != null) { 045 log.info("Importing " + javaEF); 046 _interpreter.eval("import static " + javaEF + ".*;"); 047 } 048 if (bshEF != null) { 049 log.info("Importing " + bshEF); 050 _interpreter.eval(new StringResource(bshEF).toString()); 051 } 052 } 053 catch (EvalError e) { 054 throw new LomikelException("Can't initiate Evaluator", e); 055 } 056 } 057 058 /** Evaluate boolean formula with supplied variables and values. 059 * @param values The names and values of variables. 060 * @param formula The formula to be evaluated. It should use supplied 061 * variables, which will be filled with supplied values. 062 * @throws LomikelException If formula cannot be evaluated with supplied variables. */ 063 public boolean evalBoolean(Map<String, String> values, 064 String formula) throws LomikelException { 065 String r = eval(values, formula, "boolean"); 066 return Boolean.parseBoolean(r); 067 } 068 public static double qdistance0(double v1, double v2, double v3, double v4) { 069 double distance = Math.pow(v1-v2, 2) + Math.pow(v3-v4, 2); 070 return Math.sqrt(distance); 071 } 072 073 074 /** Evaluate double formula with supplied variables and values. 075 * @param values The names and values of variables. 076 * @param formula The formula to be evaluated. It should use supplied 077 * variables, which will be filled with supplied values. 078 * @throws LomikelException If formula cannot be evaluated with supplied variables. */ 079 public double evalDouble(Map<String, String> values, 080 String formula) throws LomikelException { 081 String r = eval(values, formula, "double"); 082 return Double.parseDouble(r); 083 } 084 085 /** Evaluate formula with supplied variables and values. 086 * @param values The names and values of variables. 087 * @param formula The formula to be evaluated. It should use supplied 088 * variables, which will be filled with supplied values. 089 * @param type The formula result type. 090 * @throws LomikelException If formula cannot be evaluated with supplied variables. */ 091 public String eval(Map<String, String> values, 092 String formula, 093 String type) throws LomikelException { 094 try { 095 if (values != null) { 096 for (String variable : _variables) { 097 if (values.containsKey(variable)) { 098 setVariable(variable, values.get(variable)); 099 } 100 } 101 } 102 _interpreter.eval(type + " result = " + formula + ";"); 103 Object o = _interpreter.get("result"); 104 String result = o.toString(); 105 return result; 106 } 107 catch (EvalError e) { 108 throw new LomikelException("Can't evaluate formula: " + formula, e); 109 } 110 } 111 112 /** Declare variable. Set to <tt>0</tt>. 113 * @param name The name of variables.*/ 114 public void setVariable(String name) { 115 setVariable(name, "0"); 116 } 117 118 /** Set variable. 119 * @param name The name of variables. 120 * @param value The value of variables. 121 * Undefined types are considered <code>double</code>, 122 * unknown types are taken as <code>string</code>. */ 123 public void setVariable(String name, 124 String value) { 125 String fname = varName(name); 126 String type = "double"; 127 if (_schema.type(name) != null) { 128 type = _schema.type(name); 129 } 130 try { 131 switch (type) { 132 case "float": 133 case "java.lang.Float": 134 _interpreter.set(fname, Float.parseFloat(value)); 135 break; 136 case "double": 137 case "java.lang.Double": 138 _interpreter.set(fname, Double.parseDouble(value)); 139 break; 140 case "integer": 141 case "java.lang.Integer": 142 _interpreter.set(fname, Integer.parseInt(value)); 143 break; 144 case "long": 145 case "java.lang.Long": 146 _interpreter.set(fname, Long.parseLong(value)); 147 break; 148 case "short": 149 case "java.lang.Short": 150 _interpreter.set(fname, Short.parseShort(value)); 151 break; 152 default: // includes "string" 153 _interpreter.set(fname, value); 154 } 155 } 156 catch (EvalError e) { 157 log.error("Cannot assign " + name + " = '" + value); 158 } 159 } 160 161 /** TBD */ 162 public void setVariable(String name, 163 double value) { 164 String fname = varName(name); 165 try { 166 _interpreter.set(fname, value); 167 } 168 catch (EvalError e) { 169 log.error("Cannot assign " + name + " = '" + value); 170 } 171 } 172 173 /** Set array variable. 174 * @param name The name of variables. 175 * @param values The values of variables. 176 * Undefined types are considered <code>double</code>, 177 * unknown types are taken as <code>string</code>. */ 178 public void setVariable(String name, 179 String[] values) { 180 String fname = varName(name); 181 int length = values.length; 182 String type = "double"; 183 if (_schema.type(name) != null) { 184 type = _schema.type(name); 185 } 186 try { 187 switch (type) { 188 case "float": 189 case "java.lang.Float": 190 _interpreter.set(fname, Stream.of(values).mapToDouble(Float::parseFloat).toArray()); 191 break; 192 case "double": 193 case "java.lang.Double": 194 _interpreter.set(fname, Stream.of(values).mapToDouble(Double::parseDouble).toArray()); 195 break; 196 case "integer": 197 case "java.lang.Integer": 198 _interpreter.set(fname, Stream.of(values).mapToInt(Integer::parseInt).toArray()); 199 break; 200 case "long": 201 case "java.lang.Long": 202 _interpreter.set(fname, Stream.of(values).mapToLong(Long::parseLong).toArray()); 203 break; 204 case "short": 205 case "java.lang.Short": 206 _interpreter.set(fname, Stream.of(values).mapToInt(Short::parseShort).toArray()); 207 break; 208 default: // includes "string" 209 _interpreter.set(fname, values); 210 } 211 } 212 catch (EvalError e) { 213 log.error("Cannot assign " + name + " = '" + values); 214 } 215 } 216 217 /** TBD */ 218 public void setVariable(String name, 219 double[] values) { 220 String fname = varName(name); 221 try { 222 _interpreter.set(fname, values); 223 } 224 catch (EvalError e) { 225 log.error("Cannot assign " + name + " = '" + values); 226 } 227 } 228 229 /** Give the array of used variables. 230 * @return The array of used variables. */ 231 public String[] variables() { 232 return _variables.toArray(new String[0]); 233 } 234 235 /** Shows whether a variable exists. 236 * @param var The name of a variable. 237 * @return Whether that variable exists. */ 238 public boolean hasVariable(String var) { 239 return _variables.contains(var); 240 } 241 242 /** Add variables to the list of used variables. 243 * Only add variables available in {@link Schema}. 244 * @param formula The formula to be used for list of used variables. */ 245 public void setVariables(String formula) { 246 Set<String> cn = _schema.columnNames(); 247 for (String column : cn) { 248 if (formula.contains(varName(column))) { 249 _variables.add(column); 250 } 251 } 252 } 253 254 /** Add variables to the list of used variables. 255 * Add variables even if they are not available in {@link Schema}. 256 * @param formula The formula to be used for list of used variables. */ 257 public void forceVariables(String variables) { 258 for (String v : variables.split(" ")) { 259 _variables.add(v.trim()); 260 } 261 } 262 263 /** Give variable name from the database name. 264 * @param fullName The fill name of the database column. 265 * @return The schema variable name. */ 266 protected String varName(String fullName) { 267 return fullName; 268 } 269 270 private Schema _schema; 271 272 private Set<String> _variables = new TreeSet<>(); 273 274 private Interpreter _interpreter; 275 276 /** Logging . */ 277 private static Logger log = LogManager.getLogger(Evaluator.class); 278 279 }