/*! ****************************************************************************** * * 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.fuzzymatch; import java.util.List; import org.pentaho.di.core.CheckResult; import org.pentaho.di.core.CheckResultInterface; import org.pentaho.di.core.Const; import org.pentaho.di.core.util.Utils; import org.pentaho.di.core.database.DatabaseMeta; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.exception.KettleStepException; import org.pentaho.di.core.exception.KettleXMLException; import org.pentaho.di.core.row.RowMetaInterface; import org.pentaho.di.core.row.ValueMetaInterface; import org.pentaho.di.core.row.value.ValueMetaInteger; import org.pentaho.di.core.row.value.ValueMetaNumber; import org.pentaho.di.core.row.value.ValueMetaString; import org.pentaho.di.core.variables.VariableSpace; import org.pentaho.di.core.xml.XMLHandler; import org.pentaho.di.i18n.BaseMessages; import org.pentaho.di.repository.ObjectId; import org.pentaho.di.repository.Repository; import org.pentaho.di.shared.SharedObjectInterface; import org.pentaho.di.trans.Trans; import org.pentaho.di.trans.TransMeta; import org.pentaho.di.trans.step.BaseStepMeta; import org.pentaho.di.trans.step.StepDataInterface; import org.pentaho.di.trans.step.StepIOMeta; import org.pentaho.di.trans.step.StepIOMetaInterface; import org.pentaho.di.trans.step.StepInterface; import org.pentaho.di.trans.step.StepMeta; import org.pentaho.di.trans.step.StepMetaInterface; import org.pentaho.di.trans.step.errorhandling.Stream; import org.pentaho.di.trans.step.errorhandling.StreamIcon; import org.pentaho.di.trans.step.errorhandling.StreamInterface; import org.pentaho.di.trans.step.errorhandling.StreamInterface.StreamType; import org.pentaho.metastore.api.IMetaStore; import org.w3c.dom.Node; public class FuzzyMatchMeta extends BaseStepMeta implements StepMetaInterface { private static Class<?> PKG = FuzzyMatchMeta.class; // for i18n purposes, needed by Translator2!! public static final String DEFAULT_SEPARATOR = ","; /** Algorithms type */ private int algorithm; /** * The algorithms description */ public static final String[] algorithmDesc = { BaseMessages.getString( PKG, "FuzzyMatchMeta.algorithm.Levenshtein" ), BaseMessages.getString( PKG, "FuzzyMatchMeta.algorithm.DamerauLevenshtein" ), BaseMessages.getString( PKG, "FuzzyMatchMeta.algorithm.NeedlemanWunsch" ), BaseMessages.getString( PKG, "FuzzyMatchMeta.algorithm.Jaro" ), BaseMessages.getString( PKG, "FuzzyMatchMeta.algorithm.JaroWinkler" ), BaseMessages.getString( PKG, "FuzzyMatchMeta.algorithm.PairSimilarity" ), BaseMessages.getString( PKG, "FuzzyMatchMeta.algorithm.Metaphone" ), BaseMessages.getString( PKG, "FuzzyMatchMeta.algorithm.DoubleMetaphone" ), BaseMessages.getString( PKG, "FuzzyMatchMeta.algorithm.SoundEx" ), BaseMessages.getString( PKG, "FuzzyMatchMeta.algorithm.RefinedSoundEx" ) }; /** * The algorithms type codes */ public static final String[] algorithmCode = { "levenshtein", "dameraulevenshtein", "needlemanwunsch", "jaro", "jarowinkler", "pairsimilarity", "metaphone", "doublemataphone", "soundex", "refinedsoundex" }; public static final int OPERATION_TYPE_LEVENSHTEIN = 0; public static final int OPERATION_TYPE_DAMERAU_LEVENSHTEIN = 1; public static final int OPERATION_TYPE_NEEDLEMAN_WUNSH = 2; public static final int OPERATION_TYPE_JARO = 3; public static final int OPERATION_TYPE_JARO_WINKLER = 4; public static final int OPERATION_TYPE_PAIR_SIMILARITY = 5; public static final int OPERATION_TYPE_METAPHONE = 6; public static final int OPERATION_TYPE_DOUBLE_METAPHONE = 7; public static final int OPERATION_TYPE_SOUNDEX = 8; public static final int OPERATION_TYPE_REFINED_SOUNDEX = 9; /** field in lookup stream with which we look up values */ private String lookupfield; /** field in input stream for which we lookup values */ private String mainstreamfield; /** output match fieldname **/ private String outputmatchfield; /** ouput value fieldname **/ private String outputvaluefield; /** case sensitive **/ private boolean caseSensitive; /** minimal value, distance for levenshtein, similarity, ... **/ private String minimalValue; /** maximal value, distance for levenshtein, similarity, ... **/ private String maximalValue; /** values separator ... **/ private String separator; /** get closer matching value **/ private boolean closervalue; /** return these field values from lookup */ private String[] value; /** rename to this after lookup */ private String[] valueName; public FuzzyMatchMeta() { super(); // allocate BaseStepMeta } /** * @return Returns the value. */ public String[] getValue() { return value; } /** * @param value * The value to set. */ public void setValue( String[] value ) { this.value = value; } public void allocate( int nrvalues ) { value = new String[nrvalues]; valueName = new String[nrvalues]; } public Object clone() { FuzzyMatchMeta retval = (FuzzyMatchMeta) super.clone(); int nrvalues = value.length; retval.allocate( nrvalues ); System.arraycopy( value, 0, retval.value, 0, nrvalues ); System.arraycopy( valueName, 0, retval.valueName, 0, nrvalues ); return retval; } /** * @return Returns the mainstreamfield. */ public String getMainStreamField() { return mainstreamfield; } /** * @param mainstreamfield * The mainstreamfield to set. */ public void setMainStreamField( String mainstreamfield ) { this.mainstreamfield = mainstreamfield; } /** * @return Returns the lookupfield. */ public String getLookupField() { return lookupfield; } /** * @param lookupfield * The lookupfield to set. */ public void setLookupField( String lookupfield ) { this.lookupfield = lookupfield; } /** * @return Returns the outputmatchfield. */ public String getOutputMatchField() { return outputmatchfield; } /** * @param outputmatchfield * The outputmatchfield to set. */ public void setOutputMatchField( String outputmatchfield ) { this.outputmatchfield = outputmatchfield; } /** * @return Returns the outputmatchfield. */ public String getOutputValueField() { return outputvaluefield; } /** * @param outputvaluefield * The outputvaluefield to set. */ public void setOutputValueField( String outputvaluefield ) { this.outputvaluefield = outputvaluefield; } /** * @return Returns the closervalue. */ public boolean isGetCloserValue() { return closervalue; } /** * @return Returns the valueName. */ public String[] getValueName() { return valueName; } /** * @param valueName * The valueName to set. */ public void setValueName( String[] valueName ) { this.valueName = valueName; } /** * @param closervalue * The closervalue to set. */ public void setGetCloserValue( boolean closervalue ) { this.closervalue = closervalue; } /** * @return Returns the caseSensitive. */ public boolean isCaseSensitive() { return caseSensitive; } /** * @param caseSensitive * The caseSensitive to set. */ public void setCaseSensitive( boolean caseSensitive ) { this.caseSensitive = caseSensitive; } /** * @return Returns the minimalValue. */ public String getMinimalValue() { return minimalValue; } /** * @param minimalValue * The minimalValue to set. */ public void setMinimalValue( String minimalValue ) { this.minimalValue = minimalValue; } /** * @return Returns the minimalValue. */ public String getMaximalValue() { return maximalValue; } /** * @param maximalValue * The maximalValue to set. */ public void setMaximalValue( String maximalValue ) { this.maximalValue = maximalValue; } /** * @return Returns the separator. */ public String getSeparator() { return separator; } /** * @param separator * The separator to set. */ public void setSeparator( String separator ) { this.separator = separator; } public void loadXML( Node stepnode, List<DatabaseMeta> databases, IMetaStore metaStore ) throws KettleXMLException { readData( stepnode, databases ); } public int getAlgorithmType() { return algorithm; } public void setAlgorithmType( int algorithm ) { this.algorithm = algorithm; } public static String getAlgorithmTypeDesc( int i ) { if ( i < 0 || i >= algorithmDesc.length ) { return algorithmDesc[0]; } return algorithmDesc[i]; } public static int getAlgorithmTypeByDesc( String tt ) { if ( tt == null ) { return 0; } for ( int i = 0; i < algorithmDesc.length; i++ ) { if ( algorithmDesc[i].equalsIgnoreCase( tt ) ) { return i; } } // If this fails, try to match using the code. return getAlgorithmTypeByCode( tt ); } private static int getAlgorithmTypeByCode( String tt ) { if ( tt == null ) { return 0; } for ( int i = 0; i < algorithmCode.length; i++ ) { if ( algorithmCode[i].equalsIgnoreCase( tt ) ) { return i; } } return 0; } private void readData( Node stepnode, List<? extends SharedObjectInterface> databases ) throws KettleXMLException { try { String lookupFromStepname = XMLHandler.getTagValue( stepnode, "from" ); StreamInterface infoStream = getStepIOMeta().getInfoStreams().get( 0 ); infoStream.setSubject( lookupFromStepname ); lookupfield = XMLHandler.getTagValue( stepnode, "lookupfield" ); mainstreamfield = XMLHandler.getTagValue( stepnode, "mainstreamfield" ); caseSensitive = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "caseSensitive" ) ); closervalue = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "closervalue" ) ); minimalValue = XMLHandler.getTagValue( stepnode, "minimalValue" ); maximalValue = XMLHandler.getTagValue( stepnode, "maximalValue" ); separator = XMLHandler.getTagValue( stepnode, "separator" ); outputmatchfield = XMLHandler.getTagValue( stepnode, "outputmatchfield" ); outputvaluefield = XMLHandler.getTagValue( stepnode, "outputvaluefield" ); algorithm = getAlgorithmTypeByCode( Const.NVL( XMLHandler.getTagValue( stepnode, "algorithm" ), "" ) ); Node lookup = XMLHandler.getSubNode( stepnode, "lookup" ); int nrvalues = XMLHandler.countNodes( lookup, "value" ); allocate( nrvalues ); for ( int i = 0; i < nrvalues; i++ ) { Node vnode = XMLHandler.getSubNodeByNr( lookup, "value", i ); value[i] = XMLHandler.getTagValue( vnode, "name" ); valueName[i] = XMLHandler.getTagValue( vnode, "rename" ); if ( valueName[i] == null ) { valueName[i] = value[i]; // default: same name to return! } } } catch ( Exception e ) { throw new KettleXMLException( BaseMessages.getString( PKG, "FuzzyMatchMeta.Exception.UnableToLoadStepInfoFromXML" ), e ); } } private static String getAlgorithmTypeCode( int i ) { if ( i < 0 || i >= algorithmCode.length ) { return algorithmCode[0]; } return algorithmCode[i]; } public void setDefault() { value = null; valueName = null; separator = DEFAULT_SEPARATOR; closervalue = true; minimalValue = "0"; maximalValue = "1"; caseSensitive = false; lookupfield = null; mainstreamfield = null; outputmatchfield = BaseMessages.getString( PKG, "FuzzyMatchMeta.OutputMatchFieldname" ); outputvaluefield = BaseMessages.getString( PKG, "FuzzyMatchMeta.OutputValueFieldname" ); int nrvalues = 0; allocate( nrvalues ); for ( int i = 0; i < nrvalues; i++ ) { value[i] = "value" + i; valueName[i] = "valuename" + i; } } public void getFields( RowMetaInterface inputRowMeta, String name, RowMetaInterface[] info, StepMeta nextStep, VariableSpace space, Repository repository, IMetaStore metaStore ) throws KettleStepException { // Add match field ValueMetaInterface v = new ValueMetaString( space.environmentSubstitute( getOutputMatchField() ) ); v.setOrigin( name ); v.setStorageType( ValueMetaInterface.STORAGE_TYPE_NORMAL ); inputRowMeta.addValueMeta( v ); String mainField = space.environmentSubstitute( getOutputValueField() ); if ( !Utils.isEmpty( mainField ) && isGetCloserValue() ) { switch ( getAlgorithmType() ) { case FuzzyMatchMeta.OPERATION_TYPE_DAMERAU_LEVENSHTEIN: case FuzzyMatchMeta.OPERATION_TYPE_LEVENSHTEIN: v = new ValueMetaInteger( mainField ); v.setLength( ValueMetaInterface.DEFAULT_INTEGER_LENGTH ); break; case FuzzyMatchMeta.OPERATION_TYPE_JARO: case FuzzyMatchMeta.OPERATION_TYPE_JARO_WINKLER: case FuzzyMatchMeta.OPERATION_TYPE_PAIR_SIMILARITY: v = new ValueMetaNumber( mainField ); break; default: // Phonetic algorithms v = new ValueMetaString( mainField ); break; } v.setStorageType( ValueMetaInterface.STORAGE_TYPE_NORMAL ); v.setOrigin( name ); inputRowMeta.addValueMeta( v ); } boolean activateAdditionalFields = isGetCloserValue() || ( getAlgorithmType() == FuzzyMatchMeta.OPERATION_TYPE_DOUBLE_METAPHONE ) || ( getAlgorithmType() == FuzzyMatchMeta.OPERATION_TYPE_SOUNDEX ) || ( getAlgorithmType() == FuzzyMatchMeta.OPERATION_TYPE_REFINED_SOUNDEX ) || ( getAlgorithmType() == FuzzyMatchMeta.OPERATION_TYPE_METAPHONE ); if ( activateAdditionalFields ) { if ( info != null && info.length == 1 && info[0] != null ) { for ( int i = 0; i < valueName.length; i++ ) { v = info[0].searchValueMeta( value[i] ); if ( v != null ) { // Configuration error/missing resources... v.setName( valueName[i] ); v.setOrigin( name ); v.setStorageType( ValueMetaInterface.STORAGE_TYPE_NORMAL ); // Only normal storage goes into the cache inputRowMeta.addValueMeta( v ); } else { throw new KettleStepException( BaseMessages.getString( PKG, "FuzzyMatchMeta.Exception.ReturnValueCanNotBeFound", value[i] ) ); } } } else { for ( int i = 0; i < valueName.length; i++ ) { v = new ValueMetaString( valueName[i] ); v.setOrigin( name ); inputRowMeta.addValueMeta( v ); } } } } public String getXML() { StringBuilder retval = new StringBuilder(); StreamInterface infoStream = getStepIOMeta().getInfoStreams().get( 0 ); retval.append( " " + XMLHandler.addTagValue( "from", infoStream.getStepname() ) ); retval.append( " " + XMLHandler.addTagValue( "lookupfield", lookupfield ) ); retval.append( " " + XMLHandler.addTagValue( "mainstreamfield", mainstreamfield ) ); retval.append( " " + XMLHandler.addTagValue( "outputmatchfield", outputmatchfield ) ); retval.append( " " + XMLHandler.addTagValue( "outputvaluefield", outputvaluefield ) ); retval.append( " " + XMLHandler.addTagValue( "caseSensitive", caseSensitive ) ); retval.append( " " + XMLHandler.addTagValue( "closervalue", closervalue ) ); retval.append( " " + XMLHandler.addTagValue( "minimalValue", minimalValue ) ); retval.append( " " + XMLHandler.addTagValue( "maximalValue", maximalValue ) ); retval.append( " " + XMLHandler.addTagValue( "separator", separator ) ); retval.append( " " ).append( XMLHandler.addTagValue( "algorithm", getAlgorithmTypeCode( algorithm ) ) ); retval.append( " <lookup>" + Const.CR ); for ( int i = 0; i < value.length; i++ ) { retval.append( " <value>" + Const.CR ); retval.append( " " + XMLHandler.addTagValue( "name", value[i] ) ); retval.append( " " + XMLHandler.addTagValue( "rename", valueName[i] ) ); retval.append( " </value>" + Const.CR ); } retval.append( " </lookup>" + Const.CR ); return retval.toString(); } public void readRep( Repository rep, IMetaStore metaStore, ObjectId id_step, List<DatabaseMeta> databases ) throws KettleException { try { String lookupFromStepname = rep.getStepAttributeString( id_step, "lookup_from_step" ); StreamInterface infoStream = getStepIOMeta().getInfoStreams().get( 0 ); infoStream.setSubject( lookupFromStepname ); lookupfield = rep.getStepAttributeString( id_step, "lookupfield" ); mainstreamfield = rep.getStepAttributeString( id_step, "mainstreamfield" ); outputmatchfield = rep.getStepAttributeString( id_step, "outputmatchfield" ); outputvaluefield = rep.getStepAttributeString( id_step, "outputvaluefield" ); caseSensitive = rep.getStepAttributeBoolean( id_step, "caseSensitive" ); closervalue = rep.getStepAttributeBoolean( id_step, "closervalue" ); minimalValue = rep.getStepAttributeString( id_step, "minimalValue" ); maximalValue = rep.getStepAttributeString( id_step, "maximalValue" ); separator = rep.getStepAttributeString( id_step, "separator" ); algorithm = getAlgorithmTypeByCode( Const.NVL( rep.getStepAttributeString( id_step, "algorithm" ), "" ) ); int nrvalues = rep.countNrStepAttributes( id_step, "return_value_name" ); allocate( nrvalues ); for ( int i = 0; i < nrvalues; i++ ) { value[i] = rep.getStepAttributeString( id_step, i, "return_value_name" ); valueName[i] = rep.getStepAttributeString( id_step, i, "return_value_rename" ); } } catch ( Exception e ) { throw new KettleException( BaseMessages.getString( PKG, "FuzzyMatchMeta.Exception.UnexpecteErrorReadingStepInfoFromRepository" ), e ); } } public void saveRep( Repository rep, IMetaStore metaStore, ObjectId id_transformation, ObjectId id_step ) throws KettleException { try { StreamInterface infoStream = getStepIOMeta().getInfoStreams().get( 0 ); rep.saveStepAttribute( id_transformation, id_step, "lookup_from_step", infoStream.getStepname() ); rep.saveStepAttribute( id_transformation, id_step, "lookupfield", lookupfield ); rep.saveStepAttribute( id_transformation, id_step, "mainstreamfield", mainstreamfield ); rep.saveStepAttribute( id_transformation, id_step, "outputmatchfield", outputmatchfield ); rep.saveStepAttribute( id_transformation, id_step, "outputvaluefield", outputvaluefield ); rep.saveStepAttribute( id_transformation, id_step, "caseSensitive", caseSensitive ); rep.saveStepAttribute( id_transformation, id_step, "closervalue", closervalue ); rep.saveStepAttribute( id_transformation, id_step, "minimalValue", minimalValue ); rep.saveStepAttribute( id_transformation, id_step, "maximalValue", maximalValue ); rep.saveStepAttribute( id_transformation, id_step, "separator", separator ); rep.saveStepAttribute( id_transformation, id_step, "algorithm", getAlgorithmTypeCode( algorithm ) ); for ( int i = 0; i < value.length; i++ ) { rep.saveStepAttribute( id_transformation, id_step, i, "return_value_name", value[i] ); rep.saveStepAttribute( id_transformation, id_step, i, "return_value_rename", valueName[i] ); } } catch ( Exception e ) { throw new KettleException( BaseMessages.getString( PKG, "FuzzyMatchMeta.Exception.UnableToSaveStepInfoToRepository" ) + id_step, e ); } } public void check( List<CheckResultInterface> remarks, TransMeta transMeta, StepMeta stepMeta, RowMetaInterface prev, String[] input, String[] output, RowMetaInterface info, VariableSpace space, Repository repository, IMetaStore metaStore ) { CheckResult cr; if ( prev != null && prev.size() > 0 ) { cr = new CheckResult( CheckResult.TYPE_RESULT_OK, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.StepReceivingFields", prev.size() + "" ), stepMeta ); remarks.add( cr ); // Starting from selected fields in ... // Check the fields from the previous stream! String mainField = transMeta.environmentSubstitute( getMainStreamField() ); int idx = prev.indexOfValue( mainField ); if ( idx < 0 ) { cr = new CheckResult( CheckResult.TYPE_RESULT_ERROR, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.MainFieldNotFound", mainField ), stepMeta ); } else { cr = new CheckResult( CheckResult.TYPE_RESULT_OK, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.MainFieldFound", mainField ), stepMeta ); } remarks.add( cr ); } else { cr = new CheckResult( CheckResult.TYPE_RESULT_ERROR, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.CouldNotFindFieldsFromPreviousSteps" ), stepMeta ); remarks.add( cr ); } if ( info != null && info.size() > 0 ) { cr = new CheckResult( CheckResult.TYPE_RESULT_OK, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.StepReceivingLookupData", info.size() + "" ), stepMeta ); remarks.add( cr ); // Check the fields from the lookup stream! String lookupField = transMeta.environmentSubstitute( getLookupField() ); int idx = info.indexOfValue( lookupField ); if ( idx < 0 ) { cr = new CheckResult( CheckResult.TYPE_RESULT_ERROR, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.FieldNotFoundInLookupStream", lookupField ), stepMeta ); } else { cr = new CheckResult( CheckResult.TYPE_RESULT_OK, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.FieldFoundInTheLookupStream", lookupField ), stepMeta ); } remarks.add( cr ); String error_message = ""; boolean error_found = false; // Check the values to retrieve from the lookup stream! for ( int i = 0; i < value.length; i++ ) { idx = info.indexOfValue( value[i] ); if ( idx < 0 ) { error_message += "\t\t" + value[i] + Const.CR; error_found = true; } } if ( error_found ) { error_message = BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.FieldsNotFoundInLookupStream2" ) + Const.CR + Const.CR + error_message; cr = new CheckResult( CheckResultInterface.TYPE_RESULT_ERROR, error_message, stepMeta ); } else { cr = new CheckResult( CheckResultInterface.TYPE_RESULT_OK, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.AllFieldsFoundInTheLookupStream2" ), stepMeta ); } remarks.add( cr ); } else { cr = new CheckResult( CheckResult.TYPE_RESULT_ERROR, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.FieldsNotFoundFromInLookupSep" ), stepMeta ); remarks.add( cr ); } // See if the source step is filled in! StreamInterface infoStream = getStepIOMeta().getInfoStreams().get( 0 ); if ( infoStream.getStepMeta() == null ) { cr = new CheckResult( CheckResult.TYPE_RESULT_ERROR, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.SourceStepNotSelected" ), stepMeta ); remarks.add( cr ); } else { cr = new CheckResult( CheckResult.TYPE_RESULT_OK, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.SourceStepIsSelected" ), stepMeta ); remarks.add( cr ); // See if the step exists! // if ( info != null ) { cr = new CheckResult( CheckResult.TYPE_RESULT_OK, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.SourceStepExist", infoStream.getStepname() + "" ), stepMeta ); remarks.add( cr ); } else { cr = new CheckResult( CheckResult.TYPE_RESULT_ERROR, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.SourceStepDoesNotExist", infoStream.getStepname() + "" ), stepMeta ); remarks.add( cr ); } } // See if we have input streams leading to this step! if ( input.length >= 2 ) { cr = new CheckResult( CheckResult.TYPE_RESULT_OK, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.StepReceivingInfoFromInputSteps", input.length + "" ), stepMeta ); remarks.add( cr ); } else { cr = new CheckResult( CheckResult.TYPE_RESULT_ERROR, BaseMessages.getString( PKG, "FuzzyMatchMeta.CheckResult.NeedAtLeast2InputStreams", Const.CR, Const.CR ), stepMeta ); remarks.add( cr ); } } @Override public void searchInfoAndTargetSteps( List<StepMeta> steps ) { for ( StreamInterface stream : getStepIOMeta().getInfoStreams() ) { stream.setStepMeta( StepMeta.findStep( steps, (String) stream.getSubject() ) ); } } public StepInterface getStep( StepMeta stepMeta, StepDataInterface stepDataInterface, int cnr, TransMeta transMeta, Trans trans ) { return new FuzzyMatch( stepMeta, stepDataInterface, cnr, transMeta, trans ); } public StepDataInterface getStepData() { return new FuzzyMatchData(); } public boolean excludeFromRowLayoutVerification() { return true; } public boolean supportsErrorHandling() { return true; } /** * Returns the Input/Output metadata for this step. The generator step only produces output, does not accept input! */ public StepIOMetaInterface getStepIOMeta() { if ( ioMeta == null ) { ioMeta = new StepIOMeta( true, true, false, false, false, false ); StreamInterface stream = new Stream( StreamType.INFO, null, BaseMessages.getString( PKG, "FuzzyMatchMeta.InfoStream.Description" ), StreamIcon.INFO, null ); ioMeta.addStream( stream ); } return ioMeta; } public void resetStepIoMeta() { // Do nothing, don't reset as there is no need to do this. } }