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

Last change on this file since 11446 was 11393, checked in by Don-vip, 7 years ago

code style

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