/* * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Version 1.0, and under the Eclipse Public License, Version 1.0 * (http://h2database.com/html/license.html). */ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.h2.test.trace; import java.math.BigDecimal; import java.util.ArrayList; import org.h2.util.New; import org.h2.util.StringUtils; /** * Parses an entry in a Java-style log file. */ class Parser { private static final int STRING = 0, NAME = 1, NUMBER = 2, SPECIAL = 3; private Player player; private Statement stat; private String line; private String token; private int tokenType; private int pos; private Parser(Player player, String line) { this.player = player; this.line = line; read(); } /** * Parse a Java statement. * * @param player the player * @param line the statement text * @return the statement */ static Statement parseStatement(Player player, String line) { Parser p = new Parser(player, line); p.parseStatement(); return p.stat; } private Statement parseStatement() { stat = new Statement(player); String name = readToken(); Object o = player.getObject(name); if (o == null) { if (readIf(".")) { // example: java.lang.System.exit(0); parseStaticCall(name); } else { // example: Statement s1 = ... stat.setAssign(name, readToken()); read("="); name = readToken(); o = player.getObject(name); if (o != null) { // example: ... = s1.executeQuery(); read("."); parseCall(name, o, readToken()); } else if (readIf(".")) { // ... = x.y.z("..."); parseStaticCall(name); } } } else { // example: s1.execute() read("."); String methodName = readToken(); parseCall(name, o, methodName); } return stat; } private void read() { while (line.charAt(pos) == ' ') { pos++; } int start = pos; char ch = line.charAt(pos); switch (ch) { case '\"': tokenType = STRING; pos++; while (pos < line.length()) { ch = line.charAt(pos); if (ch == '\\') { pos += 2; } else if (ch == '\"') { pos++; break; } else { pos++; } } break; case '.': case ',': case '(': case ')': case ';': case '{': case '}': case '[': case ']': case '=': tokenType = SPECIAL; pos++; break; default: if (Character.isLetter(ch) || ch == '_') { tokenType = NAME; pos++; while (true) { ch = line.charAt(pos); if (Character.isLetterOrDigit(ch) || ch == '_') { pos++; } else { break; } } } else if (ch == '-' || Character.isDigit(ch)) { tokenType = NUMBER; pos++; while (true) { ch = line.charAt(pos); if (Character.isDigit(ch) || ".+-eElLxabcdefABCDEF".indexOf(ch) >= 0) { pos++; } else { break; } } } } token = line.substring(start, pos); } private boolean readIf(String s) { if (token.equals(s)) { read(); return true; } return false; } private String readToken() { String s = token; read(); return s; } private void read(String s) { if (!readIf(s)) { throw new RuntimeException("Expected: " + s + " got: " + token + " in " + line); } } private Arg parseValue() { if (tokenType == STRING) { String s = readToken(); s = StringUtils.javaDecode(s.substring(1, s.length() - 1)); return new Arg(String.class, s); } else if (tokenType == NUMBER) { String number = readToken().toLowerCase(); if (number.endsWith("f")) { Float v = Float.parseFloat(number); return new Arg(float.class, v); } else if (number.endsWith("d") || number.indexOf("e") >= 0 || number.indexOf(".") >= 0) { Double v = Double.parseDouble(number); return new Arg(double.class, v); } else if (number.endsWith("L") || number.endsWith("l")) { Long v = Long.parseLong(number.substring(0, number.length() - 1)); return new Arg(long.class, v); } else { Integer v = Integer.parseInt(number); return new Arg(int.class, v); } } else if (tokenType == NAME) { if (readIf("true")) { return new Arg(boolean.class, Boolean.TRUE); } else if (readIf("false")) { return new Arg(boolean.class, Boolean.FALSE); } else if (readIf("null")) { throw new RuntimeException( "Null: class not specified. Example: (java.lang.String)null"); } else if (readIf("new")) { if (readIf("String")) { read("["); read("]"); read("{"); ArrayList<Object> values = New.arrayList(); do { values.add(parseValue().getValue()); } while (readIf(",")); read("}"); String[] list = new String[values.size()]; values.toArray(list); return new Arg(String[].class, list); } else if (readIf("BigDecimal")) { read("("); BigDecimal value = new BigDecimal((String) parseValue().getValue()); read(")"); return new Arg(BigDecimal.class, value); } else { throw new RuntimeException("Unsupported constructor: " + readToken()); } } String name = readToken(); Object obj = player.getObject(name); if (obj != null) { return new Arg(obj.getClass(), obj); } read("."); Statement outer = stat; stat = new Statement(player); parseStaticCall(name); Arg s = new Arg(stat); stat = outer; return s; } else if (readIf("(")) { read("short"); read(")"); String number = readToken(); return new Arg(short.class, Short.parseShort(number)); } else { throw new RuntimeException("Value expected, got: " + readToken() + " in " + line); } } private void parseCall(String objectName, Object o, String methodName) { stat.setMethodCall(objectName, o, methodName); ArrayList<Arg> args = New.arrayList(); read("("); while (true) { if (readIf(")")) { break; } Arg p = parseValue(); args.add(p); if (readIf(")")) { break; } read(","); } stat.setArgs(args); } private void parseStaticCall(String clazz) { String last = readToken(); while (readIf(".")) { clazz += last == null ? "" : "." + last; last = readToken(); } String methodName = last; stat.setStaticCall(clazz); parseCall(null, null, methodName); } }