/* // $Id: //guest/julian_hyde/saffron/src/main/saffron/Statement.java#4 $ // 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.ojc.JavaCompiler; import openjava.ptree.*; import openjava.ptree.util.MemberAccessCorrector; import openjava.ptree.util.PartialParser; import openjava.ptree.util.QueryExpander; import openjava.ptree.util.SyntheticClass; import openjava.tools.DebugOut; import saffron.rel.SaffronQueryExpander; import saffron.runtime.BufferedIterator; import saffron.runtime.EnumerationIterator; import saffron.runtime.Iterable; import saffron.runtime.VarDecl; import saffron.util.ClassCollector; import saffron.util.SaffronSchemaExpander; import saffron.util.SaffronValidator; import saffron.util.Util; import java.io.File; import java.io.FileWriter; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Enumeration; import java.util.Iterator; import java.util.StringTokenizer; import java.util.Vector; /** * A <code>Statement</code> is used to execute a saffron (or regular Java) * expression dynamically. **/ public class Statement { Environment env; int executionCount = 0; public Statement() {} public Environment getEnvironment() { return env; } /** * 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(); ClassDeclaration decl = init(arguments); ParseTree parseTree = parse(queryString); parseTree = validate(parseTree); return evaluate(decl,parseTree,arguments); } public Expression parse(String queryString) { try { return PartialParser.makeExpression( env, "(" + queryString + ")"); } catch (MOPException e) { throw Util.newInternal( e, "while parsing [" + queryString + "]"); } } /** * Validates and transforms an expression: expands schemas, validates, * expands queries. */ public ParseTree validate(ParseTree parseTree) { MemberAccessCorrector corrector = new MemberAccessCorrector(env); parseTree = Util.go(corrector, parseTree); SaffronSchemaExpander schemaExpander = new SaffronSchemaExpander(env); parseTree = Util.go(schemaExpander, parseTree); SaffronValidator validator = new SaffronValidator(env); parseTree = Util.go(validator, parseTree); QueryExpander queryExpander = new SaffronQueryExpander(env); parseTree = Util.go(queryExpander, parseTree); return parseTree; } public ClassDeclaration init(Argument[] arguments) { env = OJSystem.env; String packageName = getTempPackageName(), className = "Dummy_" + Integer.toHexString( this.hashCode() + executionCount++); 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)); } } return decl; } /** * 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; } interface Binder { void declareVariable(String name, OJClass clazz, Object value); void declareClass(ClassDeclaration cdecl); } /** * Evaluates an expression represented by a parse tree. The parse tree must * not contain any non-standard java syntax. */ public Object evaluate( ClassDeclaration decl, ParseTree parseTree, Argument[] arguments) { Thunk thunk = compile(decl, env, parseTree, 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, ParseTree parseTree, Argument[] arguments) { if (Util.getProperties().getBooleanProperty( "saffron.Statement.printBeforeCompile")) { PrintStream ps = DebugOut.getStream(); ps.print("Before compile ["); ps.print(parseTree); ps.println("]"); } ClassCollector classCollector = new ClassCollector(env); Util.discard(Util.go(classCollector, parseTree)); OJClass[] classes = classCollector.getClasses(); SyntheticClass.addMembers(decl, classes); // form parameter list String[] parameterNames = new String[arguments.length]; Class[] parameterTypes = new Class[arguments.length]; OJClass[] parameterOjTypes = new OJClass[arguments.length]; ExpressionList returnDeclList = new ExpressionList(); 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); } // form the body of the method, and figure out the return type OJClass returnType = Util.clazzVoid; StatementList statementList; if (parseTree instanceof Expression) { Expression expression = (Expression) parseTree; returnType = Util.getType(env, expression); if (!returnType.isPrimitive()) { returnType = Util.clazzObject; } openjava.ptree.Statement statement; if (returnType == OJSystem.VOID) { statement = new ExpressionStatement(expression); } else { statement = new ReturnStatement(expression); } statementList = new StatementList(statement); returnDeclList = null; } else if (parseTree instanceof openjava.ptree.Statement) { openjava.ptree.Statement statement = (openjava.ptree.Statement) parseTree; statementList = new StatementList(statement); addDecl(statement, returnDeclList); } else if (parseTree instanceof StatementList) { statementList = (StatementList) parseTree; for (int i = 0, count = statementList.size(); i < count; i++) { addDecl(statementList.get(i), returnDeclList); } } else { throw Util.newInternal("cannot handle a " + parseTree.getClass()); } if (returnDeclList != null) { statementList.add( new ReturnStatement( new ArrayAllocationExpression( OJClass.forClass(VarDecl.class), new ExpressionList(null), new ArrayInitializer(returnDeclList)))); returnType = OJClass.arrayOf(OJClass.forClass(VarDecl.class)); } SyntheticClass.addMethod( decl, env, statementList, "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)); } private void addDecl( openjava.ptree.Statement statement, ExpressionList exprList) { if (exprList == null) { return; } if (statement instanceof VariableDeclaration) { VariableDeclaration varDecl = (VariableDeclaration) statement; TypeName typeSpecifier = varDecl.getTypeSpecifier(); String qname = env.toQualifiedName(typeSpecifier.getName()); OJClass clazz = env.lookupClass(qname, typeSpecifier.getDimension()); String varName = varDecl.getVariable(); // return new VarDecl[] { // new VarDecl("s", "java.lang.String", s), // new VarDecl("i", "int", new Integer(i))}; exprList.add( new AllocationExpression( OJClass.forClass(VarDecl.class), new ExpressionList( Literal.makeLiteral(varName), new FieldAccess( TypeName.forOJClass(clazz), "class"), Util.box( clazz, new Variable(varDecl.getVariable()))))); } } 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. |