private void writeField(ValueMetaInterface v, Object valueData, byte[] nullString)
      throws KettleStepException {
    try {
      byte[] str;

      // First check whether or not we have a null string set
      // These values should be set when a null value passes
      //
      if (nullString != null && v.isNull(valueData)) {
        str = nullString;
      } else {
        if (meta.isFastDump()) {
          if (valueData instanceof byte[]) {
            str = (byte[]) valueData;
          } else {
            str = getBinaryString((valueData == null) ? "" : valueData.toString());
          }
        } else {
          str = formatField(v, valueData);
        }
      }

      if (str != null && str.length > 0) {
        List<Integer> enclosures = null;

        if (v.isString() && meta.isEnclosureForced() && !meta.isPadded()) {
          data.writer.write(data.binaryEnclosure);

          // Also check for the existence of the enclosure character.
          // If needed we double (escape) the enclosure character.
          //
          enclosures = getEnclosurePositions(str);
        }

        if (enclosures == null) {
          data.writer.write(str);
        } else {
          // Skip the enclosures, double them instead...
          int from = 0;
          for (int i = 0; i < enclosures.size(); i++) {
            int position = enclosures.get(i);
            data.writer.write(str, from, position + data.binaryEnclosure.length - from);
            data.writer.write(data.binaryEnclosure); // write enclosure a second time
            from = position + data.binaryEnclosure.length;
          }
          if (from < str.length) {
            data.writer.write(str, from, str.length - from);
          }
        }

        if (v.isString() && meta.isEnclosureForced() && !meta.isPadded()) {
          data.writer.write(data.binaryEnclosure);
        }
      }
    } catch (Exception e) {
      throw new KettleStepException("Error writing field content to file", e);
    }
  }
  private boolean writeHeader() {
    boolean retval = false;
    RowMetaInterface r = data.outputRowMeta;

    try {
      // If we have fields specified: list them in this order!
      if (meta.getOutputFields() != null && meta.getOutputFields().length > 0) {
        for (int i = 0; i < meta.getOutputFields().length; i++) {
          String fieldName = meta.getOutputFields()[i].getName();
          ValueMetaInterface v = r.searchValueMeta(fieldName);

          if (i > 0 && data.binarySeparator.length > 0) {
            data.writer.write(data.binarySeparator);
          }
          if (meta.isEnclosureForced()
              && data.binaryEnclosure.length > 0
              && v != null
              && v.isString()) {
            data.writer.write(data.binaryEnclosure);
          }
          data.writer.write(getBinaryString(fieldName));
          if (meta.isEnclosureForced()
              && data.binaryEnclosure.length > 0
              && v != null
              && v.isString()) {
            data.writer.write(data.binaryEnclosure);
          }
        }
        data.writer.write(data.binaryNewline);
      } else if (r != null) // Just put all field names in the header/footer
      {
        for (int i = 0; i < r.size(); i++) {
          if (i > 0 && data.binarySeparator.length > 0) {
            data.writer.write(data.binarySeparator);
          }
          ValueMetaInterface v = r.getValueMeta(i);

          if (meta.isEnclosureForced() && data.binaryEnclosure.length > 0 && v.isString()) {
            data.writer.write(data.binaryEnclosure);
          }
          data.writer.write(getBinaryString(v.getName()));
          if (meta.isEnclosureForced() && data.binaryEnclosure.length > 0 && v.isString()) {
            data.writer.write(data.binaryEnclosure);
          }
        }
        data.writer.write(data.binaryNewline);
      } else {
        data.writer.write(getBinaryString("no rows selected" + Const.CR));
      }
    } catch (Exception e) {
      logError("Error writing header line: " + e.toString());
      logError(Const.getStackTracker(e));
      retval = true;
    }
    incrementLinesOutput();
    return retval;
  }
 private byte[] formatField(ValueMetaInterface v, Object valueData) throws KettleValueException {
   if (v.isString()) {
     String svalue = (valueData instanceof String) ? (String) valueData : v.getString(valueData);
     return convertStringToBinaryString(v, Const.trimToType(svalue, v.getTrimType()));
   } else {
     return v.getBinaryString(valueData);
   }
 }
  private void writeRowToBulk(RowMetaInterface rowMeta, Object[] r) throws KettleException {

    try {
      // So, we have this output stream to which we can write CSV data to.
      // Basically, what we need to do is write the binary data (from strings to
      // it as part of this proof of concept)
      //
      // The data format required is essentially "value|value|value|value"
      // new feature implemented
      // "use SSV which requires the format to be '"value";"value","value"'
      byte[] delimiter;
      if (meta.isUseSSV()) {
        delimiter = data.semicolon;
      } else {
        delimiter = data.separator;
      }

      for (int i = 0; i < data.keynrs.length; i++) {
        if (i > 0) {
          // Write a separator
          //
          write(delimiter);
        }

        int index = data.keynrs[i];
        ValueMetaInterface valueMeta = rowMeta.getValueMeta(index);
        Object valueData = r[index];

        if (valueData == null) {
          // Don't output anything for null
          //
        } else {
          if (valueMeta.isStorageBinaryString()) {
            byte[] value = valueMeta.getBinaryString(valueData);
            write(value);
          } else {
            // We're using the bulk row metadata so dates and numerics should be in the correct
            // format now...
            //
            String string = valueMeta.getString(valueData);
            if (string != null) {
              // support of SSV feature
              //
              if (meta.isUseSSV()) {

                // replace " in string fields
                //
                if (meta.isEscapingSpecialCharacters() && valueMeta.isString()) {

                  StringBuilder builder = new StringBuilder(string);
                  String[] escapeStrings =
                      new String[] {
                        "\"", "\n", "\r",
                      };
                  String[] replaceStrings =
                      new String[] {
                        "\\\"", "\\n", "\\r",
                      };
                  for (int e = 0; e < escapeStrings.length; e++) {
                    String chr = escapeStrings[e];
                    String rep = replaceStrings[e];
                    int idx = builder.indexOf(chr, 0);
                    while (idx > 0) {
                      builder.replace(idx, idx + chr.length(), rep);
                      idx = builder.indexOf(chr, idx + rep.length());
                    }
                  }
                  string = builder.toString();
                }
                write(data.doubleQuote);
                write(data.getBytes(string));
                write(data.doubleQuote);
              } else {
                write(data.getBytes(string));
              }
            }
          }
        }
      }

      // finally write a newline
      //
      write(data.newline);
    } catch (Exception e) {
      // If something went wrong with the import,
      // rather return that error, in stead of "Pipe Broken"
      try {
        data.sqlRunner.checkExcn();
      } catch (Exception loadEx) {
        throw new KettleException("Error serializing rows of data to the fifo file", loadEx);
      }

      throw new KettleException("Error serializing rows of data to the fifo file", e);
    }
  }