QueryExpStringTest.java revision 11884:b45c81ca8671
1/*
2 * Copyright (c) 2003, 2015, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 4886011
27 * @summary Test that QueryExp.toString() is reversible
28 * @author Eamonn McManus
29 * @modules java.management
30 * @run clean QueryExpStringTest
31 * @run build QueryExpStringTest
32 * @run main QueryExpStringTest
33 */
34
35import java.util.*;
36import javax.management.*;
37
38public class QueryExpStringTest {
39
40    private static final ValueExp
41        attr = Query.attr("attr"),
42        qattr = Query.attr("className", "attr"),
43        aa = Query.attr("A"),
44        bb = Query.attr("B"),
45        cc = Query.attr("C"),
46        dd = Query.attr("D"),
47        zero = Query.value(0),
48        classattr = Query.classattr(),
49        simpleString = Query.value("simpleString"),
50        complexString = Query.value("a'b\\'\""),
51        intValue = Query.value(12345678),
52        integerValue = Query.value(new Integer(12345678)),
53        longValue = Query.value(12345678L),
54        floatValue = Query.value(2.5f),
55        doubleValue = Query.value(2.5d),
56        booleanValue = Query.value(true),
57        plusValue = Query.plus(intValue, integerValue),
58        timesValue = Query.times(doubleValue, floatValue),
59        minusValue = Query.minus(floatValue, doubleValue),
60        divValue = Query.div(doubleValue, floatValue);
61
62    private static final QueryExp
63        gt = Query.gt(intValue, floatValue),
64        geq = Query.geq(intValue, floatValue),
65        leq = Query.leq(intValue, floatValue),
66        lt = Query.lt(intValue, floatValue),
67        eq = Query.eq(intValue, floatValue),
68        between = Query.between(intValue, floatValue, doubleValue),
69        match = Query.match((AttributeValueExp) attr,
70                            (StringValueExp) simpleString),
71        initial = Query.initialSubString((AttributeValueExp) attr,
72                                         (StringValueExp) simpleString),
73        initialStar = Query.initialSubString((AttributeValueExp) attr,
74                                             Query.value("*")),
75        initialPercent = Query.initialSubString((AttributeValueExp) attr,
76                                                Query.value("%")),
77        any = Query.anySubString((AttributeValueExp) attr,
78                                 (StringValueExp) simpleString),
79        anyStar = Query.anySubString((AttributeValueExp) attr,
80                                     Query.value("*")),
81        anyPercent = Query.anySubString((AttributeValueExp) attr,
82                                        Query.value("%")),
83        ffinal = Query.finalSubString((AttributeValueExp) attr,
84                                      (StringValueExp) simpleString),
85        finalMagic = Query.finalSubString((AttributeValueExp) attr,
86                                          Query.value("?*[\\")),
87        in = Query.in(intValue, new ValueExp[] {intValue, floatValue}),
88        and = Query.and(gt, lt),
89        or = Query.or(gt, lt),
90        not = Query.not(gt),
91        aPlusB_PlusC = Query.gt(Query.plus(Query.plus(aa, bb), cc), zero),
92        aPlus_BPlusC = Query.gt(Query.plus(aa, Query.plus(bb, cc)), zero);
93
94    // Commented-out tests below require change to implementation
95
96    private static final Object tests[] = {
97        attr, "attr",
98//      qattr, "className.attr",
99// Preceding form now appears as className#attr, an incompatible change
100// which we don't mind much because nobody uses the two-arg Query.attr.
101        classattr, "Class",
102        simpleString, "'simpleString'",
103        complexString, "'a''b\\\''\"'",
104        intValue, "12345678",
105        integerValue, "12345678",
106        longValue, "12345678",
107        floatValue, "2.5",
108        doubleValue, "2.5",
109        booleanValue, "true",
110        plusValue, "12345678 + 12345678",
111        timesValue, "2.5 * 2.5",
112        minusValue, "2.5 - 2.5",
113        divValue, "2.5 / 2.5",
114        gt, "(12345678) > (2.5)",
115        geq, "(12345678) >= (2.5)",
116        leq, "(12345678) <= (2.5)",
117        lt, "(12345678) < (2.5)",
118        eq, "(12345678) = (2.5)",
119        between, "(12345678) between (2.5) and (2.5)",
120        match, "attr like 'simpleString'",
121        initial, "attr like 'simpleString*'",
122        initialStar, "attr like '\\**'",
123        initialPercent, "attr like '%*'",
124        any, "attr like '*simpleString*'",
125        anyStar, "attr like '*\\**'",
126        anyPercent, "attr like '*%*'",
127        ffinal, "attr like '*simpleString'",
128        finalMagic, "attr like '*\\?\\*\\[\\\\'",
129        in, "12345678 in (12345678, 2.5)",
130        and, "((12345678) > (2.5)) and ((12345678) < (2.5))",
131        or, "((12345678) > (2.5)) or ((12345678) < (2.5))",
132        not, "not ((12345678) > (2.5))",
133        aPlusB_PlusC, "(A + B + C) > (0)",
134//        aPlus_BPlusC, "(A + (B + C)) > (0)",
135    };
136
137    public static void main(String[] args) throws Exception {
138        System.out.println("Testing QueryExp.toString()");
139
140        boolean ok = true;
141
142        for (int i = 0; i < tests.length; i += 2) {
143            String testString = tests[i].toString();
144            String expected = (String) tests[i + 1];
145            if (expected.equals(testString))
146                System.out.println("OK: " + expected);
147            else {
148                System.err.println("Expected: {" + expected + "}; got: {" +
149                                   testString + "}");
150                ok = false;
151            }
152
153            try {
154                Object parsed;
155                String[] expectedref = new String[] {expected};
156                if (tests[i] instanceof ValueExp)
157                    parsed = parseExp(expectedref);
158                else
159                    parsed = parseQuery(expectedref);
160                if (expectedref[0].length() > 0)
161                    throw new Exception("Junk after parse: " + expectedref[0]);
162                String parsedString = parsed.toString();
163                if (parsedString.equals(expected))
164                    System.out.println("OK: parsed " + parsedString);
165                else {
166                    System.err.println("Parse differs: expected: {" +
167                                       expected + "}; got: {" +
168                                       parsedString + "}");
169                    ok = false;
170                }
171            } catch (Exception e) {
172                System.err.println("Parse got exception: {" + expected +
173                                   "}: " + e);
174                ok = false;
175            }
176        }
177
178        if (ok)
179            System.out.println("Test passed");
180        else {
181            System.out.println("TEST FAILED");
182            System.exit(1);
183        }
184    }
185
186    private static QueryExp parseQuery(String[] ss) throws Exception {
187        if (skip(ss, "("))
188            return parseQueryAfterParen(ss);
189
190        if (skip(ss, "not (")) {
191            QueryExp not = parseQuery(ss);
192            if (!skip(ss, ")"))
193                throw new Exception("Expected ) after not (...");
194            return Query.not(not);
195        }
196
197        ValueExp exp = parseExp(ss);
198
199        if (skip(ss, " like ")) {
200            ValueExp pat = parseExp(ss);
201            if (!(exp instanceof AttributeValueExp &&
202                  pat instanceof StringValueExp)) {
203                throw new Exception("Expected types `attr like string': " +
204                                    exp + " like " + pat);
205            }
206            StringValueExp spat = (StringValueExp) pat;
207            return Query.match((AttributeValueExp) exp, spat);
208        }
209
210        if (skip(ss, " in (")) {
211            List values = new ArrayList();
212            if (!skip(ss, ")")) {
213                do {
214                    values.add(parseExp(ss));
215                } while (skip(ss, ", "));
216                if (!skip(ss, ")"))
217                    throw new Exception("Expected ) after in (...");
218            }
219            return Query.in(exp, (ValueExp[]) values.toArray(new ValueExp[0]));
220        }
221
222        throw new Exception("Expected in or like after expression");
223    }
224
225    private static QueryExp parseQueryAfterParen(String[] ss)
226            throws Exception {
227        /* This is very ugly.  We might have "(q1) and (q2)" here, or
228           we might have "(e1) < (e2)".  Since the syntax for a query
229           (q1) is not the same as for an expression (e1), but can
230           begin with one, we try to parse the query, and if we get an
231           exception we then try to parse an expression.  It's a hacky
232           kind of look-ahead.  */
233        String start = ss[0];
234        try {
235            QueryExp lhs = parseQuery(ss);
236            QueryExp result;
237
238            if (skip(ss, ") and ("))
239                result = Query.and(lhs, parseQuery(ss));
240            else if (skip(ss, ") or ("))
241                result = Query.or(lhs, parseQuery(ss));
242            else
243                throw new Exception("Expected `) and/or ('");
244            if (!skip(ss, ")"))
245                throw new Exception("Expected `)' after subquery");
246            return result;
247        } catch (Exception e) {
248            ss[0] = start;
249            ValueExp lhs = parseExp(ss);
250            if (!skip(ss, ") "))
251                throw new Exception("Expected `) ' after subexpression: " + ss[0]);
252            String op = scanWord(ss);
253            if (!skip(ss, " ("))
254                throw new Exception("Expected ` (' after `" + op + "'");
255            ValueExp rhs = parseExp(ss);
256            if (!skip(ss, ")"))
257                throw new Exception("Expected `)' after subexpression");
258            if (op.equals("="))
259                return Query.eq(lhs, rhs);
260            if (op.equals("<"))
261                return Query.lt(lhs, rhs);
262            if (op.equals(">"))
263                return Query.gt(lhs, rhs);
264            if (op.equals("<="))
265                return Query.leq(lhs, rhs);
266            if (op.equals(">="))
267                return Query.geq(lhs, rhs);
268            if (!op.equals("between"))
269                throw new Exception("Unknown operator `" + op + "'");
270            if (!skip(ss, " and ("))
271                throw new Exception("Expected ` and (' after between");
272            ValueExp high = parseExp(ss);
273            if (!skip(ss, ")"))
274                throw new Exception("Expected `)' after subexpression");
275            return Query.between(lhs, rhs, high);
276        }
277    }
278
279    private static ValueExp parseExp(String[] ss) throws Exception {
280        ValueExp lhs = parsePrimary(ss);
281
282        while (true) {
283        /* Look ahead to see if we have an arithmetic operator. */
284        String back = ss[0];
285        if (!skip(ss, " "))
286                return lhs;
287        if (ss[0].equals("") || "+-*/".indexOf(ss[0].charAt(0)) < 0) {
288            ss[0] = back;
289                return lhs;
290        }
291
292        final String op = scanWord(ss);
293        if (op.length() != 1)
294            throw new Exception("Expected arithmetic operator after space");
295        if ("+-*/".indexOf(op) < 0)
296            throw new Exception("Unknown arithmetic operator: " + op);
297        if (!skip(ss, " "))
298            throw new Exception("Expected space after arithmetic operator");
299            ValueExp rhs = parsePrimary(ss);
300        switch (op.charAt(0)) {
301            case '+': lhs = Query.plus(lhs, rhs); break;
302            case '-': lhs = Query.minus(lhs, rhs); break;
303            case '*': lhs = Query.times(lhs, rhs); break;
304            case '/': lhs = Query.div(lhs, rhs); break;
305        default: throw new Exception("Can't happen: " + op.charAt(0));
306        }
307    }
308    }
309
310    private static ValueExp parsePrimary(String[] ss) throws Exception {
311        String s = ss[0];
312
313        if (s.length() == 0)
314            throw new Exception("Empty string found, expression expected");
315
316        char first = s.charAt(0);
317
318        if (first == ' ')
319            throw new Exception("Space found, expression expected");
320
321        if (first == '-' || Character.isDigit(first))
322            return parseNumberExp(ss);
323
324        if (first == '\'')
325            return parseString(ss);
326
327        if (matchWord(ss, "true"))
328            return Query.value(true);
329
330        if (matchWord(ss, "false"))
331            return Query.value(false);
332
333        if (matchWord(ss, "Class"))
334            return Query.classattr();
335
336        String word = scanWord(ss);
337        int lastDot = word.lastIndexOf('.');
338        if (lastDot < 0)
339            return Query.attr(word);
340        else
341            return Query.attr(word.substring(0, lastDot),
342                              word.substring(lastDot + 1));
343    }
344
345    private static String scanWord(String[] ss) throws Exception {
346        String s = ss[0];
347        int space = s.indexOf(' ');
348        int rpar = s.indexOf(')');
349        if (space < 0 && rpar < 0) {
350            ss[0] = "";
351            return s;
352        }
353        int stop;
354        if (space >= 0 && rpar >= 0)  // string has both space and ), stop at first
355            stop = Math.min(space, rpar);
356        else                          // string has only one, stop at it
357            stop = Math.max(space, rpar);
358        String word = s.substring(0, stop);
359        ss[0] = s.substring(stop);
360        return word;
361    }
362
363    private static boolean matchWord(String[] ss, String word)
364            throws Exception {
365        String s = ss[0];
366        if (s.startsWith(word)) {
367            int len = word.length();
368            if (s.length() == len || s.charAt(len) == ' '
369                || s.charAt(len) == ')') {
370                ss[0] = s.substring(len);
371                return true;
372            }
373        }
374        return false;
375    }
376
377    private static ValueExp parseNumberExp(String[] ss) throws Exception {
378        String s = ss[0];
379        int len = s.length();
380        boolean isFloat = false;
381        int i;
382        for (i = 0; i < len; i++) {
383            char c = s.charAt(i);
384            if (Character.isDigit(c) || c == '-' || c == '+')
385                continue;
386            if (c == '.' || c == 'e' || c == 'E') {
387                isFloat = true;
388                continue;
389            }
390            break;
391        }
392        ss[0] = s.substring(i);
393        s = s.substring(0, i);
394        if (isFloat)
395            return Query.value(Double.parseDouble(s));
396        else
397            return Query.value(Long.parseLong(s));
398    }
399
400    private static ValueExp parseString(String[] ss) throws Exception {
401        if (!skip(ss, "'"))
402            throw new Exception("Expected ' at start of string");
403        String s = ss[0];
404        int len = s.length();
405        StringBuffer buf = new StringBuffer();
406        int i;
407        for (i = 0; i < len; i++) {
408            char c = s.charAt(i);
409            if (c == '\'') {
410                ++i;
411                if (i >= len || s.charAt(i) != '\'') {
412                    ss[0] = s.substring(i);
413                return Query.value(buf.toString());
414            }
415            }
416            buf.append(c);
417        }
418        throw new Exception("No closing ' at end of string");
419    }
420
421    private static boolean skip(String[] ss, String skip) {
422        if (ss[0].startsWith(skip)) {
423            ss[0] = ss[0].substring(skip.length());
424            return true;
425        } else
426            return false;
427    }
428}
429