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 */
019 package org.crsh.cli.impl.line;
020
021 /**
022 * Line parser.
023 *
024 * @author Julien Viet
025 */
026 public 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 }