/* * Copyright (C) 2015 Stratio (http://stratio.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.stratio.morphlines.commons; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.util.Collection; import java.util.Collections; import org.kitesdk.morphline.api.Command; import org.kitesdk.morphline.api.CommandBuilder; import org.kitesdk.morphline.api.MorphlineContext; import org.kitesdk.morphline.api.Record; import org.kitesdk.morphline.stdio.AbstractParser; import com.typesafe.config.Config; //@formatter:off /** * The calculator command makes computations between fields or literals and save result into a new field as BigDecimal. * <ul> * <li><em>leftOperand</em>: Field or literal which will be left operand.</li> * <li><em>rightOperand</em>: Field or literal which will be right operand.</a>. * <li><em>operator</em>: Operator. Possible values: +,-,*,/,%,sqrt,^</li> * <li><em>output</em>: Output field. Default: output</li> * </ul> * <code> * Example: * { * { calculator { leftOperand : metric operator : "/" rightOperand : 2 output : "result" } } * } * </code> * */ //@formatter:on public class CalculatorBuilder implements CommandBuilder { private static final String CONF_OPERAND1 = "leftOperand"; private static final String CONF_OPERAND2 = "rightOperand"; private static final String CONF_OUTPUT_FIELD = "output"; private static final String CONF_OPERATOR = "operator"; private static final String DEFAULT_OUT_FIELD = "output"; public Collection<String> getNames() { return Collections.singletonList("calculator"); } public Command build(Config config, Command parent, Command child, MorphlineContext context) { return new Calculator(this, config, parent, child, context); } private static final class Calculator extends AbstractParser { private String operator; private String outField; private Object op1; private Object op2; protected Calculator(CommandBuilder builder, Config config, Command parent, Command child, MorphlineContext context) { super(builder, config, parent, child, context); String operand1 = getConfigs().getString(config, CONF_OPERAND1); String operand2 = getConfigs().getString(config, CONF_OPERAND2, null); operator = getConfigs().getString(config, CONF_OPERATOR); outField = getConfigs().getString(config, CONF_OUTPUT_FIELD, DEFAULT_OUT_FIELD); checkOperands(operand1, operand2); } @Override protected boolean doProcess(Record record, InputStream stream) throws IOException { Record outputRecord = record.copy(); BigDecimal result = doComputation(outputRecord); outputRecord.put(outField, result); return getChild().process(outputRecord); } private void checkOperands(String rawOp1, String rawOp2) { try { op1 = new BigDecimal(rawOp1); } catch (NumberFormatException ex) { op1 = rawOp1; } if (rawOp2 != null) { try { op2 = new BigDecimal(rawOp2); } catch (NumberFormatException ex) { op2 = rawOp2; } } } private BigDecimal doComputation(Record record) { BigDecimal opLeft = null, opRight = null, result = null; if (op1 instanceof String) { String field = String.valueOf(op1); if(field != null){ opLeft = new BigDecimal(String.valueOf(record.getFirstValue(field))); } else { throw new RuntimeException("Inexistent field " + field); } } else if (op1 instanceof BigDecimal) { opLeft = (BigDecimal) op1; } if (op2 instanceof String) { String field = String.valueOf(op2); if(field != null){ opRight = new BigDecimal(String.valueOf(record.getFirstValue(field))); } else { throw new RuntimeException("Inexistent field " + field); } } else if (op2 instanceof BigDecimal) { opRight = (BigDecimal) op2; } if ("+".equals(operator)) { result = opLeft.add(opRight); } else if ("-".equals(operator)) { result = opLeft.subtract(opRight); } else if ("*".equals(operator)) { result = opLeft.multiply(opRight); } else if ("/".equals(operator)) { result = opLeft.divide(opRight); } else if ("^".equals(operator)) { result = opLeft.pow(opRight.intValue()); } else if ("sqrt".equals(operator)) { result = new BigDecimal(Math.sqrt(opLeft.doubleValue())); } else if ("%".equals(operator)) { result = opLeft.remainder(opRight); } else { throw new RuntimeException("Incorrect operator"); } return result; } } }