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