/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.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 org.pentaho.di.trans.steps.calculator;
import java.util.ArrayList;
import java.util.List;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleStepException;
import org.pentaho.di.core.exception.KettleValueException;
import org.pentaho.di.core.row.RowDataUtil;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.ValueDataUtil;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.BaseStep;
import org.pentaho.di.trans.step.StepDataInterface;
import org.pentaho.di.trans.step.StepInterface;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaInterface;
/**
* Calculate new field values using pre-defined functions.
*
* @author Matt
* @since 8-sep-2005
*/
public class Calculator extends BaseStep implements StepInterface {
private static Class<?> PKG = CalculatorMeta.class; // for i18n purposes, needed by Translator2!!
public class FieldIndexes {
public int indexName;
public int indexA;
public int indexB;
public int indexC;
}
private CalculatorMeta meta;
private CalculatorData data;
public Calculator( StepMeta stepMeta, StepDataInterface stepDataInterface, int copyNr, TransMeta transMeta,
Trans trans ) {
super( stepMeta, stepDataInterface, copyNr, transMeta, trans );
}
@Override
public boolean processRow( StepMetaInterface smi, StepDataInterface sdi ) throws KettleException {
meta = (CalculatorMeta) smi;
data = (CalculatorData) sdi;
Object[] r = getRow(); // get row, set busy!
if ( r == null ) { // no more input to be expected...
setOutputDone();
data.clearValuesMetaMapping();
return false;
}
if ( first ) {
first = false;
data.setOutputRowMeta( getInputRowMeta().clone() );
meta.getFields( data.getOutputRowMeta(), getStepname(), null, null, this, repository, metaStore );
// get all metadata, including source rows and temporary fields.
data.setCalcRowMeta( meta.getAllFields( getInputRowMeta() ) );
data.setFieldIndexes( new FieldIndexes[meta.getCalculation().length] );
List<Integer> tempIndexes = new ArrayList<Integer>();
// Calculate the indexes of the values and arguments in the target data or temporary data
// We do this in advance to save time later on.
//
//CHECKSTYLE:Indentation:OFF
for ( int i = 0; i < meta.getCalculation().length; i++ ) {
CalculatorMetaFunction function = meta.getCalculation()[i];
data.getFieldIndexes()[i] = new FieldIndexes();
if ( !Utils.isEmpty( function.getFieldName() ) ) {
data.getFieldIndexes()[i].indexName = data.getCalcRowMeta().indexOfValue( function.getFieldName() );
if ( data.getFieldIndexes()[i].indexName < 0 ) {
// Nope: throw an exception
throw new KettleStepException( BaseMessages.getString(
PKG, "Calculator.Error.UnableFindField", function.getFieldName(), "" + ( i + 1 ) ) );
}
} else {
throw new KettleStepException( BaseMessages.getString( PKG, "Calculator.Error.NoNameField", ""
+ ( i + 1 ) ) );
}
if ( !Utils.isEmpty( function.getFieldA() ) ) {
if ( function.getCalcType() != CalculatorMetaFunction.CALC_CONSTANT ) {
data.getFieldIndexes()[i].indexA = data.getCalcRowMeta().indexOfValue( function.getFieldA() );
if ( data.getFieldIndexes()[i].indexA < 0 ) {
// Nope: throw an exception
throw new KettleStepException( "Unable to find the first argument field '"
+ function.getFieldName() + " for calculation #" + ( i + 1 ) );
}
} else {
data.getFieldIndexes()[i].indexA = -1;
}
} else {
throw new KettleStepException( "There is no first argument specified for calculated field #" + ( i + 1 ) );
}
if ( !Utils.isEmpty( function.getFieldB() ) ) {
data.getFieldIndexes()[i].indexB = data.getCalcRowMeta().indexOfValue( function.getFieldB() );
if ( data.getFieldIndexes()[i].indexB < 0 ) {
// Nope: throw an exception
throw new KettleStepException( "Unable to find the second argument field '"
+ function.getFieldName() + " for calculation #" + ( i + 1 ) );
}
}
data.getFieldIndexes()[i].indexC = -1;
if ( !Utils.isEmpty( function.getFieldC() ) ) {
data.getFieldIndexes()[i].indexC = data.getCalcRowMeta().indexOfValue( function.getFieldC() );
if ( data.getFieldIndexes()[i].indexC < 0 ) {
// Nope: throw an exception
throw new KettleStepException( "Unable to find the third argument field '"
+ function.getFieldName() + " for calculation #" + ( i + 1 ) );
}
}
if ( function.isRemovedFromResult() ) {
tempIndexes.add( getInputRowMeta().size() + i );
}
}
// Convert temp indexes to int[]
data.setTempIndexes( new int[tempIndexes.size()] );
for ( int i = 0; i < data.getTempIndexes().length; i++ ) {
data.getTempIndexes()[i] = tempIndexes.get( i );
}
}
if ( log.isRowLevel() ) {
logRowlevel( BaseMessages.getString( PKG, "Calculator.Log.ReadRow" )
+ getLinesRead() + " : " + getInputRowMeta().getString( r ) );
}
try {
Object[] row = calcFields( getInputRowMeta(), r );
putRow( data.getOutputRowMeta(), row ); // copy row to possible alternate rowset(s).
if ( log.isRowLevel() ) {
logRowlevel( "Wrote row #" + getLinesWritten() + " : " + getInputRowMeta().getString( r ) );
}
if ( checkFeedback( getLinesRead() ) ) {
if ( log.isBasic() ) {
logBasic( BaseMessages.getString( PKG, "Calculator.Log.Linenr", "" + getLinesRead() ) );
}
}
} catch ( KettleException e ) {
if ( getStepMeta().isDoingErrorHandling() ) {
putError( getInputRowMeta(), r, 1, e.toString(), null, "CALC001" );
} else {
logError( BaseMessages.getString( PKG, "Calculator.ErrorInStepRunning" + " : " + e.getMessage() ) );
throw new KettleStepException( BaseMessages.getString( PKG, "Calculator.ErrorInStepRunning" ), e );
}
}
return true;
}
/**
* @param inputRowMeta
* the input row metadata
* @param r
* the input row (data)
* @return A row including the calculations, excluding the temporary values
* @throws KettleValueException
* in case there is a calculation error.
*/
private Object[] calcFields( RowMetaInterface inputRowMeta, Object[] r ) throws KettleValueException {
// First copy the input data to the new result...
Object[] calcData = RowDataUtil.resizeArray( r, data.getCalcRowMeta().size() );
for ( int i = 0, index = inputRowMeta.size() + i; i < meta.getCalculation().length; i++, index++ ) {
CalculatorMetaFunction fn = meta.getCalculation()[i];
if ( !Utils.isEmpty( fn.getFieldName() ) ) {
ValueMetaInterface targetMeta = data.getCalcRowMeta().getValueMeta( index );
// Get the metadata & the data...
// ValueMetaInterface metaTarget = data.calcRowMeta.getValueMeta(i);
ValueMetaInterface metaA = null;
Object dataA = null;
if ( data.getFieldIndexes()[i].indexA >= 0 ) {
metaA = data.getCalcRowMeta().getValueMeta( data.getFieldIndexes()[ i ].indexA );
dataA = calcData[ data.getFieldIndexes()[i].indexA];
}
ValueMetaInterface metaB = null;
Object dataB = null;
if ( data.getFieldIndexes()[i].indexB >= 0 ) {
metaB = data.getCalcRowMeta().getValueMeta( data.getFieldIndexes()[ i ].indexB );
dataB = calcData[ data.getFieldIndexes()[i].indexB];
}
ValueMetaInterface metaC = null;
Object dataC = null;
if ( data.getFieldIndexes()[i].indexC >= 0 ) {
metaC = data.getCalcRowMeta().getValueMeta( data.getFieldIndexes()[ i ].indexC );
dataC = calcData[ data.getFieldIndexes()[i].indexC];
}
int calcType = fn.getCalcType();
// The data types are those of the first argument field, convert to the target field.
// Exceptions:
// - multiply can be string
// - constant is string
// - all date functions except add days/months
// - hex encode / decodes
int resultType;
if ( metaA != null ) {
resultType = metaA.getType();
} else {
resultType = ValueMetaInterface.TYPE_NONE;
}
switch ( calcType ) {
case CalculatorMetaFunction.CALC_NONE:
break;
case CalculatorMetaFunction.CALC_COPY_OF_FIELD: // Create a copy of field A
calcData[index] = dataA;
break;
case CalculatorMetaFunction.CALC_ADD: // A + B
calcData[index] = ValueDataUtil.plus( metaA, dataA, metaB, dataB );
if ( metaA.isString() || metaB.isString() ) {
resultType = ValueMetaInterface.TYPE_STRING;
}
break;
case CalculatorMetaFunction.CALC_SUBTRACT: // A - B
calcData[index] = ValueDataUtil.minus( metaA, dataA, metaB, dataB );
if ( metaA.isDate() ) {
resultType = ValueMetaInterface.TYPE_INTEGER;
}
break;
case CalculatorMetaFunction.CALC_MULTIPLY: // A * B
calcData[index] = ValueDataUtil.multiply( metaA, dataA, metaB, dataB );
if ( metaA.isString() || metaB.isString() ) {
resultType = ValueMetaInterface.TYPE_STRING;
}
break;
case CalculatorMetaFunction.CALC_DIVIDE: // A / B
calcData[index] = ValueDataUtil.divide( metaA, dataA, metaB, dataB );
break;
case CalculatorMetaFunction.CALC_SQUARE: // A * A
calcData[index] = ValueDataUtil.multiply( metaA, dataA, metaA, dataA );
break;
case CalculatorMetaFunction.CALC_SQUARE_ROOT: // SQRT( A )
calcData[index] = ValueDataUtil.sqrt( metaA, dataA );
break;
case CalculatorMetaFunction.CALC_PERCENT_1: // 100 * A / B
calcData[index] = ValueDataUtil.percent1( metaA, dataA, metaB, dataB );
break;
case CalculatorMetaFunction.CALC_PERCENT_2: // A - ( A * B / 100 )
calcData[index] = ValueDataUtil.percent2( metaA, dataA, metaB, dataB );
break;
case CalculatorMetaFunction.CALC_PERCENT_3: // A + ( A * B / 100 )
calcData[index] = ValueDataUtil.percent3( metaA, dataA, metaB, dataB );
break;
case CalculatorMetaFunction.CALC_COMBINATION_1: // A + B * C
calcData[index] = ValueDataUtil.combination1( metaA, dataA, metaB, dataB, metaC, dataC );
break;
case CalculatorMetaFunction.CALC_COMBINATION_2: // SQRT( A*A + B*B )
calcData[index] = ValueDataUtil.combination2( metaA, dataA, metaB, dataB );
break;
case CalculatorMetaFunction.CALC_ROUND_1: // ROUND( A )
calcData[index] = ValueDataUtil.round( metaA, dataA );
break;
case CalculatorMetaFunction.CALC_ROUND_2: // ROUND( A , B )
calcData[index] = ValueDataUtil.round( metaA, dataA, metaB, dataB );
break;
case CalculatorMetaFunction.CALC_ROUND_CUSTOM_1: // ROUND( A , B )
calcData[index] = ValueDataUtil.round( metaA, dataA, metaB.getNumber( dataB ).intValue() );
break;
case CalculatorMetaFunction.CALC_ROUND_CUSTOM_2: // ROUND( A , B, C )
calcData[index] = ValueDataUtil.round( metaA, dataA, metaB, dataB, metaC.getNumber( dataC ).intValue() );
break;
case CalculatorMetaFunction.CALC_ROUND_STD_1: // ROUND( A )
calcData[index] = ValueDataUtil.round( metaA, dataA, java.math.BigDecimal.ROUND_HALF_UP );
break;
case CalculatorMetaFunction.CALC_ROUND_STD_2: // ROUND( A , B )
calcData[index] = ValueDataUtil.round( metaA, dataA, metaB, dataB, java.math.BigDecimal.ROUND_HALF_UP );
break;
case CalculatorMetaFunction.CALC_CEIL: // CEIL( A )
calcData[index] = ValueDataUtil.ceil( metaA, dataA );
break;
case CalculatorMetaFunction.CALC_FLOOR: // FLOOR( A )
calcData[index] = ValueDataUtil.floor( metaA, dataA );
break;
case CalculatorMetaFunction.CALC_CONSTANT: // Set field to constant value...
calcData[index] = fn.getFieldA(); // A string
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_NVL: // Replace null values with another value
calcData[index] = ValueDataUtil.nvl( metaA, dataA, metaB, dataB );
break;
case CalculatorMetaFunction.CALC_ADD_DAYS: // Add B days to date field A
calcData[index] = ValueDataUtil.addDays( metaA, dataA, metaB, dataB );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_ADD_HOURS: // Add B hours to date field A
calcData[index] = ValueDataUtil.addHours( metaA, dataA, metaB, dataB );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_ADD_MINUTES: // Add B minutes to date field A
calcData[index] = ValueDataUtil.addMinutes( metaA, dataA, metaB, dataB );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_YEAR_OF_DATE: // What is the year (Integer) of a date?
calcData[index] = ValueDataUtil.yearOfDate( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_MONTH_OF_DATE: // What is the month (Integer) of a date?
calcData[index] = ValueDataUtil.monthOfDate( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_DAY_OF_YEAR: // What is the day of year (Integer) of a date?
calcData[index] = ValueDataUtil.dayOfYear( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_DAY_OF_MONTH: // What is the day of month (Integer) of a date?
calcData[index] = ValueDataUtil.dayOfMonth( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_DAY_OF_WEEK: // What is the day of week (Integer) of a date?
calcData[index] = ValueDataUtil.dayOfWeek( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_WEEK_OF_YEAR: // What is the week of year (Integer) of a date?
calcData[index] = ValueDataUtil.weekOfYear( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_WEEK_OF_YEAR_ISO8601: // What is the week of year (Integer) of a date ISO8601
// style?
calcData[index] = ValueDataUtil.weekOfYearISO8601( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_YEAR_OF_DATE_ISO8601: // What is the year (Integer) of a date ISO8601 style?
calcData[index] = ValueDataUtil.yearOfDateISO8601( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_BYTE_TO_HEX_ENCODE: // Byte to Hex encode string field A
calcData[index] = ValueDataUtil.byteToHexEncode( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_HEX_TO_BYTE_DECODE: // Hex to Byte decode string field A
calcData[index] = ValueDataUtil.hexToByteDecode( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_CHAR_TO_HEX_ENCODE: // Char to Hex encode string field A
calcData[index] = ValueDataUtil.charToHexEncode( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_HEX_TO_CHAR_DECODE: // Hex to Char decode string field A
calcData[index] = ValueDataUtil.hexToCharDecode( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_CRC32: // CRC32
calcData[index] = ValueDataUtil.ChecksumCRC32( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_ADLER32: // ADLER32
calcData[index] = ValueDataUtil.ChecksumAdler32( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_MD5: // MD5
calcData[index] = ValueDataUtil.createChecksum( metaA, dataA, "MD5" );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_SHA1: // SHA-1
calcData[index] = ValueDataUtil.createChecksum( metaA, dataA, "SHA-1" );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_LEVENSHTEIN_DISTANCE: // LEVENSHTEIN DISTANCE
calcData[index] = ValueDataUtil.getLevenshtein_Distance( metaA, dataA, metaB, dataB );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_METAPHONE: // METAPHONE
calcData[index] = ValueDataUtil.get_Metaphone( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_DOUBLE_METAPHONE: // Double METAPHONE
calcData[index] = ValueDataUtil.get_Double_Metaphone( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_ABS: // ABS( A )
calcData[index] = ValueDataUtil.abs( metaA, dataA );
break;
case CalculatorMetaFunction.CALC_REMOVE_TIME_FROM_DATE: // Remove Time from field A
calcData[index] = ValueDataUtil.removeTimeFromDate( metaA, dataA );
break;
case CalculatorMetaFunction.CALC_DATE_DIFF: // DateA - DateB
calcData[index] = ValueDataUtil.DateDiff( metaA, dataA, metaB, dataB, "d" );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_ADD3: // A + B + C
calcData[index] = ValueDataUtil.plus3( metaA, dataA, metaB, dataB, metaC, dataC );
if ( metaA.isString() || metaB.isString() || metaC.isString() ) {
resultType = ValueMetaInterface.TYPE_STRING;
}
break;
case CalculatorMetaFunction.CALC_INITCAP: // InitCap( A )
calcData[index] = ValueDataUtil.initCap( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_UPPER_CASE: // UpperCase( A )
calcData[index] = ValueDataUtil.upperCase( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_LOWER_CASE: // UpperCase( A )
calcData[index] = ValueDataUtil.lowerCase( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_MASK_XML: // escapeXML( A )
calcData[index] = ValueDataUtil.escapeXML( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_USE_CDATA: // CDATA( A )
calcData[index] = ValueDataUtil.useCDATA( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_REMOVE_CR: // REMOVE CR FROM A
calcData[index] = ValueDataUtil.removeCR( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_REMOVE_LF: // REMOVE LF FROM A
calcData[index] = ValueDataUtil.removeLF( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_REMOVE_CRLF: // REMOVE CRLF FROM A
calcData[index] = ValueDataUtil.removeCRLF( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_REMOVE_TAB: // REMOVE TAB FROM A
calcData[index] = ValueDataUtil.removeTAB( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_GET_ONLY_DIGITS: // GET ONLY DIGITS FROM A
calcData[index] = ValueDataUtil.getDigits( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_REMOVE_DIGITS: // REMOVE DIGITS FROM A
calcData[index] = ValueDataUtil.removeDigits( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_STRING_LEN: // RETURN THE LENGTH OF A
calcData[index] = ValueDataUtil.stringLen( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_LOAD_FILE_CONTENT_BINARY: // LOAD CONTENT OF A FILE A IN A BLOB
calcData[index] = ValueDataUtil.loadFileContentInBinary( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_ADD_TIME_TO_DATE: // Add time B to a date A
calcData[index] = ValueDataUtil.addTimeToDate( metaA, dataA, metaB, dataB, metaC, dataC );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_QUARTER_OF_DATE: // What is the quarter (Integer) of a date?
calcData[index] = ValueDataUtil.quarterOfDate( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_SUBSTITUTE_VARIABLE: // variable substitution in string
calcData[index] = environmentSubstitute( dataA.toString() );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_UNESCAPE_XML: // UnescapeXML( A )
calcData[index] = ValueDataUtil.unEscapeXML( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_ESCAPE_HTML: // EscapeHTML( A )
calcData[index] = ValueDataUtil.escapeHTML( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_UNESCAPE_HTML: // UnescapeHTML( A )
calcData[index] = ValueDataUtil.unEscapeHTML( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_ESCAPE_SQL: // EscapeSQL( A )
calcData[index] = ValueDataUtil.escapeSQL( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_DATE_WORKING_DIFF: // DateWorkingDiff( A , B)
calcData[index] = ValueDataUtil.DateWorkingDiff( metaA, dataA, metaB, dataB );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_ADD_MONTHS: // Add B months to date field A
calcData[index] = ValueDataUtil.addMonths( metaA, dataA, metaB, dataB );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_CHECK_XML_FILE_WELL_FORMED: // Check if file A is well formed
calcData[index] = ValueDataUtil.isXMLFileWellFormed( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_CHECK_XML_WELL_FORMED: // Check if xml A is well formed
calcData[index] = ValueDataUtil.isXMLWellFormed( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_GET_FILE_ENCODING: // Get file encoding from a file A
calcData[index] = ValueDataUtil.getFileEncoding( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_DAMERAU_LEVENSHTEIN: // DAMERAULEVENSHTEIN DISTANCE
calcData[index] = ValueDataUtil.getDamerauLevenshtein_Distance( metaA, dataA, metaB, dataB );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_NEEDLEMAN_WUNSH: // NEEDLEMANWUNSH DISTANCE
calcData[index] = ValueDataUtil.getNeedlemanWunsch_Distance( metaA, dataA, metaB, dataB );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_JARO: // Jaro DISTANCE
calcData[index] = ValueDataUtil.getJaro_Similitude( metaA, dataA, metaB, dataB );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_JARO_WINKLER: // Jaro DISTANCE
calcData[index] = ValueDataUtil.getJaroWinkler_Similitude( metaA, dataA, metaB, dataB );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_SOUNDEX: // SOUNDEX
calcData[index] = ValueDataUtil.get_SoundEx( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_REFINED_SOUNDEX: // REFINEDSOUNDEX
calcData[index] = ValueDataUtil.get_RefinedSoundEx( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_DATE_DIFF_MSEC: // DateA - DateB (ms)
calcData[index] = ValueDataUtil.DateDiff( metaA, dataA, metaB, dataB, "ms" );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_DATE_DIFF_SEC: // DateA - DateB (s)
calcData[index] = ValueDataUtil.DateDiff( metaA, dataA, metaB, dataB, "s" );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_DATE_DIFF_MN: // DateA - DateB (mn)
calcData[index] = ValueDataUtil.DateDiff( metaA, dataA, metaB, dataB, "mn" );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_DATE_DIFF_HR: // DateA - DateB (h)
calcData[index] = ValueDataUtil.DateDiff( metaA, dataA, metaB, dataB, "h" );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_HOUR_OF_DAY:
calcData[index] = ValueDataUtil.hourOfDay( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_MINUTE_OF_HOUR:
calcData[index] = ValueDataUtil.minuteOfHour( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_SECOND_OF_MINUTE:
calcData[index] = ValueDataUtil.secondOfMinute( metaA, dataA );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_ADD_SECONDS: // Add B seconds to date field A
calcData[index] = ValueDataUtil.addSeconds( metaA, dataA, metaB, dataB );
resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
break;
case CalculatorMetaFunction.CALC_REMAINDER:
calcData[index] = ValueDataUtil.remainder( metaA, dataA, metaB, dataB );
break;
default:
throw new KettleValueException( BaseMessages.getString( PKG, "Calculator.Log.UnknownCalculationType" )
+ fn.getCalcType() );
}
// If we don't have a target data type, throw an error.
// Otherwise the result is non-deterministic.
//
if ( targetMeta.getType() == ValueMetaInterface.TYPE_NONE ) {
throw new KettleValueException( BaseMessages.getString( PKG, "Calculator.Log.NoType" )
+ ( i + 1 ) + " : " + fn.getFieldName() + " = " + fn.getCalcTypeDesc() + " / "
+ fn.getCalcTypeLongDesc() );
}
// Convert the data to the correct target data type.
//
if ( calcData[index] != null ) {
if ( targetMeta.getType() != resultType ) {
ValueMetaInterface resultMeta;
try {
// clone() is not necessary as one data instance belongs to one step instance and no race condition occurs
resultMeta = data.getValueMetaFor( resultType, "result" );
} catch ( Exception exception ) {
throw new KettleValueException( "Error creating value" );
}
resultMeta.setConversionMask( fn.getConversionMask() );
resultMeta.setGroupingSymbol( fn.getGroupingSymbol() );
resultMeta.setDecimalSymbol( fn.getDecimalSymbol() );
resultMeta.setCurrencySymbol( fn.getCurrencySymbol() );
try {
calcData[index] = targetMeta.convertData( resultMeta, calcData[index] );
} catch ( Exception ex ) {
throw new KettleValueException( "resultType: "
+ resultType + "; targetMeta: " + targetMeta.getType(), ex );
}
}
}
}
}
// OK, now we should refrain from adding the temporary fields to the result.
// So we remove them.
//
return RowDataUtil.removeItems( calcData, data.getTempIndexes() );
}
@Override
public boolean init( StepMetaInterface smi, StepDataInterface sdi ) {
meta = (CalculatorMeta) smi;
data = (CalculatorData) sdi;
return super.init( smi, sdi );
}
}