public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException {
    if (data.linesWritten >= meta.getDataLines().size()) // no more rows to be written
    {
      setOutputDone();
      return false;
    }

    if (first) {
      // The output meta is the original input meta + the
      // additional constant fields.

      first = false;
      data.linesWritten = 0;

      data.outputRowMeta = new RowMeta();
      meta.getFields(data.outputRowMeta, getStepname(), null, null, this);

      // Use these metadata values to convert data...
      //
      data.convertMeta = data.outputRowMeta.clone();
      for (ValueMetaInterface valueMeta : data.convertMeta.getValueMetaList()) {
        valueMeta.setType(ValueMetaInterface.TYPE_STRING);
      }
    }

    Object[] outputRowData = RowDataUtil.allocateRowData(data.outputRowMeta.size());
    List<String> outputLine = meta.getDataLines().get(data.linesWritten);

    for (int i = 0; i < data.outputRowMeta.size(); i++) {
      ValueMetaInterface valueMeta = data.outputRowMeta.getValueMeta(i);
      ValueMetaInterface convertMeta = data.convertMeta.getValueMeta(i);
      String valueData = outputLine.get(i);

      outputRowData[i] = valueMeta.convertDataFromString(valueData, convertMeta, null, null, 0);
    }

    putRow(data.outputRowMeta, outputRowData);
    data.linesWritten++;

    if (log.isRowLevel()) {
      log.logRowlevel(
          toString(),
          BaseMessages.getString(
              PKG,
              "DataGrid.Log.Wrote.Row",
              Long.toString(getLinesWritten()),
              data.outputRowMeta.getString(outputRowData)));
    }

    if (checkFeedback(getLinesWritten())) {
      if (log.isBasic())
        logBasic(
            BaseMessages.getString(PKG, "DataGrid.Log.LineNr", Long.toString(getLinesWritten())));
    }

    return true;
  }
  private Object[] splitField(Object[] r) throws KettleException {
    if (first) {
      first = false;
      // get the RowMeta
      data.previousMeta = getInputRowMeta().clone();

      // search field
      data.fieldnr = data.previousMeta.indexOfValue(meta.getSplitField());
      if (data.fieldnr < 0) {
        throw new KettleValueException(
            BaseMessages.getString(
                PKG, "FieldSplitter.Log.CouldNotFindFieldToSplit", meta.getSplitField()));
      }

      // only String type allowed
      if (!data.previousMeta.getValueMeta(data.fieldnr).isString()) {
        throw new KettleValueException(
            (BaseMessages.getString(
                PKG, "FieldSplitter.Log.SplitFieldNotValid", meta.getSplitField())));
      }

      // prepare the outputMeta
      //
      data.outputMeta = getInputRowMeta().clone();
      meta.getFields(data.outputMeta, getStepname(), null, null, this, repository, metaStore);

      // Now create objects to do string to data type conversion...
      //
      data.conversionMeta = data.outputMeta.cloneToType(ValueMetaInterface.TYPE_STRING);

      data.delimiter = environmentSubstitute(meta.getDelimiter());
      data.enclosure = environmentSubstitute(meta.getEnclosure());
    }

    // reserve room
    Object[] outputRow = RowDataUtil.allocateRowData(data.outputMeta.size());

    int nrExtraFields = meta.getFieldID().length - 1;

    System.arraycopy(r, 0, outputRow, 0, data.fieldnr);
    System.arraycopy(
        r,
        data.fieldnr + 1,
        outputRow,
        data.fieldnr + 1 + nrExtraFields,
        data.previousMeta.size() - (data.fieldnr + 1));

    // OK, now we have room in the middle to place the fields...
    //

    // Named values info.id[0] not filled in!
    final boolean selectFieldById =
        (meta.getFieldID().length > 0)
            && (meta.getFieldID()[0] != null)
            && (meta.getFieldID()[0].length() > 0);

    if (log.isDebug()) {
      if (selectFieldById) {
        logDebug(BaseMessages.getString(PKG, "FieldSplitter.Log.UsingIds"));
      } else {
        logDebug(BaseMessages.getString(PKG, "FieldSplitter.Log.UsingPositionOfValue"));
      }
    }

    String valueToSplit = data.previousMeta.getString(r, data.fieldnr);
    String[] valueParts = Const.splitString(valueToSplit, data.delimiter, data.enclosure);
    int prev = 0;
    for (int i = 0; i < meta.getFieldName().length; i++) {
      String rawValue = null;
      if (selectFieldById) {
        for (String part : valueParts) {
          if (part.startsWith(meta.getFieldID()[i])) {
            // Optionally remove the id
            if (meta.getFieldRemoveID()[i]) {
              rawValue = part.substring(meta.getFieldID()[i].length());
            } else {
              rawValue = part;
            }

            break;
          }
        }

        if (log.isDebug()) {
          logDebug(BaseMessages.getString(PKG, "FieldSplitter.Log.SplitInfo") + rawValue);
        }
      } else {
        rawValue = (valueParts == null || i >= valueParts.length) ? null : valueParts[i];
        prev += (rawValue == null ? 0 : rawValue.length()) + data.delimiter.length();

        if (log.isDebug()) {
          logDebug(
              BaseMessages.getString(
                  PKG, "FieldSplitter.Log.SplitFieldsInfo", rawValue, String.valueOf(prev)));
        }
      }

      Object value;
      try {
        ValueMetaInterface valueMeta = data.outputMeta.getValueMeta(data.fieldnr + i);
        ValueMetaInterface conversionValueMeta = data.conversionMeta.getValueMeta(data.fieldnr + i);

        if (rawValue != null && valueMeta.isNull(rawValue)) {
          rawValue = null;
        }
        value =
            valueMeta.convertDataFromString(
                rawValue,
                conversionValueMeta,
                meta.getFieldNullIf()[i],
                meta.getFieldIfNull()[i],
                meta.getFieldTrimType()[i]);
      } catch (Exception e) {
        throw new KettleValueException(
            BaseMessages.getString(
                PKG,
                "FieldSplitter.Log.ErrorConvertingSplitValue",
                rawValue,
                meta.getSplitField() + "]!"),
            e);
      }
      outputRow[data.fieldnr + i] = value;
    }

    return outputRow;
  }