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