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 35 import java.util.*; 36 import javax.management.*; 37 38 public 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 }