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

Last change on this file since 2993 was 2993, checked in by jttt, 14 years ago

Fix #4528 Search for colon-containing keys broken

  • Property svn:eol-style set to native
File size: 5.7 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;
6
7import java.io.IOException;
8import java.io.Reader;
9
10import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
11
12public class PushbackTokenizer {
13
14 public class Range {
15 private final long start;
16 private final long end;
17
18 public Range(long start, long end) {
19 this.start = start;
20 this.end = end;
21 }
22
23 public long getStart() {
24 return start;
25 }
26
27 public long getEnd() {
28 return end;
29 }
30 }
31
32 private final Reader search;
33
34 private Token currentToken;
35 private String currentText;
36 private long currentNumber;
37 private long currentRange;
38 private int c;
39
40 public PushbackTokenizer(Reader search) {
41 this.search = search;
42 getChar();
43 }
44
45 public enum Token {
46 NOT(marktr("<not>")), OR(marktr("<or>")), LEFT_PARENT(marktr("<left parent>")),
47 RIGHT_PARENT(marktr("<right parent>")), COLON(marktr("<colon>")), EQUALS(marktr("<equals>")),
48 KEY(marktr("<key>")), QUESTION_MARK(marktr("<question mark>")), NUMBER(marktr("<number>")),
49 RANGE(marktr("range")), EOF(marktr("<end-of-file>"));
50
51 private Token(String name) {
52 this.name = name;
53 }
54
55 private final String name;
56
57 @Override
58 public String toString() {
59 return tr(name);
60 }
61 }
62
63
64 private void getChar() {
65 try {
66 c = search.read();
67 } catch (IOException e) {
68 throw new RuntimeException(e.getMessage(), e);
69 }
70 }
71
72 private long getNumber() {
73 long result = 0;
74 while (Character.isDigit(c)) {
75 result = result * 10 + (c - '0');
76 getChar();
77 }
78 return result;
79 }
80
81 /**
82 * The token returned is <code>null</code> or starts with an identifier character:
83 * - for an '-'. This will be the only character
84 * : for an key. The value is the next token
85 * | for "OR"
86 * ' ' for anything else.
87 * @return The next token in the stream.
88 */
89 public Token nextToken() {
90 if (currentToken != null) {
91 Token result = currentToken;
92 currentToken = null;
93 return result;
94 }
95
96 while (Character.isWhitespace(c)) {
97 getChar();
98 }
99 switch (c) {
100 case -1:
101 getChar();
102 return Token.EOF;
103 case ':':
104 getChar();
105 return Token.COLON;
106 case '=':
107 getChar();
108 return Token.EQUALS;
109 case '-':
110 getChar();
111 if (Character.isDigit(c)) {
112 currentNumber = -1 * getNumber();
113 return Token.NUMBER;
114 } else
115 return Token.NOT;
116 case '(':
117 getChar();
118 return Token.LEFT_PARENT;
119 case ')':
120 getChar();
121 return Token.RIGHT_PARENT;
122 case '|':
123 getChar();
124 return Token.OR;
125 case '?':
126 getChar();
127 return Token.QUESTION_MARK;
128 case '"':
129 {
130 getChar();
131 StringBuilder s = new StringBuilder();
132 boolean escape = false;
133 while (c != -1 && (c != '"' || escape)) {
134 if (c == '\\' && !escape) {
135 escape = true;
136 } else {
137 s.append((char)c);
138 escape = false;
139 }
140 getChar();
141 }
142 getChar();
143 currentText = s.toString();
144 return Token.KEY;
145 }
146 default:
147 {
148 if (Character.isDigit(c)) {
149 currentNumber = getNumber();
150 if (c == '-') {
151 getChar();
152 currentRange = getNumber();
153 return Token.RANGE;
154 } else
155 return Token.NUMBER;
156
157 }
158
159 StringBuilder s = new StringBuilder();
160 while (!(c == -1 || Character.isWhitespace(c) || c == '"'|| c == ':' || c == '(' || c == ')' || c == '|' || c == '=' || c == '?')) {
161 s.append((char)c);
162 getChar();
163 }
164 currentText = s.toString();
165 if ("or".equals(currentText))
166 return Token.OR;
167 else
168 return Token.KEY;
169 }
170 }
171 }
172
173 public boolean readIfEqual(Token token) {
174 Token nextTok = nextToken();
175 if (nextTok == null ? token == null : nextTok == token)
176 return true;
177 currentToken = nextTok;
178 return false;
179 }
180
181 public String readTextOrNumber() {
182 Token nextTok = nextToken();
183 if (nextTok == Token.KEY)
184 return currentText;
185 if (nextTok == Token.NUMBER)
186 return String.valueOf(currentNumber);
187 currentToken = nextTok;
188 return null;
189 }
190
191 public long readNumber(String errorMessage) throws ParseError {
192 if (nextToken() == Token.NUMBER)
193 return currentNumber;
194 else
195 throw new ParseError(errorMessage);
196 }
197
198 public long getReadNumber() {
199 return currentNumber;
200 }
201
202 public Range readRange() throws ParseError {
203 Token token = nextToken();
204 if (token == Token.NUMBER)
205 return new Range(currentNumber, currentNumber);
206 else if (token == Token.RANGE)
207 return new Range(currentNumber, currentRange);
208 else
209 throw new ParseError(Token.NUMBER, token);
210 }
211
212 public String getText() {
213 return currentText;
214 }
215}
Note: See TracBrowser for help on using the repository browser.