View Javadoc
1   package org.cyclopsgroup.caff.token;
2   
3   /**
4    * An implementation that escapes special character with an escape character
5    *
6    * @author <a href="mailto:jiaqi@cyclopsgroup.org">Jiaqi Guo</a>
7    */
8   public class EscapingValueTokenizer implements ValueTokenizer {
9     private static enum ProcessingState {
10      /** Followd by escape character */
11      ESCAPING,
12      /** Ready for new word */
13      READY,
14      /** Appending a word */
15      WORD;
16    }
17  
18    private final char delimiter;
19  
20    private final char escaper;
21  
22    /**
23     * Default constructor sets escape character with back slash, and imply white space as delimiter
24     */
25    public EscapingValueTokenizer() {
26      this(' ', '\\');
27    }
28  
29    /**
30     * @param delimiter Delimiter character
31     * @param escaper Given escape character
32     */
33    public EscapingValueTokenizer(char delimiter, char escaper) {
34      this.escaper = escaper;
35      this.delimiter = delimiter;
36    }
37  
38    @Override
39    public String escape(String output) {
40      if (output.indexOf(escaper) == -1 && output.indexOf(delimiter) == -1) {
41        return output;
42      }
43      StringBuffer sb = new StringBuffer();
44      for (int i = 0, j = 0; i < output.length(); ) {
45        int e = output.indexOf(escaper, i);
46        int d = output.indexOf(delimiter, i);
47        if (e == -1 && d == -1) {
48          sb.append(output.substring(i));
49          break;
50        }
51        if (e >= 0 && d >= 0) {
52          j = Math.min(e, d);
53        } else {
54          j = Math.max(e, d);
55        }
56        sb.append(output.substring(i, j)).append(escaper).append(output.charAt(j));
57        i = ++j;
58      }
59      return sb.toString();
60    }
61  
62    /** @return Delimiter character */
63    public final char getDelimiter() {
64      return delimiter;
65    }
66  
67    /** @return Escape character */
68    public final char getEscaper() {
69      return escaper;
70    }
71  
72    @Override
73    public void parse(CharSequence input, TokenEventHandler handler) {
74      ProcessingState state = ProcessingState.READY;
75      StringBuilder word = null;
76      int wordStart = 0;
77      for (int i = 0; i < input.length(); i++) {
78        char c = input.charAt(i);
79        switch (state) {
80          case READY:
81            if (c == delimiter) {
82              continue;
83            } else {
84              wordStart = i;
85              word = new StringBuilder();
86              if (c == escaper) {
87                state = ProcessingState.ESCAPING;
88              } else {
89                state = ProcessingState.WORD;
90                word.append(c);
91              }
92            }
93            break;
94          case WORD:
95            if (c == delimiter) {
96              state = ProcessingState.READY;
97              TokenEvent ev = new TokenEvent(word.toString(), wordStart, i, true);
98              handler.handleEvent(ev);
99              word = null;
100           } else if (c == escaper) {
101             state = ProcessingState.ESCAPING;
102           } else {
103             word.append(c);
104           }
105           break;
106         case ESCAPING:
107           state = ProcessingState.WORD;
108           word.append(c);
109       }
110     }
111     if (word != null) {
112       TokenEvent ev = new TokenEvent(word.toString(), wordStart, input.length(), false);
113       handler.handleEvent(ev);
114     }
115   }
116 }