Java tutorial
// ============================================================================ // Copyright (C) 2006-2016 Talend Inc. - www.talend.com // // This source code is available under agreement available at // https://github.com/Talend/data-prep/blob/master/LICENSE // // You should have received a copy of the agreement // along with this program; if not, write to Talend SA // 9 rue Pages 92150 Suresnes, France // // ============================================================================ package org.talend.dataprep.transformation.actions.math; import static java.math.RoundingMode.HALF_UP; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.talend.daikon.exception.ExceptionContext; import org.talend.daikon.exception.TalendRuntimeException; import org.talend.daikon.number.BigDecimalParser; import org.talend.dataprep.api.action.Action; import org.talend.dataprep.api.dataset.ColumnMetadata; import org.talend.dataprep.api.dataset.RowMetadata; import org.talend.dataprep.api.dataset.row.DataSetRow; import org.talend.dataprep.api.type.Type; import org.talend.dataprep.exception.error.ActionErrorCodes; import org.talend.dataprep.i18n.ActionsBundle; import org.talend.dataprep.parameters.Parameter; import org.talend.dataprep.parameters.ParameterType; import org.talend.dataprep.parameters.SelectParameter; import org.talend.dataprep.transformation.actions.category.ActionCategory; import org.talend.dataprep.transformation.actions.common.AbstractActionMetadata; import org.talend.dataprep.transformation.actions.common.ColumnAction; import org.talend.dataprep.transformation.actions.common.OtherColumnParameters; import org.talend.dataprep.transformation.api.action.context.ActionContext; import org.talend.dataprep.util.NumericHelper; /** * Concat action concatenates 2 columns into a new one. The new column name will be "column_source + selected_column." * The new column content is "prefix + column_source + separator + selected_column + suffix" */ @Action(AbstractActionMetadata.ACTION_BEAN_PREFIX + NumericOperations.ACTION_NAME) public class NumericOperations extends AbstractActionMetadata implements ColumnAction, OtherColumnParameters { /** * The action name. */ public static final String ACTION_NAME = "numeric_ops"; //$NON-NLS-1$ /** * Mode: tells if operand is taken from another column or is a constant */ public static final String MODE_PARAMETER = "mode"; //$NON-NLS-1$ /** * The operator to use. */ public static final String OPERATOR_PARAMETER = "operator"; //$NON-NLS-1$ /** * The operand to use. */ public static final String OPERAND_PARAMETER = "operand"; //$NON-NLS-1$ private static final String PLUS = "+"; private static final String MINUS = "-"; private static final String MULTIPLY = "x"; private static final String DIVIDE = "/"; @Override public String getName() { return ACTION_NAME; } @Override public String getCategory() { return ActionCategory.MATH.getDisplayName(); } @Override public List<Parameter> getParameters() { final List<Parameter> parameters = super.getParameters(); //@formatter:off parameters.add(SelectParameter.Builder.builder().name(OPERATOR_PARAMETER).item(PLUS).item(MULTIPLY) .item(MINUS).item(DIVIDE).defaultValue(MULTIPLY).build()); //@formatter:on //@formatter:off parameters.add(SelectParameter.Builder.builder().name(MODE_PARAMETER) .item(CONSTANT_MODE, CONSTANT_MODE, new Parameter(OPERAND_PARAMETER, ParameterType.STRING, "2")) .item(OTHER_COLUMN_MODE, OTHER_COLUMN_MODE, new Parameter(SELECTED_COLUMN_PARAMETER, ParameterType.COLUMN, // StringUtils.EMPTY, false, false, StringUtils.EMPTY)) // .defaultValue(CONSTANT_MODE).build()); //@formatter:on return ActionsBundle.attachToAction(parameters, this); } @Override public boolean acceptField(ColumnMetadata column) { Type columnType = Type.get(column.getType()); return Type.NUMERIC.isAssignableFrom(columnType); } @Override public void compile(ActionContext context) { super.compile(context); if (context.getActionStatus() == ActionContext.ActionStatus.OK) { checkParameters(context.getParameters(), context.getRowMetadata()); // Create column final Map<String, String> parameters = context.getParameters(); final String columnId = context.getColumnId(); final RowMetadata rowMetadata = context.getRowMetadata(); final ColumnMetadata sourceColumn = rowMetadata.getById(columnId); final String operator = parameters.get(OPERATOR_PARAMETER); String operandName; if (parameters.get(MODE_PARAMETER).equals(CONSTANT_MODE)) { operandName = parameters.get(OPERAND_PARAMETER); } else { final ColumnMetadata selectedColumn = rowMetadata .getById(parameters.get(SELECTED_COLUMN_PARAMETER)); operandName = selectedColumn.getName(); } context.column("result", r -> { final ColumnMetadata c = ColumnMetadata.Builder // .column() // .name(sourceColumn.getName() + " " + operator + " " + operandName) // .type(Type.DOUBLE) // .build(); rowMetadata.insertAfter(columnId, c); return c; }); } } @Override public void applyOnColumn(final DataSetRow row, final ActionContext context) { final Map<String, String> parameters = context.getParameters(); final String columnId = context.getColumnId(); final RowMetadata rowMetadata = context.getRowMetadata(); // extract transformation parameters final String operator = parameters.get(OPERATOR_PARAMETER); String operand; if (parameters.get(MODE_PARAMETER).equals(CONSTANT_MODE)) { operand = parameters.get(OPERAND_PARAMETER); } else { final ColumnMetadata selectedColumn = rowMetadata.getById(parameters.get(SELECTED_COLUMN_PARAMETER)); operand = row.get(selectedColumn.getId()); } // column creation final String newColumnId = context.column("result"); // set new column value final String sourceValue = row.get(columnId); final String newValue = compute(sourceValue, operator, operand); row.set(newColumnId, newValue); } protected String compute(final String stringOperandOne, final String operator, final String stringOperandTwo) { if (!NumericHelper.isBigDecimal(stringOperandOne) || !NumericHelper.isBigDecimal(stringOperandTwo)) { return StringUtils.EMPTY; } try { final BigDecimal operandOne = BigDecimalParser.toBigDecimal(stringOperandOne); final BigDecimal operandTwo = BigDecimalParser.toBigDecimal(stringOperandTwo); BigDecimal toReturn; final int scale = 2; final RoundingMode rm = HALF_UP; switch (operator) { case PLUS: toReturn = operandOne.add(operandTwo); break; case MULTIPLY: toReturn = operandOne.multiply(operandTwo); break; case MINUS: toReturn = operandOne.subtract(operandTwo); break; case DIVIDE: toReturn = operandOne.divide(operandTwo, scale, rm); break; default: return ""; } // Format result: return toReturn.setScale(scale, rm).stripTrailingZeros().toPlainString(); } catch (ArithmeticException | NullPointerException e) { return StringUtils.EMPTY; } } /** * Check that the selected column parameter is correct : defined in the parameters and there's a matching column. If * the parameter is invalid, an exception is thrown. * * @param parameters where to look the parameter value. * @param rowMetadata the row where to look for the column. */ private void checkParameters(Map<String, String> parameters, RowMetadata rowMetadata) { if (parameters.get(MODE_PARAMETER).equals(CONSTANT_MODE) && !parameters.containsKey(OPERAND_PARAMETER)) { throw new TalendRuntimeException(ActionErrorCodes.BAD_ACTION_PARAMETER, ExceptionContext.build().put("paramName", OPERAND_PARAMETER)); } else if (!parameters.get(MODE_PARAMETER).equals(CONSTANT_MODE) && (!parameters.containsKey(SELECTED_COLUMN_PARAMETER) || rowMetadata.getById(parameters.get(SELECTED_COLUMN_PARAMETER)) == null)) { throw new TalendRuntimeException(ActionErrorCodes.BAD_ACTION_PARAMETER, ExceptionContext.build().put("paramName", SELECTED_COLUMN_PARAMETER)); } } @Override public Set<Behavior> getBehavior() { return EnumSet.of(Behavior.METADATA_CREATE_COLUMNS); } }