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  }