blob: 320c3ac56b1672498cf7e279b92b13493d479b68 [file] [log] [blame]
Stuart McCulloch26e7a5a2011-10-17 10:31:43 +00001/**
2 * Copyright (c) 2000 Gatespace AB. All Rights Reserved.
3 *
4 * Gatespace grants Open Services Gateway Initiative (OSGi) an irrevocable,
5 * perpetual, non-exclusive, worldwide, paid-up right and license to
6 * reproduce, display, perform, prepare and have prepared derivative works
7 * based upon and distribute and sublicense this material and derivative
8 * works thereof as set out in the OSGi MEMBER AGREEMENT as of January 24
9 * 2000, for use in accordance with Section 2.2 of the BY-LAWS of the
10 * OSGi MEMBER AGREEMENT.
11 */
12
13package aQute.lib.filter;
14
15import java.lang.reflect.*;
16import java.math.*;
17import java.util.*;
18
19public class Filter {
20 final char WILDCARD = 65535;
21
22 final int EQ = 0;
23 final int LE = 1;
24 final int GE = 2;
25 final int APPROX = 3;
26
27 private String filter;
28
29 abstract class Query {
30 static final String GARBAGE = "Trailing garbage";
31 static final String MALFORMED = "Malformed query";
32 static final String EMPTY = "Empty list";
33 static final String SUBEXPR = "No subexpression";
34 static final String OPERATOR = "Undefined operator";
35 static final String TRUNCATED = "Truncated expression";
36 static final String EQUALITY = "Only equality supported";
37
38 private String tail;
39
40 boolean match() throws IllegalArgumentException {
41 tail = filter;
42 boolean val = doQuery();
43 if (tail.length() > 0)
44 error(GARBAGE);
45 return val;
46 }
47
48 private boolean doQuery() throws IllegalArgumentException {
49 if (tail.length() < 3 || !prefix("("))
50 error(MALFORMED);
51 boolean val;
52
53 switch (tail.charAt(0)) {
54 case '&':
55 val = doAnd();
56 break;
57 case '|':
58 val = doOr();
59 break;
60 case '!':
61 val = doNot();
62 break;
63 default:
64 val = doSimple();
65 break;
66 }
67
68 if (!prefix(")"))
69 error(MALFORMED);
70 return val;
71 }
72
73 private boolean doAnd() throws IllegalArgumentException {
74 tail = tail.substring(1);
75 boolean val = true;
76 if (!tail.startsWith("("))
77 error(EMPTY);
78 do {
79 if (!doQuery())
80 val = false;
81 } while (tail.startsWith("("));
82 return val;
83 }
84
85 private boolean doOr() throws IllegalArgumentException {
86 tail = tail.substring(1);
87 boolean val = false;
88 if (!tail.startsWith("("))
89 error(EMPTY);
90 do {
91 if (doQuery())
92 val = true;
93 } while (tail.startsWith("("));
94 return val;
95 }
96
97 private boolean doNot() throws IllegalArgumentException {
98 tail = tail.substring(1);
99 if (!tail.startsWith("("))
100 error(SUBEXPR);
101 return !doQuery();
102 }
103
104 private boolean doSimple() throws IllegalArgumentException {
105 int op = 0;
106 Object attr = getAttr();
107
108 if (prefix("="))
109 op = EQ;
110 else if (prefix("<="))
111 op = LE;
112 else if (prefix(">="))
113 op = GE;
114 else if (prefix("~="))
115 op = APPROX;
116 else
117 error(OPERATOR);
118
119 return compare(attr, op, getValue());
120 }
121
122 private boolean prefix(String pre) {
123 if (!tail.startsWith(pre))
124 return false;
125 tail = tail.substring(pre.length());
126 return true;
127 }
128
129 private Object getAttr() {
130 int len = tail.length();
131 int ix = 0;
132 label: for (; ix < len; ix++) {
133 switch (tail.charAt(ix)) {
134 case '(':
135 case ')':
136 case '<':
137 case '>':
138 case '=':
139 case '~':
140 case '*':
141 case '\\':
142 break label;
143 }
144 }
145 String attr = tail.substring(0, ix).toLowerCase();
146 tail = tail.substring(ix);
147 return getProp(attr);
148 }
149
150 abstract Object getProp(String key);
151
152 private String getValue() {
153 StringBuffer sb = new StringBuffer();
154 int len = tail.length();
155 int ix = 0;
156 label: for (; ix < len; ix++) {
157 char c = tail.charAt(ix);
158 switch (c) {
159 case '(':
160 case ')':
161 break label;
162 case '*':
163 sb.append(WILDCARD);
164 break;
165 case '\\':
166 if (ix == len - 1)
167 break label;
168 sb.append(tail.charAt(++ix));
169 break;
170 default:
171 sb.append(c);
172 break;
173 }
174 }
175 tail = tail.substring(ix);
176 return sb.toString();
177 }
178
179 private void error(String m) throws IllegalArgumentException {
180 throw new IllegalArgumentException(m + " " + tail);
181 }
182
183 private boolean compare(Object obj, int op, String s) {
184 if (obj == null)
185 return false;
186 try {
187 Class<?> numClass = obj.getClass();
188 if (numClass == String.class) {
189 return compareString((String) obj, op, s);
190 } else if (numClass == Character.class) {
191 return compareString(obj.toString(), op, s);
192 } else if (numClass == Long.class) {
193 return compareSign(op, Long.valueOf(s)
194 .compareTo((Long) obj));
195 } else if (numClass == Integer.class) {
196 return compareSign(op, Integer.valueOf(s).compareTo(
197 (Integer) obj));
198 } else if (numClass == Short.class) {
199 return compareSign(op, Short.valueOf(s).compareTo(
200 (Short) obj));
201 } else if (numClass == Byte.class) {
202 return compareSign(op, Byte.valueOf(s)
203 .compareTo((Byte) obj));
204 } else if (numClass == Double.class) {
205 return compareSign(op, Double.valueOf(s).compareTo(
206 (Double) obj));
207 } else if (numClass == Float.class) {
208 return compareSign(op, Float.valueOf(s).compareTo(
209 (Float) obj));
210 } else if (numClass == Boolean.class) {
211 if (op != EQ)
212 return false;
213 int a = Boolean.valueOf(s).booleanValue() ? 1 : 0;
214 int b = ((Boolean) obj).booleanValue() ? 1 : 0;
215 return compareSign(op, a - b);
216 } else if (numClass == BigInteger.class) {
217 return compareSign(op, new BigInteger(s)
218 .compareTo((BigInteger) obj));
219 } else if (numClass == BigDecimal.class) {
220 return compareSign(op, new BigDecimal(s)
221 .compareTo((BigDecimal) obj));
222 } else if (obj instanceof Collection<?>) {
223 for (Object x : (Collection<?>) obj)
224 if (compare(x, op, s))
225 return true;
226 } else if (numClass.isArray()) {
227 int len = Array.getLength(obj);
228 for (int i = 0; i < len; i++)
229 if (compare(Array.get(obj, i), op, s))
230 return true;
231 }
232 } catch (Exception e) {
233 }
234 return false;
235 }
236 }
237
238 class DictQuery extends Query {
239 private Dictionary<?,?> dict;
240
241 DictQuery(Dictionary<?,?> dict) {
242 this.dict = dict;
243 }
244
245 Object getProp(String key) {
246 return dict.get(key);
247 }
248 }
249
250 public Filter(String filter) throws IllegalArgumentException {
251 // NYI: Normalize the filter string?
252 this.filter = filter;
253 if (filter == null || filter.length() == 0)
254 throw new IllegalArgumentException("Null query");
255 }
256
257 public boolean match(Dictionary<?,?> dict) {
258 try {
259 return new DictQuery(dict).match();
260 } catch (IllegalArgumentException e) {
261 return false;
262 }
263 }
264
265 public String verify() {
266 try {
267 new DictQuery(new Hashtable<Object,Object>()).match();
268 } catch (IllegalArgumentException e) {
269 return e.getMessage();
270 }
271 return null;
272 }
273
274 public String toString() {
275 return filter;
276 }
277
278 public boolean equals(Object obj) {
279 return obj != null && obj instanceof Filter
280 && filter.equals(((Filter) obj).filter);
281 }
282
283 public int hashCode() {
284 return filter.hashCode();
285 }
286
287 boolean compareString(String s1, int op, String s2) {
288 switch (op) {
289 case EQ:
290 return patSubstr(s1, s2);
291 case APPROX:
292 return fixupString(s2).equals(fixupString(s1));
293 default:
294 return compareSign(op, s2.compareTo(s1));
295 }
296 }
297
298 boolean compareSign(int op, int cmp) {
299 switch (op) {
300 case LE:
301 return cmp >= 0;
302 case GE:
303 return cmp <= 0;
304 case EQ:
305 return cmp == 0;
306 default: /* APPROX */
307 return cmp == 0;
308 }
309 }
310
311 String fixupString(String s) {
312 StringBuffer sb = new StringBuffer();
313 int len = s.length();
314 boolean isStart = true;
315 boolean isWhite = false;
316 for (int i = 0; i < len; i++) {
317 char c = s.charAt(i);
318 if (Character.isWhitespace(c)) {
319 isWhite = true;
320 } else {
321 if (!isStart && isWhite)
322 sb.append(' ');
323 if (Character.isUpperCase(c))
324 c = Character.toLowerCase(c);
325 sb.append(c);
326 isStart = false;
327 isWhite = false;
328 }
329 }
330 return sb.toString();
331 }
332
333 boolean patSubstr(String s, String pat) {
334 if (s == null)
335 return false;
336 if (pat.length() == 0)
337 return s.length() == 0;
338 if (pat.charAt(0) == WILDCARD) {
339 pat = pat.substring(1);
340 for (;;) {
341 if (patSubstr(s, pat))
342 return true;
343 if (s.length() == 0)
344 return false;
345 s = s.substring(1);
346 }
347 } else {
348 if (s.length() == 0 || s.charAt(0) != pat.charAt(0))
349 return false;
350 return patSubstr(s.substring(1), pat.substring(1));
351 }
352 }
353}