/* // $Id: //guest/julian_hyde/saffron/src/main/saffron/Statement.java#3 $ // Saffron preprocessor and data engine // Copyright (C) 2002 Julian Hyde <julian.hyde@mail.com> // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the // Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307, USA. // // See the COPYING file located in the top-level-directory of // the archive of this library for complete text of license. */ package saffron; import openjava.mop.*; import openjava.ptree.*; import openjava.ptree.util.PartialParser; import openjava.ptree.util.QueryExpander; import openjava.ptree.util.SyntheticClass; import openjava.ojc.JavaCompiler; import openjava.tools.DebugOut; import saffron.rel.SaffronQueryExpander; import saffron.util.ClassCollector; import saffron.util.SaffronSchemaExpander; import saffron.util.Util; import saffron.util.SaffronValidator; import saffron.runtime.BufferedIterator; import saffron.runtime.EnumerationIterator; import saffron.runtime.Iterable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Vector; import java.util.StringTokenizer; import java.util.Iterator; import java.util.Enumeration; import java.io.PrintStream; import java.io.File; import java.io.FileWriter; /** * A <code>Statement</code> is used to execute a saffron (or regular Java) * expression dynamically. **/ public class Statement { public Statement() {} /** * Executes a query string, passing in a set of arguments, and returns the * result. * * @param queryString expression to execute. (Although this method is * intended to allow execution of relational expressions, * <code>queryString</code> can actually be any valid Java expression, * for example <code>1 + 2</code>.) * @param arguments a set of name/value pairs to pass to the expression. * @return the result of evaluating the expression. If the expression is * relational, the return is a {@link java.util.Enumeration} * * <p>Example:<blockquote> * <pre>Sales sales = new Sales("jdbc:odbc:Northwind"); * int maxSalary = 10000; * Statement statement = new Statement(); * statement.execute( * "select * from sales.emp where emp.sal > maxSalary", * this.class, * new Argument[] { * new Argument("sales", sales), * new Argument("maxSalary", maxSalary)});</pre> * </blockquote> **/ public Object execute(String queryString, Argument[] arguments) { // (re)load trace level etc. from saffron.properties Util.applyProperties(); Environment env = OJSystem.env; String packageName = getTempPackageName(), className = "Dummy_" + Integer.toHexString(this.hashCode()); env = new FileEnvironment(env, packageName, className); ClassDeclaration decl = new ClassDeclaration( new ModifierList(ModifierList.PUBLIC), className, null, null, new MemberDeclarationList()); OJClass clazz = new OJClass(env, null, decl); env.record(clazz.getName(), clazz); env = new ClosedEnvironment(clazz.getEnvironment()); if (arguments != null && arguments.length > 0) { for (int i = 0; i < arguments.length; i++) { Argument argument = arguments[i]; if (argument.value instanceof Enumeration) { argument.value = new EnumerationIterator( (Enumeration) argument.value); argument.clazz = argument.value.getClass(); } if (argument.value instanceof Iterator && !(argument.value instanceof Iterable)) { argument.value = new BufferedIterator( (Iterator) argument.value); argument.clazz = argument.value.getClass(); } argument.clazz = visibleBaseClass( argument.clazz, packageName, className); env.bindVariable( argument.name, OJClass.forClass(argument.clazz)); } } Expression expression; try { expression = PartialParser.makeExpression( env, "(" + queryString + ")"); } catch (MOPException e) { throw Util.newInternal( e, "while parsing [" + queryString + "]"); } SaffronSchemaExpander schemaExpander = new SaffronSchemaExpander(env); expression = Util.go(schemaExpander, expression); SaffronValidator validator = new SaffronValidator(env); expression = Util.go(validator, expression); QueryExpander queryExpander = new SaffronQueryExpander(env); expression = Util.go(queryExpander, expression); return evaluate(decl,env,expression,arguments); } /** * Returns the lowest ancestor of <code>clazz</code> which is visible from * <code>fromPackage</code>.<code>fromClazz</code>. */ private static Class visibleBaseClass( Class clazz, String fromPackageName, String fromClassName) { // String fromClassFullName; // if (fromPackageName == null || fromPackageName.equals("")) { // fromClassFullName = fromClassName; // } else { // fromClassFullName = fromPackageName + "." + fromClassFullName; // } for (Class c = clazz; c != null; c = c.getSuperclass()) { int modifiers = c.getModifiers(); if (Modifier.isPublic(modifiers)) { return c; } Package pakkage = c.getPackage(); if (pakkage == null) { pakkage = Object.class.getPackage(); } if (!Modifier.isPrivate(modifiers) && pakkage.getName().equals(fromPackageName)) { return c; } } return java.lang.Object.class; } private static String getTempPackageName() { return Util.getProperties().getProperty( "saffron.package.name", "saffron.runtime"); } private static String getJavaRoot() { return Util.getProperties().getProperty( "saffron.java.dir", getClassRoot()); } private static String getClassRoot() { String classRoot = Util.getProperties().getProperty( "saffron.class.dir"); if (classRoot == null) { throw Util.newInternal("Property saffron.class.dir must be set"); } return classRoot; } private static String getCompilerClassName() { return Util.getProperties().getProperty( "saffron.java.compiler.class", "JP.ac.tsukuba.openjava.SunJavaCompiler"); } private static String[] getCompilerArgs(String[] args) { // should contain at least "-classpath ...;d:/saffron/classes -d d:/saffron/classes", maybe "-verbose" String initialArgs = Util.getProperties().getProperty( "saffron.java.compiler.args"); Vector v = new Vector(); StringTokenizer tok = new StringTokenizer(initialArgs); while (tok.hasMoreTokens()) { v.addElement(tok.nextToken()); } for (int i = 0; i < args.length; i++) { v.addElement(args[i]); } String[] fullArgs = new String[v.size()]; v.copyInto(fullArgs); return fullArgs; } /** * Evaluates an expression represented by a parse tree. The parse tree must * not contain any non-standard java syntax. */ Object evaluate( ClassDeclaration decl, Environment env, Expression expression, Argument[] arguments) { if (false) { // String s = expression.toString(); // ParserFactory parserFactory = new JavaCCParserFactory(); // Interpreter interpreter = new TreeInterpreter(parserFactory); // Object o = interpreter.interpret(new StringReader(s), "no file"); // return o; return null; } else { Thunk thunk = compile(decl, env, expression, arguments); Object[] args = new Object[thunk.parameterNames.length]; for (int i = 0; i < thunk.parameterNames.length; i++) { String parameterName = thunk.parameterNames[i]; Argument argument = null; for (int j = 0; j < arguments.length; j++) { if (arguments[j].name.equals(parameterName)) { argument = arguments[j]; break; } } if (argument == null) { throw Toolbox.newInternal( "variable '" + parameterName + "' not found"); } args[i] = argument.value; } try { return thunk.call(args); } catch (IllegalAccessException e) { throw Toolbox.newInternal(e); } catch (InvocationTargetException e) { throw Toolbox.newInternal(e); } } } private Thunk compile( ClassDeclaration decl, Environment env, Expression expression, Argument[] arguments) { if (Util.getProperties().getBooleanProperty( "saffron.Statement.printBeforeCompile")) { PrintStream ps = DebugOut.getStream(); ps.print("Before compile ["); ps.print(expression); ps.println("]"); } ClassCollector classCollector = new ClassCollector(env); Util.discard(Util.go(classCollector, expression)); OJClass[] classes = classCollector.getClasses(); SyntheticClass.addMembers(decl, classes); String[] parameterNames = new String[arguments.length]; Class[] parameterTypes = new Class[arguments.length]; OJClass[] parameterOjTypes = new OJClass[arguments.length]; for (int i = 0; i < arguments.length; i++) { parameterNames[i] = arguments[i].name; parameterTypes[i] = arguments[i].clazz; parameterOjTypes[i] = OJClass.forClass(arguments[i].clazz); } OJClass returnType = Util.getType(env, expression); if (!returnType.isPrimitive()) { returnType = Util.clazzObject; } SyntheticClass.addMethod( decl, env, expression, "dummy", parameterNames, parameterOjTypes, returnType); String packageName = getTempPackageName(); CompilationUnit compUnit = new CompilationUnit( packageName, new String[0], new ClassDeclarationList(decl)); String s = compUnit.toString(); String className = decl.getName(); packageName = compUnit.getPackage(); // e.g. "abc.def", or null if (packageName != null) { className = packageName + "." + className; } String javaFileName = className.replace( '.', Util.fileSeparator.charAt(0)) + ".java"; File javaRoot = new File(getJavaRoot()), javaFile = new File(javaRoot, javaFileName); try { javaFile.getParentFile().mkdirs(); // make any necessary parent directories System.out.println("Compiling " + javaFile); FileWriter fw = new FileWriter(javaFile); fw.write(s); fw.close(); } catch (java.io.IOException e) { throw Util.newInternal( e, "while writing java file '" + javaFile + "'"); } String compilerClassName = getCompilerClassName(); JavaCompiler compiler; try { Class compilerClass = Class.forName(compilerClassName); compiler = (JavaCompiler) compilerClass.newInstance(); } catch (ClassNotFoundException e) { throw Util.newInternal(e, "while instantiating compiler"); } catch (InstantiationException e) { throw Util.newInternal(e, "while instantiating compiler"); } catch (IllegalAccessException e) { throw Util.newInternal(e, "while instantiating compiler"); } catch (ClassCastException e) { throw Util.newInternal(e, "while instantiating compiler"); } String[] compilerArgs = getCompilerArgs(new String[] { javaFile.toString()}); compiler.compile(compilerArgs); try { Class clazz = Class.forName(className); Object o = clazz.newInstance(); Method method = clazz.getDeclaredMethod("dummy", parameterTypes); return new Thunk(o, method, parameterNames); } catch (ClassNotFoundException e) { throw Toolbox.newInternal(e); } catch (InstantiationException e) { throw Toolbox.newInternal(e); } catch (IllegalAccessException e) { throw Toolbox.newInternal(e); } catch (NoSuchMethodException e) { throw Toolbox.newInternal(e); } // ExpCompiler expCompiler = new ExpCompiler(); // Util.discard(Util.go(expCompiler, rel)); } static class Thunk { Object o; Method method; String[] parameterNames; Thunk(Object o, Method method, String[] parameterNames) { this.o = o; this.method = method; this.parameterNames = parameterNames; } Object call(Object[] args) throws IllegalAccessException, InvocationTargetException { return method.invoke(o, args); } }; /** * An <code>Argument</code> supplies a name/value pair to a statement. The * class of the argument is usually superfluous, but is necessary when the * value is a primitive type (such as <code>int</code>, as opposed to * {@link Integer}), or is a superclass of the object's runtime type. **/ public static class Argument { String name; Class clazz; Object value; /** * Creates an argument. **/ public Argument(String name, Class clazz, Object value) { this.name = name; this.clazz = clazz; this.value = value; } /** * Creates an argument whose type is the runtime type of * <code>value</code>. **/ public Argument(String name, Object value) { this.name = name; this.clazz = value.getClass(); this.value = value; } /** * Creates an <code>int</code> argument. **/ public Argument(String name, int value) { this(name, java.lang.Integer.TYPE, new Integer(value)); } }; } // End Statement.java
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#4 | 1801 | Julian Hyde |
saffron: add ObjectSchema; rules can now be matched more than once; started to implement correlations in queries in from list. |
||
#3 | 1748 | Julian Hyde |
saffron: convert unit tests to JUnit; add CallingConvention.ITERABLE; lots of other stuff; release 0.1. |
||
#2 | 1474 | Julian Hyde |
saffron: Aggregations are working. Renamed 'aggregator' to 'aggregation'. |
||
#1 | 1467 | Julian Hyde |
saffron: First saffron check-in; incorporate my changes to openjava. |