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

Last change on this file since 4346 was 4346, checked in by stoecker, 13 years ago

fix #3942 - patch by simon04 - improve range handling for search

  • Property svn:eol-style set to native
File size: 6.9 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>")), 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 anything else.
105 * @return The next token in the stream.
106 */
107 public Token nextToken() {
108 if (currentToken != null) {
109 Token result = currentToken;
110 currentToken = null;
111 return result;
112 }
113
114 while (Character.isWhitespace(c)) {
115 getChar();
116 }
117 switch (c) {
118 case -1:
119 getChar();
120 return Token.EOF;
121 case ':':
122 getChar();
123 return Token.COLON;
124 case '=':
125 getChar();
126 return Token.EQUALS;
127 case '(':
128 getChar();
129 return Token.LEFT_PARENT;
130 case ')':
131 getChar();
132 return Token.RIGHT_PARENT;
133 case '|':
134 getChar();
135 return Token.OR;
136 case '&':
137 getChar();
138 return nextToken();
139 case '?':
140 getChar();
141 return Token.QUESTION_MARK;
142 case '"':
143 getChar();
144 currentText = getString(true);
145 getChar();
146 return Token.KEY;
147 default:
148 String prefix = "";
149 if (c == '-') {
150 getChar();
151 if (!Character.isDigit(c))
152 return Token.NOT;
153 prefix = "-";
154 }
155 currentText = prefix + getString();
156 if ("or".equalsIgnoreCase(currentText))
157 return Token.OR;
158 if ("and".equalsIgnoreCase(currentText))
159 return nextToken();
160 // try parsing number
161 try {
162 currentNumber = Long.parseLong(currentText);
163 } catch (NumberFormatException e) {
164 currentNumber = null;
165 }
166 // if text contains "-", try parsing a range
167 int pos = currentText.indexOf('-', 1);
168 isRange = pos > 0;
169 if (isRange) {
170 try {
171 currentNumber = Long.parseLong(currentText.substring(0, pos));
172 } catch (NumberFormatException e) {
173 currentNumber = null;
174 }
175 try {
176 currentRange = Long.parseLong(currentText.substring(pos + 1));
177 } catch (NumberFormatException e) {
178 currentRange = null;
179 }
180 } else {
181 currentRange = null;
182 }
183 return Token.KEY;
184 }
185 }
186
187 public boolean readIfEqual(Token token) {
188 Token nextTok = nextToken();
189 if (equal(nextTok, token))
190 return true;
191 currentToken = nextTok;
192 return false;
193 }
194
195 public String readTextOrNumber() {
196 Token nextTok = nextToken();
197 if (nextTok == Token.KEY)
198 return currentText;
199 currentToken = nextTok;
200 return null;
201 }
202
203 public long readNumber(String errorMessage) throws ParseError {
204 if ((nextToken() == Token.KEY) && (currentNumber != null))
205 return currentNumber;
206 else
207 throw new ParseError(errorMessage);
208 }
209
210 public long getReadNumber() {
211 return (currentNumber != null) ? currentNumber : 0;
212 }
213
214 public Range readRange(String errorMessage) throws ParseError {
215 if (nextToken() != Token.KEY || (currentNumber == null && currentRange == null)) {
216 throw new ParseError(errorMessage);
217 } else if (!isRange && currentNumber != null) {
218 if (currentNumber >= 0) {
219 return new Range(currentNumber, currentNumber);
220 } else {
221 return new Range(0, Math.abs(currentNumber));
222 }
223 } else if (isRange && currentRange == null) {
224 return new Range(currentNumber, Integer.MAX_VALUE);
225 } else if (currentNumber != null && currentRange != null) {
226 return new Range(currentNumber, currentRange);
227 } else {
228 throw new ParseError(errorMessage);
229 }
230 }
231
232 public String getText() {
233 return currentText;
234 }
235}
Note: See TracBrowser for help on using the repository browser.