class FuzzyMatch extends BaseTransform<FuzzyMatchMeta, FuzzyMatchData> implements ITransform<FuzzyMatchMeta, FuzzyMatchData> {

  private boolean readLookupValues() throws HopException {
    data.infoStream = meta.getTransformIOMeta().getInfoStreams().get( 0 );
    if ( data.infoStream.getTransformMeta() == null ) {
      logError( BaseMessages.getString( PKG, "FuzzyMatch.Log.NoLookupTransformSpecified" ) );
      return false;
    }

    if ( isDetailed() ) {
      logDetailed( BaseMessages.getString( PKG, "FuzzyMatch.Log.ReadingFromStream" )
        + data.infoStream.getTransformName() + "]" );
    }

    boolean firstRun = true;
    // Which row set do we read from?
    //
    IRowSet rowSet = findInputRowSet( data.infoStream.getTransformName() );
    Object[] rowData = getRowFrom( rowSet ); // rows are originating from "lookup_from"

    while ( rowData != null ) {
      if ( firstRun ) {
        data.infoMeta = rowSet.getRowMeta().clone();
        // Check lookup field
        int indexOfLookupField = data.infoMeta.indexOfValue( environmentSubstitute( meta.getLookupField() ) );
        if ( indexOfLookupField < 0 ) {
          // The field is unreachable !
          throw new HopException( BaseMessages.getString(
            PKG, "FuzzyMatch.Exception.CouldnotFindLookField", meta.getLookupField() ) );
        }
        data.infoCache = new RowMeta();
        IValueMeta keyValueMeta = data.infoMeta.getValueMeta( indexOfLookupField );
        keyValueMeta.setStorageType( IValueMeta.STORAGE_TYPE_NORMAL );
        data.infoCache.addValueMeta( keyValueMeta );
        // Add key
        data.indexOfCachedFields[ 0 ] = indexOfLookupField;

        // Check additional fields
        if ( data.addAdditionalFields ) {
          IValueMeta additionalFieldValueMeta;
          for ( int i = 0; i < meta.getValue().length; i++ ) {
            int fi = i + 1;
            data.indexOfCachedFields[ fi ] = data.infoMeta.indexOfValue( meta.getValue()[ i ] );
            if ( data.indexOfCachedFields[ fi ] < 0 ) {
              // The field is unreachable !
              throw new HopException( BaseMessages.getString(
                PKG, "FuzzyMatch.Exception.CouldnotFindLookField", meta.getValue()[ i ] ) );
            }
            additionalFieldValueMeta = data.infoMeta.getValueMeta( data.indexOfCachedFields[ fi ] );
            additionalFieldValueMeta.setStorageType( IValueMeta.STORAGE_TYPE_NORMAL );
            data.infoCache.addValueMeta( additionalFieldValueMeta );
          }
          data.nrCachedFields += meta.getValue().length;
        }
      }
      if ( log.isRowLevel() ) {
        logRowlevel( BaseMessages.getString( PKG, "FuzzyMatch.Log.ReadLookupRow" )
          + rowSet.getRowMeta().getString( rowData ) );
      }

      // Look up the keys in the source rows
      // and store values in cache

      Object[] storeData = new Object[ data.nrCachedFields ];
      // Add key field
      if ( rowData[ data.indexOfCachedFields[ 0 ] ] == null ) {
        storeData[ 0 ] = "";
      } else {
        IValueMeta fromStreamRowMeta = rowSet.getRowMeta().getValueMeta( data.indexOfCachedFields[ 0 ] );
        if ( fromStreamRowMeta.isStorageBinaryString() ) {
          storeData[ 0 ] = fromStreamRowMeta.convertToNormalStorageType( rowData[ data.indexOfCachedFields[ 0 ] ] );
        } else {
          storeData[ 0 ] = rowData[ data.indexOfCachedFields[ 0 ] ];
        }
      }

      // Add additional fields?
      for ( int i = 1; i < data.nrCachedFields; i++ ) {
        IValueMeta fromStreamRowMeta = rowSet.getRowMeta().getValueMeta( data.indexOfCachedFields[ i ] );
        if ( fromStreamRowMeta.isStorageBinaryString() ) {
          storeData[ i ] = fromStreamRowMeta.convertToNormalStorageType( rowData[ data.indexOfCachedFields[ i ] ] );
        } else {
          storeData[ i ] = rowData[ data.indexOfCachedFields[ i ] ];
        }
      }
      if ( isDebug() ) {
        logDebug( BaseMessages.getString( PKG, "FuzzyMatch.Log.AddingValueToCache", data.infoCache
          .getString( storeData ) ) );
      }

      addToCache( storeData );

      rowData = getRowFrom( rowSet );

      if ( firstRun ) {
        firstRun = false;
      }
    }

    return true;
  }

  public  FuzzyMatch( TransformMeta transformMeta, FuzzyMatchMeta meta, FuzzyMatchData data, int copyNr, PipelineMeta pipelineMeta,
                     Pipeline pipeline );

}