QueryExpStringTest.java revision 0:37a05a11f281
1/*
2 * Copyright 2003 Sun Microsystems, Inc.  All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25 * @test
26 * @bug 4886011
27 * @summary Test that QueryExp.toString() is reversible
28 * @author Eamonn McManus
29 * @run clean QueryExpStringTest
30 * @run build QueryExpStringTest
31 * @run main QueryExpStringTest
32 */
33
34import java.util.*;
35import javax.management.*;
36
37public class QueryExpStringTest {
38
39    private static final ValueExp
40        attr = Query.attr("attr"),
41        qattr = Query.attr("className", "attr"),
42        classattr = Query.classattr(),
43        simpleString = Query.value("simpleString"),
44        complexString = Query.value("a'b\\'\""),
45        intValue = Query.value(12345678),
46        integerValue = Query.value(new Integer(12345678)),
47        longValue = Query.value(12345678L),
48        floatValue = Query.value(2.5f),
49        doubleValue = Query.value(2.5d),
50        booleanValue = Query.value(true),
51        plusValue = Query.plus(intValue, integerValue),
52        timesValue = Query.times(doubleValue, floatValue),
53        minusValue = Query.minus(floatValue, doubleValue),
54        divValue = Query.div(doubleValue, floatValue);
55
56    private static final QueryExp
57        gt = Query.gt(intValue, floatValue),
58        geq = Query.geq(intValue, floatValue),
59        leq = Query.leq(intValue, floatValue),
60        lt = Query.lt(intValue, floatValue),
61        eq = Query.eq(intValue, floatValue),
62        between = Query.between(intValue, floatValue, doubleValue),
63        match = Query.match((AttributeValueExp) attr,
64                            (StringValueExp) simpleString),
65        initial = Query.initialSubString((AttributeValueExp) attr,
66                                         (StringValueExp) simpleString),
67        initialStar = Query.initialSubString((AttributeValueExp) attr,
68                                             Query.value("*")),
69        any = Query.anySubString((AttributeValueExp) attr,
70                                 (StringValueExp) simpleString),
71        anyStar = Query.anySubString((AttributeValueExp) attr,
72                                     Query.value("*")),
73        ffinal = Query.finalSubString((AttributeValueExp) attr,
74                                      (StringValueExp) simpleString),
75        finalMagic = Query.finalSubString((AttributeValueExp) attr,
76                                          Query.value("?*[\\")),
77        in = Query.in(intValue, new ValueExp[] {intValue, floatValue}),
78        and = Query.and(gt, lt),
79        or = Query.or(gt, lt),
80        not = Query.not(gt);
81
82    // Commented-out tests below require change to implementation
83
84    private static final Object tests[] = {
85        attr, "attr",
86        qattr, "className.attr",
87        classattr, "Class",
88        simpleString, "'simpleString'",
89//      complexString, "'a\\'b\\\\\\'\"'",
90        intValue, "12345678",
91        integerValue, "12345678",
92        longValue, "12345678",
93        floatValue, "2.5",
94        doubleValue, "2.5",
95        booleanValue, "true",
96        plusValue, "12345678 + 12345678",
97        timesValue, "2.5 * 2.5",
98        minusValue, "2.5 - 2.5",
99        divValue, "2.5 / 2.5",
100        gt, "(12345678) > (2.5)",
101        geq, "(12345678) >= (2.5)",
102        leq, "(12345678) <= (2.5)",
103        lt, "(12345678) < (2.5)",
104        eq, "(12345678) = (2.5)",
105        between, "(12345678) between (2.5) and (2.5)",
106        match, "attr like 'simpleString'",
107//      initial, "attr like 'simpleString*'",
108//      initialStar, "attr like '\\\\**'",
109//      any, "attr like '*simpleString*'",
110//      anyStar, "attr like '*\\\\**'",
111//      ffinal, "attr like '*simpleString'",
112//      finalMagic, "attr like '*\\\\?\\\\*\\\\[\\\\\\\\'",
113        in, "12345678 in (12345678, 2.5)",
114        and, "((12345678) > (2.5)) and ((12345678) < (2.5))",
115        or, "((12345678) > (2.5)) or ((12345678) < (2.5))",
116        not, "not ((12345678) > (2.5))",
117    };
118
119    public static void main(String[] args) throws Exception {
120        System.out.println("Testing QueryExp.toString()");
121
122        boolean ok = true;
123
124        for (int i = 0; i < tests.length; i += 2) {
125            String testString = tests[i].toString();
126            String expected = (String) tests[i + 1];
127            if (expected.equals(testString))
128                System.out.println("OK: " + expected);
129            else {
130                System.err.println("Expected: {" + expected + "}; got: {" +
131                                   testString + "}");
132                ok = false;
133            }
134
135            try {
136                Object parsed;
137                String[] expectedref = new String[] {expected};
138                if (tests[i] instanceof ValueExp)
139                    parsed = parseExp(expectedref);
140                else
141                    parsed = parseQuery(expectedref);
142                if (expectedref[0].length() > 0)
143                    throw new Exception("Junk after parse: " + expectedref[0]);
144                String parsedString = parsed.toString();
145                if (parsedString.equals(expected))
146                    System.out.println("OK: parsed " + parsedString);
147                else {
148                    System.err.println("Parse differs: expected: {" +
149                                       expected + "}; got: {" +
150                                       parsedString + "}");
151                    ok = false;
152                }
153            } catch (Exception e) {
154                System.err.println("Parse got exception: {" + expected +
155                                   "}: " + e);
156                ok = false;
157            }
158        }
159
160        if (ok)
161            System.out.println("Test passed");
162        else {
163            System.out.println("TEST FAILED");
164            System.exit(1);
165        }
166    }
167
168    private static QueryExp parseQuery(String[] ss) throws Exception {
169        if (skip(ss, "("))
170            return parseQueryAfterParen(ss);
171
172        if (skip(ss, "not (")) {
173            QueryExp not = parseQuery(ss);
174            if (!skip(ss, ")"))
175                throw new Exception("Expected ) after not (...");
176            return Query.not(not);
177        }
178
179        ValueExp exp = parseExp(ss);
180
181        if (skip(ss, " like ")) {
182            ValueExp pat = parseExp(ss);
183            if (!(exp instanceof AttributeValueExp &&
184                  pat instanceof StringValueExp)) {
185                throw new Exception("Expected types `attr like string': " +
186                                    exp + " like " + pat);
187            }
188            return Query.match((AttributeValueExp) exp, (StringValueExp) pat);
189        }
190
191        if (skip(ss, " in (")) {
192            List values = new ArrayList();
193            if (!skip(ss, ")")) {
194                do {
195                    values.add(parseExp(ss));
196                } while (skip(ss, ", "));
197                if (!skip(ss, ")"))
198                    throw new Exception("Expected ) after in (...");
199            }
200            return Query.in(exp, (ValueExp[]) values.toArray(new ValueExp[0]));
201        }
202
203        throw new Exception("Expected in or like after expression");
204    }
205
206    private static QueryExp parseQueryAfterParen(String[] ss)
207            throws Exception {
208        /* This is very ugly.  We might have "(q1) and (q2)" here, or
209           we might have "(e1) < (e2)".  Since the syntax for a query
210           (q1) is not the same as for an expression (e1), but can
211           begin with one, we try to parse the query, and if we get an
212           exception we then try to parse an expression.  It's a hacky
213           kind of look-ahead.  */
214        String start = ss[0];
215        try {
216            QueryExp lhs = parseQuery(ss);
217            QueryExp result;
218
219            if (skip(ss, ") and ("))
220                result = Query.and(lhs, parseQuery(ss));
221            else if (skip(ss, ") or ("))
222                result = Query.or(lhs, parseQuery(ss));
223            else
224                throw new Exception("Expected `) and/or ('");
225            if (!skip(ss, ")"))
226                throw new Exception("Expected `)' after subquery");
227            return result;
228        } catch (Exception e) {
229            ss[0] = start;
230            ValueExp lhs = parseExp(ss);
231            if (!skip(ss, ") "))
232                throw new Exception("Expected `) ' after subexpression");
233            String op = scanWord(ss);
234            if (!skip(ss, " ("))
235                throw new Exception("Expected ` (' after `" + op + "'");
236            ValueExp rhs = parseExp(ss);
237            if (!skip(ss, ")"))
238                throw new Exception("Expected `)' after subexpression");
239            if (op.equals("="))
240                return Query.eq(lhs, rhs);
241            if (op.equals("<"))
242                return Query.lt(lhs, rhs);
243            if (op.equals(">"))
244                return Query.gt(lhs, rhs);
245            if (op.equals("<="))
246                return Query.leq(lhs, rhs);
247            if (op.equals(">="))
248                return Query.geq(lhs, rhs);
249            if (!op.equals("between"))
250                throw new Exception("Unknown operator `" + op + "'");
251            if (!skip(ss, " and ("))
252                throw new Exception("Expected ` and (' after between");
253            ValueExp high = parseExp(ss);
254            if (!skip(ss, ")"))
255                throw new Exception("Expected `)' after subexpression");
256            return Query.between(lhs, rhs, high);
257        }
258    }
259
260    private static ValueExp parseExp(String[] ss) throws Exception {
261        final ValueExp prim = parsePrimary(ss);
262
263        /* Look ahead to see if we have an arithmetic operator. */
264        String back = ss[0];
265        if (!skip(ss, " "))
266            return prim;
267        if (ss[0].equals("") || "+-*/".indexOf(ss[0].charAt(0)) < 0) {
268            ss[0] = back;
269            return prim;
270        }
271
272        final String op = scanWord(ss);
273        if (op.length() != 1)
274            throw new Exception("Expected arithmetic operator after space");
275        if ("+-*/".indexOf(op) < 0)
276            throw new Exception("Unknown arithmetic operator: " + op);
277        if (!skip(ss, " "))
278            throw new Exception("Expected space after arithmetic operator");
279        ValueExp rhs = parseExp(ss);
280        switch (op.charAt(0)) {
281        case '+': return Query.plus(prim, rhs);
282        case '-': return Query.minus(prim, rhs);
283        case '*': return Query.times(prim, rhs);
284        case '/': return Query.div(prim, rhs);
285        default: throw new Exception("Can't happen: " + op.charAt(0));
286        }
287    }
288
289    private static ValueExp parsePrimary(String[] ss) throws Exception {
290        String s = ss[0];
291
292        if (s.length() == 0)
293            throw new Exception("Empty string found, expression expected");
294
295        char first = s.charAt(0);
296
297        if (first == ' ')
298            throw new Exception("Space found, expression expected");
299
300        if (first == '-' || Character.isDigit(first))
301            return parseNumberExp(ss);
302
303        if (first == '\'')
304            return parseString(ss);
305
306        if (matchWord(ss, "true"))
307            return Query.value(true);
308
309        if (matchWord(ss, "false"))
310            return Query.value(false);
311
312        if (matchWord(ss, "Class"))
313            return Query.classattr();
314
315        String word = scanWord(ss);
316        int lastDot = word.lastIndexOf('.');
317        if (lastDot < 0)
318            return Query.attr(word);
319        else
320            return Query.attr(word.substring(0, lastDot),
321                              word.substring(lastDot + 1));
322    }
323
324    private static String scanWord(String[] ss) throws Exception {
325        String s = ss[0];
326        int space = s.indexOf(' ');
327        if (space < 0) {
328            ss[0] = "";
329            return s;
330        } else {
331            String word = s.substring(0, space);
332            ss[0] = s.substring(space);
333            return word;
334        }
335    }
336
337    private static boolean matchWord(String[] ss, String word)
338            throws Exception {
339        String s = ss[0];
340        if (s.startsWith(word)) {
341            int len = word.length();
342            if (s.length() == len || s.charAt(len) == ' '
343                || s.charAt(len) == ')') {
344                ss[0] = s.substring(len);
345                return true;
346            }
347        }
348        return false;
349    }
350
351    private static ValueExp parseNumberExp(String[] ss) throws Exception {
352        String s = ss[0];
353        int len = s.length();
354        boolean isFloat = false;
355        int i;
356        for (i = 0; i < len; i++) {
357            char c = s.charAt(i);
358            if (Character.isDigit(c) || c == '-' || c == '+')
359                continue;
360            if (c == '.' || c == 'e' || c == 'E') {
361                isFloat = true;
362                continue;
363            }
364            break;
365        }
366        ss[0] = s.substring(i);
367        s = s.substring(0, i);
368        if (isFloat)
369            return Query.value(Double.parseDouble(s));
370        else
371            return Query.value(Long.parseLong(s));
372    }
373
374    private static ValueExp parseString(String[] ss) throws Exception {
375        if (!skip(ss, "'"))
376            throw new Exception("Expected ' at start of string");
377        String s = ss[0];
378        int len = s.length();
379        StringBuffer buf = new StringBuffer();
380        int i;
381        for (i = 0; i < len; i++) {
382            char c = s.charAt(i);
383            if (c == '\'') {
384                ss[0] = s.substring(i + 1);
385                return Query.value(buf.toString());
386            }
387            if (c == '\\') {
388                if (++i == len)
389                    throw new Exception("\\ at end of string");
390                c = s.charAt(i);
391            }
392            buf.append(c);
393        }
394        throw new Exception("No closing ' at end of string");
395    }
396
397    private static boolean skip(String[] ss, String skip) {
398        if (ss[0].startsWith(skip)) {
399            ss[0] = ss[0].substring(skip.length());
400            return true;
401        } else
402            return false;
403    }
404}
405