public synchronized boolean processRow(StepMetaInterface smi, StepDataInterface sdi)
      throws KettleException {
    meta = (TextFileOutputMeta) smi;
    data = (TextFileOutputData) sdi;

    boolean result = true;
    boolean bEndedLineWrote = false;
    Object[] r = getRow(); // This also waits for a row to be finished.

    if (r != null && first) {
      first = false;
      data.outputRowMeta = getInputRowMeta().clone();
      meta.getFields(data.outputRowMeta, getStepname(), null, null, this);

      // if file name in field is enabled then set field name and open file
      //
      if (meta.isFileNameInField()) {

        // find and set index of file name field in input stream
        //
        data.fileNameFieldIndex = getInputRowMeta().indexOfValue(meta.getFileNameField());

        // set the file name for this row
        //
        if (data.fileNameFieldIndex < 0) {
          throw new KettleStepException(
              Messages.getString(
                  "TextFileOutput.Exception.FileNameFieldNotFound",
                  meta.getFileNameField())); // $NON-NLS-1$
        }

        data.fileNameMeta = getInputRowMeta().getValueMeta(data.fileNameFieldIndex);
        data.fileName = data.fileNameMeta.getString(r[data.fileNameFieldIndex]);
        setDataWriterForFilename(data.fileName);
      } else if (meta.isDoNotOpenNewFileInit() && !meta.isFileNameInField()) {
        // Open a new file here
        //
        openNewFile(meta.getFileName());
        data.oneFileOpened = true;
        initBinaryDataFields();
      }

      if (!meta.isFileAppended()
          && (meta.isHeaderEnabled()
              || meta.isFooterEnabled())) // See if we have to write a header-line)
      {
        if (!meta.isFileNameInField() && meta.isHeaderEnabled() && data.outputRowMeta != null) {
          writeHeader();
        }
      }

      data.fieldnrs = new int[meta.getOutputFields().length];
      for (int i = 0; i < meta.getOutputFields().length; i++) {
        data.fieldnrs[i] = data.outputRowMeta.indexOfValue(meta.getOutputFields()[i].getName());
        if (data.fieldnrs[i] < 0) {
          throw new KettleStepException(
              "Field ["
                  + meta.getOutputFields()[i].getName()
                  + "] couldn't be found in the input stream!");
        }
      }
    }

    if ((r == null && data.outputRowMeta != null && meta.isFooterEnabled())
        || (r != null
            && getLinesOutput() > 0
            && meta.getSplitEvery() > 0
            && ((getLinesOutput() + 1) % meta.getSplitEvery()) == 0)) {
      if (data.outputRowMeta != null) {
        if (meta.isFooterEnabled()) {
          writeHeader();
        }
      }

      if (r == null) {
        // add tag to last line if needed
        writeEndedLine();
        bEndedLineWrote = true;
      }
      // Done with this part or with everything.
      closeFile();

      // Not finished: open another file...
      if (r != null) {
        openNewFile(meta.getFileName());

        if (meta.isHeaderEnabled() && data.outputRowMeta != null)
          if (writeHeader()) incrementLinesOutput();
      }
    }

    if (r == null) // no more input to be expected...
    {
      if (false == bEndedLineWrote) {
        // add tag to last line if needed
        writeEndedLine();
        bEndedLineWrote = true;
      }

      setOutputDone();
      return false;
    }

    // First handle the file name in field
    // Write a header line as well if needed
    //
    if (meta.isFileNameInField()) {
      String baseFilename = data.fileNameMeta.getString(r[data.fileNameFieldIndex]);
      setDataWriterForFilename(baseFilename);
    }
    writeRowToFile(data.outputRowMeta, r);
    putRow(data.outputRowMeta, r); // in case we want it to go further...

    if (checkFeedback(getLinesOutput())) logBasic("linenr " + getLinesOutput());

    return result;
  }