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

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

sonar - squid:S00112 - Generic exceptions should never be thrown: define JosmRuntimeException

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