001/*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019package org.crsh.cli.impl.line;
020
021/**
022 * Line parser.
023 *
024 * @author Julien Viet
025 */
026public final class LineParser {
027
028  public static abstract class Visitor {
029    public void onChar(int index, Quoting quoting, boolean backslash, char c) { }
030    public void openStrongQuote(int index) {}
031    public void closeStrongQuote(int index) {}
032    public void openWeakQuote(int index) {}
033    public void closeWeakQuote(int index) {}
034    public void reset() {}
035  }
036
037  /** . */
038  private static final int NORMAL = 0, WEAK_QUOTING = 1, STRONG_QUOTING = 2;
039
040  /** . */
041  private int status = NORMAL;
042
043  /** . */
044  private boolean escaped = false;
045
046  /** . */
047  private int index = 0;
048
049  /** . */
050  private final Visitor[] visitors;
051
052  public LineParser(Visitor... visitors) {
053    this.visitors = visitors;
054  }
055
056  public boolean crlf() {
057    if (escaped) {
058      escaped = false;
059      return false;
060    } else {
061      switch (status) {
062        case WEAK_QUOTING:
063          for (Visitor visitor : visitors) visitor.onChar(index, Quoting.WEAK, false, '\n');
064          index++;
065          return false;
066        case STRONG_QUOTING:
067          for (Visitor visitor : visitors) visitor.onChar(index, Quoting.STRONG, false, '\n');
068          index++;
069          return false;
070        default:
071          return true;
072      }
073    }
074  }
075
076  public LineParser append(CharSequence s) {
077    int len = s.length();
078    for (int index = 0;index < len;index++) {
079      append(s.charAt(index));
080    }
081    return this;
082  }
083
084  public LineParser append(char c) {
085    if (!escaped) {
086      switch (status) {
087        case NORMAL:
088          switch (c) {
089            case '\\':
090              escaped = true;
091              break;
092            case '\"':
093              for (Visitor visitor : visitors) visitor.openWeakQuote(index);
094              status = WEAK_QUOTING;
095              index++;
096              break;
097            case '\'':
098              for (Visitor visitor : visitors) visitor.openStrongQuote(index);
099              index++;
100              status = STRONG_QUOTING;
101              break;
102            default:
103              for (Visitor visitor : visitors) visitor.onChar(index, null, false, c);
104              index++;
105              break;
106          }
107          break;
108        case WEAK_QUOTING:
109          switch (c) {
110            case '"':
111              for (Visitor visitor : visitors) visitor.closeWeakQuote(index);
112              index++;
113              status = NORMAL;
114              break;
115            case '\\':
116              escaped = true;
117              break;
118            default:
119              for (Visitor visitor : visitors) visitor.onChar(index, Quoting.WEAK, false, c);
120              index++;
121              break;
122          }
123          break;
124        case STRONG_QUOTING:
125          switch (c) {
126            case '\'':
127              for (Visitor visitor : visitors) visitor.closeStrongQuote(index);
128              index++;
129              status = NORMAL;
130              break;
131            case '\\':
132              escaped = true;
133              break;
134            default:
135              for (Visitor visitor : visitors) visitor.onChar(index, Quoting.STRONG, false, c);
136              index++;
137              break;
138          }
139          break;
140      }
141    } else {
142      switch (status) {
143        case NORMAL:
144          for (Visitor visitor : visitors) visitor.onChar(index + 1, null, true, c);
145          index += 2;
146          break;
147        case WEAK_QUOTING:
148          for (Visitor visitor : visitors) visitor.onChar(index + 1, Quoting.WEAK, true, c);
149          index += 2;
150          break;
151        case STRONG_QUOTING:
152          if (c == '\'') {
153            // Special case
154            status = NORMAL;
155            for (Visitor visitor : visitors) visitor.onChar(index, Quoting.STRONG, false, '\\');
156            for (Visitor visitor : visitors) visitor.closeStrongQuote(index + 1);
157            index += 2;
158          } else {
159            for (Visitor visitor : visitors) visitor.onChar(index + 1, Quoting.STRONG, true, c);
160            index += 2;
161          }
162          break;
163      }
164      escaped = false;
165    }
166    return this;
167  }
168
169  public void reset() {
170    index = 0;
171    status = NORMAL;
172    escaped = false;
173    for (Visitor visitor : visitors) visitor.reset();
174  }
175}