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

Last change on this file since 4817 was 4817, checked in by bastiK, 13 years ago

applied #7178 - Allow plugins to register search operators (patch by joshdoe)

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