0N/A/*
2362N/A * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A/*
0N/A * @test
0N/A * @bug 4886011
0N/A * @summary Test that QueryExp.toString() is reversible
0N/A * @author Eamonn McManus
0N/A * @run clean QueryExpStringTest
0N/A * @run build QueryExpStringTest
0N/A * @run main QueryExpStringTest
0N/A */
0N/A
0N/Aimport java.util.*;
0N/Aimport javax.management.*;
0N/A
0N/Apublic class QueryExpStringTest {
0N/A
0N/A private static final ValueExp
0N/A attr = Query.attr("attr"),
0N/A qattr = Query.attr("className", "attr"),
11N/A aa = Query.attr("A"),
11N/A bb = Query.attr("B"),
11N/A cc = Query.attr("C"),
11N/A dd = Query.attr("D"),
11N/A zero = Query.value(0),
0N/A classattr = Query.classattr(),
0N/A simpleString = Query.value("simpleString"),
0N/A complexString = Query.value("a'b\\'\""),
0N/A intValue = Query.value(12345678),
0N/A integerValue = Query.value(new Integer(12345678)),
0N/A longValue = Query.value(12345678L),
0N/A floatValue = Query.value(2.5f),
0N/A doubleValue = Query.value(2.5d),
0N/A booleanValue = Query.value(true),
0N/A plusValue = Query.plus(intValue, integerValue),
0N/A timesValue = Query.times(doubleValue, floatValue),
0N/A minusValue = Query.minus(floatValue, doubleValue),
0N/A divValue = Query.div(doubleValue, floatValue);
0N/A
0N/A private static final QueryExp
0N/A gt = Query.gt(intValue, floatValue),
0N/A geq = Query.geq(intValue, floatValue),
0N/A leq = Query.leq(intValue, floatValue),
0N/A lt = Query.lt(intValue, floatValue),
0N/A eq = Query.eq(intValue, floatValue),
0N/A between = Query.between(intValue, floatValue, doubleValue),
0N/A match = Query.match((AttributeValueExp) attr,
0N/A (StringValueExp) simpleString),
0N/A initial = Query.initialSubString((AttributeValueExp) attr,
0N/A (StringValueExp) simpleString),
0N/A initialStar = Query.initialSubString((AttributeValueExp) attr,
0N/A Query.value("*")),
11N/A initialPercent = Query.initialSubString((AttributeValueExp) attr,
11N/A Query.value("%")),
0N/A any = Query.anySubString((AttributeValueExp) attr,
0N/A (StringValueExp) simpleString),
0N/A anyStar = Query.anySubString((AttributeValueExp) attr,
0N/A Query.value("*")),
11N/A anyPercent = Query.anySubString((AttributeValueExp) attr,
11N/A Query.value("%")),
0N/A ffinal = Query.finalSubString((AttributeValueExp) attr,
0N/A (StringValueExp) simpleString),
0N/A finalMagic = Query.finalSubString((AttributeValueExp) attr,
0N/A Query.value("?*[\\")),
0N/A in = Query.in(intValue, new ValueExp[] {intValue, floatValue}),
0N/A and = Query.and(gt, lt),
0N/A or = Query.or(gt, lt),
11N/A not = Query.not(gt),
11N/A aPlusB_PlusC = Query.gt(Query.plus(Query.plus(aa, bb), cc), zero),
11N/A aPlus_BPlusC = Query.gt(Query.plus(aa, Query.plus(bb, cc)), zero);
0N/A
0N/A // Commented-out tests below require change to implementation
0N/A
0N/A private static final Object tests[] = {
0N/A attr, "attr",
11N/A// qattr, "className.attr",
11N/A// Preceding form now appears as className#attr, an incompatible change
11N/A// which we don't mind much because nobody uses the two-arg Query.attr.
0N/A classattr, "Class",
0N/A simpleString, "'simpleString'",
11N/A complexString, "'a''b\\\''\"'",
0N/A intValue, "12345678",
0N/A integerValue, "12345678",
0N/A longValue, "12345678",
0N/A floatValue, "2.5",
0N/A doubleValue, "2.5",
0N/A booleanValue, "true",
0N/A plusValue, "12345678 + 12345678",
0N/A timesValue, "2.5 * 2.5",
0N/A minusValue, "2.5 - 2.5",
0N/A divValue, "2.5 / 2.5",
0N/A gt, "(12345678) > (2.5)",
0N/A geq, "(12345678) >= (2.5)",
0N/A leq, "(12345678) <= (2.5)",
0N/A lt, "(12345678) < (2.5)",
0N/A eq, "(12345678) = (2.5)",
0N/A between, "(12345678) between (2.5) and (2.5)",
0N/A match, "attr like 'simpleString'",
352N/A initial, "attr like 'simpleString*'",
352N/A initialStar, "attr like '\\**'",
352N/A initialPercent, "attr like '%*'",
352N/A any, "attr like '*simpleString*'",
352N/A anyStar, "attr like '*\\**'",
352N/A anyPercent, "attr like '*%*'",
352N/A ffinal, "attr like '*simpleString'",
352N/A finalMagic, "attr like '*\\?\\*\\[\\\\'",
0N/A in, "12345678 in (12345678, 2.5)",
0N/A and, "((12345678) > (2.5)) and ((12345678) < (2.5))",
0N/A or, "((12345678) > (2.5)) or ((12345678) < (2.5))",
0N/A not, "not ((12345678) > (2.5))",
11N/A aPlusB_PlusC, "(A + B + C) > (0)",
11N/A// aPlus_BPlusC, "(A + (B + C)) > (0)",
0N/A };
0N/A
0N/A public static void main(String[] args) throws Exception {
0N/A System.out.println("Testing QueryExp.toString()");
0N/A
0N/A boolean ok = true;
0N/A
0N/A for (int i = 0; i < tests.length; i += 2) {
0N/A String testString = tests[i].toString();
0N/A String expected = (String) tests[i + 1];
0N/A if (expected.equals(testString))
0N/A System.out.println("OK: " + expected);
0N/A else {
0N/A System.err.println("Expected: {" + expected + "}; got: {" +
0N/A testString + "}");
0N/A ok = false;
0N/A }
0N/A
0N/A try {
0N/A Object parsed;
0N/A String[] expectedref = new String[] {expected};
0N/A if (tests[i] instanceof ValueExp)
0N/A parsed = parseExp(expectedref);
0N/A else
0N/A parsed = parseQuery(expectedref);
0N/A if (expectedref[0].length() > 0)
0N/A throw new Exception("Junk after parse: " + expectedref[0]);
0N/A String parsedString = parsed.toString();
0N/A if (parsedString.equals(expected))
0N/A System.out.println("OK: parsed " + parsedString);
0N/A else {
0N/A System.err.println("Parse differs: expected: {" +
0N/A expected + "}; got: {" +
0N/A parsedString + "}");
0N/A ok = false;
0N/A }
0N/A } catch (Exception e) {
0N/A System.err.println("Parse got exception: {" + expected +
0N/A "}: " + e);
0N/A ok = false;
0N/A }
0N/A }
0N/A
0N/A if (ok)
0N/A System.out.println("Test passed");
0N/A else {
0N/A System.out.println("TEST FAILED");
0N/A System.exit(1);
0N/A }
0N/A }
0N/A
0N/A private static QueryExp parseQuery(String[] ss) throws Exception {
0N/A if (skip(ss, "("))
0N/A return parseQueryAfterParen(ss);
0N/A
0N/A if (skip(ss, "not (")) {
0N/A QueryExp not = parseQuery(ss);
0N/A if (!skip(ss, ")"))
0N/A throw new Exception("Expected ) after not (...");
0N/A return Query.not(not);
0N/A }
0N/A
0N/A ValueExp exp = parseExp(ss);
0N/A
0N/A if (skip(ss, " like ")) {
0N/A ValueExp pat = parseExp(ss);
0N/A if (!(exp instanceof AttributeValueExp &&
0N/A pat instanceof StringValueExp)) {
0N/A throw new Exception("Expected types `attr like string': " +
0N/A exp + " like " + pat);
0N/A }
11N/A StringValueExp spat = (StringValueExp) pat;
11N/A return Query.match((AttributeValueExp) exp, spat);
0N/A }
0N/A
0N/A if (skip(ss, " in (")) {
0N/A List values = new ArrayList();
0N/A if (!skip(ss, ")")) {
0N/A do {
0N/A values.add(parseExp(ss));
0N/A } while (skip(ss, ", "));
0N/A if (!skip(ss, ")"))
0N/A throw new Exception("Expected ) after in (...");
0N/A }
0N/A return Query.in(exp, (ValueExp[]) values.toArray(new ValueExp[0]));
0N/A }
0N/A
0N/A throw new Exception("Expected in or like after expression");
0N/A }
0N/A
0N/A private static QueryExp parseQueryAfterParen(String[] ss)
0N/A throws Exception {
0N/A /* This is very ugly. We might have "(q1) and (q2)" here, or
0N/A we might have "(e1) < (e2)". Since the syntax for a query
0N/A (q1) is not the same as for an expression (e1), but can
0N/A begin with one, we try to parse the query, and if we get an
0N/A exception we then try to parse an expression. It's a hacky
0N/A kind of look-ahead. */
0N/A String start = ss[0];
0N/A try {
0N/A QueryExp lhs = parseQuery(ss);
0N/A QueryExp result;
0N/A
0N/A if (skip(ss, ") and ("))
0N/A result = Query.and(lhs, parseQuery(ss));
0N/A else if (skip(ss, ") or ("))
0N/A result = Query.or(lhs, parseQuery(ss));
0N/A else
0N/A throw new Exception("Expected `) and/or ('");
0N/A if (!skip(ss, ")"))
0N/A throw new Exception("Expected `)' after subquery");
0N/A return result;
0N/A } catch (Exception e) {
0N/A ss[0] = start;
0N/A ValueExp lhs = parseExp(ss);
0N/A if (!skip(ss, ") "))
11N/A throw new Exception("Expected `) ' after subexpression: " + ss[0]);
0N/A String op = scanWord(ss);
0N/A if (!skip(ss, " ("))
0N/A throw new Exception("Expected ` (' after `" + op + "'");
0N/A ValueExp rhs = parseExp(ss);
0N/A if (!skip(ss, ")"))
0N/A throw new Exception("Expected `)' after subexpression");
0N/A if (op.equals("="))
0N/A return Query.eq(lhs, rhs);
0N/A if (op.equals("<"))
0N/A return Query.lt(lhs, rhs);
0N/A if (op.equals(">"))
0N/A return Query.gt(lhs, rhs);
0N/A if (op.equals("<="))
0N/A return Query.leq(lhs, rhs);
0N/A if (op.equals(">="))
0N/A return Query.geq(lhs, rhs);
0N/A if (!op.equals("between"))
0N/A throw new Exception("Unknown operator `" + op + "'");
0N/A if (!skip(ss, " and ("))
0N/A throw new Exception("Expected ` and (' after between");
0N/A ValueExp high = parseExp(ss);
0N/A if (!skip(ss, ")"))
0N/A throw new Exception("Expected `)' after subexpression");
0N/A return Query.between(lhs, rhs, high);
0N/A }
0N/A }
0N/A
0N/A private static ValueExp parseExp(String[] ss) throws Exception {
11N/A ValueExp lhs = parsePrimary(ss);
0N/A
11N/A while (true) {
0N/A /* Look ahead to see if we have an arithmetic operator. */
0N/A String back = ss[0];
0N/A if (!skip(ss, " "))
11N/A return lhs;
0N/A if (ss[0].equals("") || "+-*/".indexOf(ss[0].charAt(0)) < 0) {
0N/A ss[0] = back;
11N/A return lhs;
0N/A }
0N/A
0N/A final String op = scanWord(ss);
0N/A if (op.length() != 1)
0N/A throw new Exception("Expected arithmetic operator after space");
0N/A if ("+-*/".indexOf(op) < 0)
0N/A throw new Exception("Unknown arithmetic operator: " + op);
0N/A if (!skip(ss, " "))
0N/A throw new Exception("Expected space after arithmetic operator");
11N/A ValueExp rhs = parsePrimary(ss);
0N/A switch (op.charAt(0)) {
11N/A case '+': lhs = Query.plus(lhs, rhs); break;
11N/A case '-': lhs = Query.minus(lhs, rhs); break;
11N/A case '*': lhs = Query.times(lhs, rhs); break;
11N/A case '/': lhs = Query.div(lhs, rhs); break;
0N/A default: throw new Exception("Can't happen: " + op.charAt(0));
0N/A }
0N/A }
11N/A }
0N/A
0N/A private static ValueExp parsePrimary(String[] ss) throws Exception {
0N/A String s = ss[0];
0N/A
0N/A if (s.length() == 0)
0N/A throw new Exception("Empty string found, expression expected");
0N/A
0N/A char first = s.charAt(0);
0N/A
0N/A if (first == ' ')
0N/A throw new Exception("Space found, expression expected");
0N/A
0N/A if (first == '-' || Character.isDigit(first))
0N/A return parseNumberExp(ss);
0N/A
0N/A if (first == '\'')
0N/A return parseString(ss);
0N/A
0N/A if (matchWord(ss, "true"))
0N/A return Query.value(true);
0N/A
0N/A if (matchWord(ss, "false"))
0N/A return Query.value(false);
0N/A
0N/A if (matchWord(ss, "Class"))
0N/A return Query.classattr();
0N/A
0N/A String word = scanWord(ss);
0N/A int lastDot = word.lastIndexOf('.');
0N/A if (lastDot < 0)
0N/A return Query.attr(word);
0N/A else
0N/A return Query.attr(word.substring(0, lastDot),
0N/A word.substring(lastDot + 1));
0N/A }
0N/A
0N/A private static String scanWord(String[] ss) throws Exception {
0N/A String s = ss[0];
0N/A int space = s.indexOf(' ');
11N/A int rpar = s.indexOf(')');
11N/A if (space < 0 && rpar < 0) {
0N/A ss[0] = "";
0N/A return s;
0N/A }
11N/A int stop;
11N/A if (space >= 0 && rpar >= 0) // string has both space and ), stop at first
11N/A stop = Math.min(space, rpar);
11N/A else // string has only one, stop at it
11N/A stop = Math.max(space, rpar);
11N/A String word = s.substring(0, stop);
11N/A ss[0] = s.substring(stop);
11N/A return word;
0N/A }
0N/A
0N/A private static boolean matchWord(String[] ss, String word)
0N/A throws Exception {
0N/A String s = ss[0];
0N/A if (s.startsWith(word)) {
0N/A int len = word.length();
0N/A if (s.length() == len || s.charAt(len) == ' '
0N/A || s.charAt(len) == ')') {
0N/A ss[0] = s.substring(len);
0N/A return true;
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A private static ValueExp parseNumberExp(String[] ss) throws Exception {
0N/A String s = ss[0];
0N/A int len = s.length();
0N/A boolean isFloat = false;
0N/A int i;
0N/A for (i = 0; i < len; i++) {
0N/A char c = s.charAt(i);
0N/A if (Character.isDigit(c) || c == '-' || c == '+')
0N/A continue;
0N/A if (c == '.' || c == 'e' || c == 'E') {
0N/A isFloat = true;
0N/A continue;
0N/A }
0N/A break;
0N/A }
0N/A ss[0] = s.substring(i);
0N/A s = s.substring(0, i);
0N/A if (isFloat)
0N/A return Query.value(Double.parseDouble(s));
0N/A else
0N/A return Query.value(Long.parseLong(s));
0N/A }
0N/A
0N/A private static ValueExp parseString(String[] ss) throws Exception {
0N/A if (!skip(ss, "'"))
0N/A throw new Exception("Expected ' at start of string");
0N/A String s = ss[0];
0N/A int len = s.length();
0N/A StringBuffer buf = new StringBuffer();
0N/A int i;
0N/A for (i = 0; i < len; i++) {
0N/A char c = s.charAt(i);
0N/A if (c == '\'') {
11N/A ++i;
11N/A if (i >= len || s.charAt(i) != '\'') {
11N/A ss[0] = s.substring(i);
0N/A return Query.value(buf.toString());
0N/A }
0N/A }
0N/A buf.append(c);
0N/A }
0N/A throw new Exception("No closing ' at end of string");
0N/A }
0N/A
0N/A private static boolean skip(String[] ss, String skip) {
0N/A if (ss[0].startsWith(skip)) {
0N/A ss[0] = ss[0].substring(skip.length());
0N/A return true;
0N/A } else
0N/A return false;
0N/A }
0N/A}