EscapingValueTokenizer.java

package org.cyclopsgroup.caff.token;

/**
 * An implementation that escapes special character with an escape character
 *
 * @author <a href="mailto:jiaqi@cyclopsgroup.org">Jiaqi Guo</a>
 */
public class EscapingValueTokenizer implements ValueTokenizer {
  private static enum ProcessingState {
    /** Followd by escape character */
    ESCAPING,
    /** Ready for new word */
    READY,
    /** Appending a word */
    WORD;
  }

  private final char delimiter;

  private final char escaper;

  /**
   * Default constructor sets escape character with back slash, and imply white space as delimiter
   */
  public EscapingValueTokenizer() {
    this(' ', '\\');
  }

  /**
   * @param delimiter Delimiter character
   * @param escaper Given escape character
   */
  public EscapingValueTokenizer(char delimiter, char escaper) {
    this.escaper = escaper;
    this.delimiter = delimiter;
  }

  @Override
  public String escape(String output) {
    if (output.indexOf(escaper) == -1 && output.indexOf(delimiter) == -1) {
      return output;
    }
    StringBuffer sb = new StringBuffer();
    for (int i = 0, j = 0; i < output.length(); ) {
      int e = output.indexOf(escaper, i);
      int d = output.indexOf(delimiter, i);
      if (e == -1 && d == -1) {
        sb.append(output.substring(i));
        break;
      }
      if (e >= 0 && d >= 0) {
        j = Math.min(e, d);
      } else {
        j = Math.max(e, d);
      }
      sb.append(output.substring(i, j)).append(escaper).append(output.charAt(j));
      i = ++j;
    }
    return sb.toString();
  }

  /**
   * @return Delimiter character
   */
  public final char getDelimiter() {
    return delimiter;
  }

  /**
   * @return Escape character
   */
  public final char getEscaper() {
    return escaper;
  }

  @Override
  public void parse(CharSequence input, TokenEventHandler handler) {
    ProcessingState state = ProcessingState.READY;
    StringBuilder word = null;
    int wordStart = 0;
    for (int i = 0; i < input.length(); i++) {
      char c = input.charAt(i);
      switch (state) {
        case READY:
          if (c == delimiter) {
            continue;
          } else {
            wordStart = i;
            word = new StringBuilder();
            if (c == escaper) {
              state = ProcessingState.ESCAPING;
            } else {
              state = ProcessingState.WORD;
              word.append(c);
            }
          }
          break;
        case WORD:
          if (c == delimiter) {
            state = ProcessingState.READY;
            TokenEvent ev = new TokenEvent(word.toString(), wordStart, i, true);
            handler.handleEvent(ev);
            word = null;
          } else if (c == escaper) {
            state = ProcessingState.ESCAPING;
          } else {
            word.append(c);
          }
          break;
        case ESCAPING:
          state = ProcessingState.WORD;
          word.append(c);
      }
    }
    if (word != null) {
      TokenEvent ev = new TokenEvent(word.toString(), wordStart, input.length(), false);
      handler.handleEvent(ev);
    }
  }
}