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