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

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

fixed #5857 - add "closed" to search, fixed #5015 - logical AND & for search tool

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