source: josm/trunk/src/org/openstreetmap/josm/actions/search/PushbackTokenizer.java@ 8510

Last change on this file since 8510 was 8510, checked in by Don-vip, 9 years ago

checkstyle: enable relevant whitespace checks and fix them

  • Property svn:eol-style set to native
File size: 7.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions.search;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.io.IOException;
8import java.io.Reader;
9import java.util.Arrays;
10import java.util.List;
11import java.util.Objects;
12
13import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
14
15public class PushbackTokenizer {
16
17 public static class Range {
18 private final long start;
19 private final long end;
20
21 public Range(long start, long end) {
22 this.start = start;
23 this.end = end;
24 }
25
26 public long getStart() {
27 return start;
28 }
29
30 public long getEnd() {
31 return end;
32 }
33
34 @Override
35 public String toString() {
36 return "Range [start=" + start + ", end=" + end + "]";
37 }
38 }
39
40 private final Reader search;
41
42 private Token currentToken;
43 private String currentText;
44 private Long currentNumber;
45 private Long currentRange;
46 private int c;
47 private boolean isRange;
48
49 public PushbackTokenizer(Reader search) {
50 this.search = search;
51 getChar();
52 }
53
54 public enum Token {
55 NOT(marktr("<not>")), OR(marktr("<or>")), XOR(marktr("<xor>")), LEFT_PARENT(marktr("<left parent>")),
56 RIGHT_PARENT(marktr("<right parent>")), COLON(marktr("<colon>")), EQUALS(marktr("<equals>")),
57 KEY(marktr("<key>")), QUESTION_MARK(marktr("<question mark>")),
58 EOF(marktr("<end-of-file>")), LESS_THAN("<less-than>"), GREATER_THAN("<greater-than>");
59
60 private Token(String name) {
61 this.name = name;
62 }
63
64 private final String name;
65
66 @Override
67 public String toString() {
68 return tr(name);
69 }
70 }
71
72 private void getChar() {
73 try {
74 c = search.read();
75 } catch (IOException e) {
76 throw new RuntimeException(e.getMessage(), e);
77 }
78 }
79
80 private static final List<Character> specialChars = Arrays.asList('"', ':', '(', ')', '|', '^', '=', '?', '<', '>');
81 private static final List<Character> specialCharsQuoted = Arrays.asList('"');
82
83 private String getString(boolean quoted) {
84 List<Character> sChars = quoted ? specialCharsQuoted : specialChars;
85 StringBuilder s = new StringBuilder();
86 boolean escape = false;
87 while (c != -1 && (escape || (!sChars.contains((char) c) && (quoted || !Character.isWhitespace(c))))) {
88 if (c == '\\' && !escape) {
89 escape = true;
90 } else {
91 s.append((char) c);
92 escape = false;
93 }
94 getChar();
95 }
96 return s.toString();
97 }
98
99 private String getString() {
100 return getString(false);
101 }
102
103 /**
104 * The token returned is <code>null</code> or starts with an identifier character:
105 * - for an '-'. This will be the only character
106 * : for an key. The value is the next token
107 * | for "OR"
108 * ^ for "XOR"
109 * ' ' for anything else.
110 * @return The next token in the stream.
111 */
112 public Token nextToken() {
113 if (currentToken != null) {
114 Token result = currentToken;
115 currentToken = null;
116 return result;
117 }
118
119 while (Character.isWhitespace(c)) {
120 getChar();
121 }
122 switch (c) {
123 case -1:
124 getChar();
125 return Token.EOF;
126 case ':':
127 getChar();
128 return Token.COLON;
129 case '=':
130 getChar();
131 return Token.EQUALS;
132 case '<':
133 getChar();
134 return Token.LESS_THAN;
135 case '>':
136 getChar();
137 return Token.GREATER_THAN;
138 case '(':
139 getChar();
140 return Token.LEFT_PARENT;
141 case ')':
142 getChar();
143 return Token.RIGHT_PARENT;
144 case '|':
145 getChar();
146 return Token.OR;
147 case '^':
148 getChar();
149 return Token.XOR;
150 case '&':
151 getChar();
152 return nextToken();
153 case '?':
154 getChar();
155 return Token.QUESTION_MARK;
156 case '"':
157 getChar();
158 currentText = getString(true);
159 getChar();
160 return Token.KEY;
161 default:
162 String prefix = "";
163 if (c == '-') {
164 getChar();
165 if (!Character.isDigit(c))
166 return Token.NOT;
167 prefix = "-";
168 }
169 currentText = prefix + getString();
170 if ("or".equalsIgnoreCase(currentText))
171 return Token.OR;
172 else if ("xor".equalsIgnoreCase(currentText))
173 return Token.XOR;
174 else if ("and".equalsIgnoreCase(currentText))
175 return nextToken();
176 // try parsing number
177 try {
178 currentNumber = Long.valueOf(currentText);
179 } catch (NumberFormatException e) {
180 currentNumber = null;
181 }
182 // if text contains "-", try parsing a range
183 int pos = currentText.indexOf('-', 1);
184 isRange = pos > 0;
185 if (isRange) {
186 try {
187 currentNumber = Long.valueOf(currentText.substring(0, pos));
188 } catch (NumberFormatException e) {
189 currentNumber = null;
190 }
191 try {
192 currentRange = Long.valueOf(currentText.substring(pos + 1));
193 } catch (NumberFormatException e) {
194 currentRange = null;
195 }
196 } else {
197 currentRange = null;
198 }
199 return Token.KEY;
200 }
201 }
202
203 public boolean readIfEqual(Token token) {
204 Token nextTok = nextToken();
205 if (Objects.equals(nextTok, token))
206 return true;
207 currentToken = nextTok;
208 return false;
209 }
210
211 public String readTextOrNumber() {
212 Token nextTok = nextToken();
213 if (nextTok == Token.KEY)
214 return currentText;
215 currentToken = nextTok;
216 return null;
217 }
218
219 public long readNumber(String errorMessage) throws ParseError {
220 if ((nextToken() == Token.KEY) && (currentNumber != null))
221 return currentNumber;
222 else
223 throw new ParseError(errorMessage);
224 }
225
226 public long getReadNumber() {
227 return (currentNumber != null) ? currentNumber : 0;
228 }
229
230 public Range readRange(String errorMessage) throws ParseError {
231 if (nextToken() != Token.KEY || (currentNumber == null && currentRange == null)) {
232 throw new ParseError(errorMessage);
233 } else if (!isRange && currentNumber != null) {
234 if (currentNumber >= 0) {
235 return new Range(currentNumber, currentNumber);
236 } else {
237 return new Range(0, Math.abs(currentNumber));
238 }
239 } else if (isRange && currentRange == null) {
240 return new Range(currentNumber, Integer.MAX_VALUE);
241 } else if (currentNumber != null && currentRange != null) {
242 return new Range(currentNumber, currentRange);
243 } else {
244 throw new ParseError(errorMessage);
245 }
246 }
247
248 public String getText() {
249 return currentText;
250 }
251}
Note: See TracBrowser for help on using the repository browser.