package org.myrobotlab.document.transformer; import org.myrobotlab.document.Document; import org.myrobotlab.logging.LoggerFactory; import org.slf4j.Logger; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; import java.util.ArrayList; import java.util.HashSet; import java.util.List; public class MathValues extends AbstractStage { public final static Logger log = LoggerFactory.getLogger(MathValues.class.getCanonicalName()); private List<String> inputFields = null; private String outputField = null; private String expressionString = null; // TODO: can I re-use this? private Expression expr; @Override public void startStage(StageConfiguration config) { // TODO: implement me! // can i reuse the expresion? inputFields = config.getListParam("inputFields"); outputField = config.getProperty("outputField"); expressionString = config.getProperty("expressionString"); // create the expression builder ExpressionBuilder eBuilder = new ExpressionBuilder(expressionString); // set up the variables. eBuilder.variables(new HashSet<String>(inputFields)); // compile the expression. expr = eBuilder.build(); } @Override public List<Document> processDocument(Document doc) { // divide the double values in 2 fields, store the result in the quotent // field. for (String inField : inputFields) { if (!doc.hasField(inField)) { // doc missing one of the input fields? // TODO: maybe we want to control this behavior (ignore unset fields?) return null; } } ArrayList<Double> results = new ArrayList<Double>(); int size = doc.getField(inputFields.get(0)).size(); for (int i = 0; i < size; i++) { // load the variables into the expression for (String inField : inputFields) { Double d = convertToDouble(doc.getField(inField).get(i)); if (d == null) { // we weren't able to parse one of the input variables. // TODO: log a warning or something? return null; } expr.setVariable(inField, d); } // Division by zero might result? try { Double result = expr.evaluate(); results.add(result); } catch (ArithmeticException e) { log.info("Division by zero error! {}", doc.getId()); } } for (Double v : results) { doc.addToField(outputField, v); } return null; } private Double convertToDouble(Object obj) throws ClassCastException { Double doubleVal = null; if (obj instanceof Integer) { doubleVal = new Double(((Integer) obj).intValue()); } else if (obj instanceof Double) { doubleVal = (Double) obj; } else if (obj instanceof String) { // ... this could throw an exception, try { // TODO: some cleaning of the string to only allow numbers and periods // and minus signs... String strVal = ((String) obj).replaceAll(",", "").trim(); doubleVal = Double.valueOf(strVal); } catch (NumberFormatException e) { // ok. we couldn't cast it. log.info("Unable to parse {} into a double.", obj); return null; } } else { throw new ClassCastException("Cannot convert " + obj.getClass().getName() + " to Double."); } return doubleVal; } @Override public void stopStage() { // TODO Auto-generated method stub } @Override public void flush() { // TODO Auto-generated method stub } }