Author: henrib Date: Tue Nov 10 19:52:51 2009 New Revision: 834631 URL: http://svn.apache.org/viewvc?rev=834631&view=rev Log: Fixed test that breaks build (use introspection instead of direct dependency upon com.sun.tools...); Added invokeMethod on JexlEngine + test; completes the get/setProperty Added tests to improve coverage
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/Interpreter.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlArithmetic.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlEngine.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreator.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreatorTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/Foo.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ForEachTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/MethodTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ScriptTest.java commons/proper/jexl/trunk/xdocs/reference/examples.xml commons/proper/jexl/trunk/xdocs/reference/syntax.xml Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/Interpreter.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/Interpreter.java?rev=834631&r1=834630&r2=834631&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/Interpreter.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/Interpreter.java Tue Nov 10 19:52:51 2009 @@ -733,19 +733,6 @@ return map; } - /** - * Replace all numbers in an arguments array with the smallest type that will fit. - * @param args the argument array - */ - protected final void narrowArguments(Object[] args) { - for (int a = 0; a < args.length; a++) { - Object arg = args[a]; - if (arg instanceof Number) { - args[a] = arithmetic.narrow((Number) arg); - } - } - } - /** {...@inheritdoc} */ public Object visit(ASTMethodNode node, Object data) { // the object to invoke the method on should be in the data argument @@ -787,8 +774,9 @@ JexlMethod vm = uberspect.getMethod(data, methodName, argv, node); // DG: If we can't find an exact match, narrow the parameters and try again! if (vm == null) { - narrowArguments(argv); - vm = uberspect.getMethod(data, methodName, argv, node); + if (arithmetic.narrowArguments(argv)) { + vm = uberspect.getMethod(data, methodName, argv, node); + } if (vm == null) { xjexl = new JexlException(node, "unknown or ambiguous method", null); } @@ -826,9 +814,9 @@ // DG: If we can't find an exact match, narrow the parameters and // try again! if (ctor == null) { - // replace all numbers with the smallest type that will fit - narrowArguments(argv); - ctor = uberspect.getConstructor(cobject, argv, node); + if (arithmetic.narrowArguments(argv)) { + ctor = uberspect.getConstructor(cobject, argv, node); + } if (ctor == null) { xjexl = new JexlException(node, "unknown constructor", null); } @@ -880,8 +868,9 @@ // try again! if (vm == null) { // replace all numbers with the smallest type that will fit - narrowArguments(argv); - vm = uberspect.getMethod(namespace, function, argv, node); + if (arithmetic.narrowArguments(argv)) { + vm = uberspect.getMethod(namespace, function, argv, node); + } if (vm == null) { xjexl = new JexlException(node, "unknown function", null); } Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlArithmetic.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlArithmetic.java?rev=834631&r1=834630&r2=834631&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlArithmetic.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlArithmetic.java Tue Nov 10 19:52:51 2009 @@ -237,6 +237,27 @@ } /** + * Replace all numbers in an arguments array with the smallest type that will fit. + * @param args the argument array + * @return true if some arguments were narrowed and args array is modified, + * false if no narrowing occured and args array has not been modified + */ + protected boolean narrowArguments(Object[] args) { + boolean narrowed = false; + for (int a = 0; a < args.length; ++a) { + Object arg = args[a]; + if (arg instanceof Number) { + Object narg = narrow((Number) arg); + if (narg != arg) { + narrowed = true; + } + args[a] = narg; + } + } + return narrowed; + } + + /** * Add two values together. * <p> * If any numeric add fails on coercion to the appropriate type, Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlEngine.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlEngine.java?rev=834631&r1=834630&r2=834631&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlEngine.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlEngine.java Tue Nov 10 19:52:51 2009 @@ -39,8 +39,10 @@ import org.apache.commons.jexl.parser.TokenMgrError; import org.apache.commons.jexl.parser.ASTJexlScript; import org.apache.commons.jexl.util.Introspector; +import org.apache.commons.jexl.util.introspection.DebugInfo; import org.apache.commons.jexl.util.introspection.Uberspect; import org.apache.commons.jexl.util.introspection.Info; +import org.apache.commons.jexl.util.introspection.JexlMethod; /** * <p> @@ -251,7 +253,7 @@ if (size <= 0) { cache = null; } else if (cache == null || cache.size() != size) { - cache = new SoftCache<String,ASTJexlScript>(size); + cache = new SoftCache<String, ASTJexlScript>(size); } } } @@ -305,7 +307,7 @@ * expression nor a reference. */ public Expression createExpression(String expression) - throws ParseException { + throws ParseException { return createExpression(expression, null); } @@ -325,8 +327,7 @@ // Parse the expression ASTJexlScript tree = parse(expression, info); if (tree.jjtGetNumChildren() > 1) { - logger.warn("The JEXL Expression created will be a reference" - + " to the first expression from the supplied script: \"" + expression + "\" "); + logger.warn("The JEXL Expression created will be a reference" + " to the first expression from the supplied script: \"" + expression + "\" "); } return new ExpressionImpl(this, expression, tree); } @@ -545,6 +546,41 @@ } /** + * Invokes an object's method by name and arguments. + * @param obj the method's invoker object + * @param meth the method's name + * @param args the method's arguments + * @return the method returned value or null if it failed and engine is silent + * @throws JexlException if method could not be found or failed and engine is not silent + */ + public Object invokeMethod(Object obj, String meth, Object... args) { + JexlException xjexl = null; + Object result = null; + try { + JexlMethod method = uberspect.getMethod(obj, meth, args, DebugInfo.NONE); + if (method == null && arithmetic.narrowArguments(args)) { + method = uberspect.getMethod(obj, meth, args, DebugInfo.NONE); + } + if (method != null) { + result = method.invoke(obj, args); + } else { + xjexl = new JexlException(null, "failed finding method " + meth); + } + } catch (Exception xany) { + xjexl = new JexlException(null, "failed executing method " + meth, xany); + } finally { + if (xjexl != null) { + if (silent) { + logger.warn(xjexl.getMessage(), xjexl.getCause()); + return null; + } + throw xjexl; + } + } + return result; + } + + /** * Creates an interpreter. * @param context a JexlContext; if null, the EMPTY_CONTEXT is used instead. * @return an Interpreter @@ -556,22 +592,23 @@ return new Interpreter(this, context); } - /** - * A soft reference on cache. - * <p>The cache is held through a soft reference, allowing it to be GCed under - * memory pressure.</p> - * @param <K> the cache key entry type - * @param <V> the cache key value type - */ - protected class SoftCache<K,V> { - /** - * The cache size. - */ + /** + * A soft reference on cache. + * <p>The cache is held through a soft reference, allowing it to be GCed under + * memory pressure.</p> + * @param <K> the cache key entry type + * @param <V> the cache key value type + */ + protected class SoftCache<K, V> { + /** + * The cache size. + */ private final int size; /** * The soft reference to the cache map. */ private SoftReference<Map<K, V>> ref = null; + /** * Creates a new instance of a soft cache. * @param theSize the cache size @@ -593,9 +630,9 @@ * @return the cache entry set */ @SuppressWarnings("unchecked") - Set<Entry<K,V>> entrySet() { - Map<K, V> map = ref != null? ref.get() : null; - return map != null? map.entrySet() : (Set<Entry<K,V>>) Collections.EMPTY_SET; + Set<Entry<K, V>> entrySet() { + Map<K, V> map = ref != null ? ref.get() : null; + return map != null ? map.entrySet() : (Set<Entry<K, V>>) Collections.EMPTY_SET; } /** @@ -604,8 +641,8 @@ * @return the cache entry value */ V get(K key) { - final Map<K, V> map = ref != null? ref.get() : null; - return map != null? map.get(key) : null; + final Map<K, V> map = ref != null ? ref.get() : null; + return map != null ? map.get(key) : null; } /** @@ -614,7 +651,7 @@ * @param script the cache entry value */ void put(K key, V script) { - Map<K, V> map = ref != null? ref.get() : null; + Map<K, V> map = ref != null ? ref.get() : null; if (map == null) { map = createCache(size); ref = new SoftReference<Map<K, V>>(map); @@ -630,10 +667,11 @@ * @param cacheSize the cache size, must be > 0 * @return a Map usable as a cache bounded to the given size */ - protected <K,V> Map<K, V> createCache(final int cacheSize) { + protected <K, V> Map<K, V> createCache(final int cacheSize) { return new java.util.LinkedHashMap<K, V>(cacheSize, LOAD_FACTOR, true) { /** Serial version UID. */ private static final long serialVersionUID = 3801124242820219131L; + @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > cacheSize; @@ -668,7 +706,7 @@ StackTraceElement[] stack = xinfo.getStackTrace(); StackTraceElement se = null; Class<?> clazz = getClass(); - for(int s = 1; s < stack.length; ++s, se = null) { + for (int s = 1; s < stack.length; ++s, se = null) { se = stack[s]; String className = se.getClassName(); if (!className.equals(clazz.getName())) { @@ -687,7 +725,7 @@ } } if (se != null) { - info = new Info(se.getClassName()+"."+se.getMethodName(), se.getLineNumber(), 0); + info = new Info(se.getClassName() + "." + se.getMethodName(), se.getLineNumber(), 0); } } tree = parser.parse(reader, info); @@ -749,4 +787,4 @@ } } -} \ No newline at end of file +} Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreator.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreator.java?rev=834631&r1=834630&r2=834631&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreator.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreator.java Tue Nov 10 19:52:51 2009 @@ -28,17 +28,33 @@ * load it through a dedicated class loader. */ public class ClassCreator { + private final JexlEngine jexl; private final File base; private File packageDir = null; private int seed = 0; private String className = null; private String sourceName = null; private ClassLoader loader = null; + public static final boolean canRun = comSunToolsJavacMain(); + /** + * Check if we can invoke Sun's java compiler. + * @return true if it is possible, false otherwise + */ + private static boolean comSunToolsJavacMain() { + try { + Class<?> javac = ClassCreatorTest.class.getClassLoader().loadClass("com.sun.tools.javac.Main"); + return javac != null; + } catch (Exception xany) { + return false; + } + } - public ClassCreator(File theBase) throws Exception { + public ClassCreator(JexlEngine theJexl, File theBase) throws Exception { + jexl = theJexl; base = theBase; } + public void clear() { seed = 0; packageDir = null; @@ -106,8 +122,13 @@ } Class<?> compile() throws Exception { - String[] source = {packageDir.getPath() + "/" + sourceName}; - if (com.sun.tools.javac.Main.compile(source) >= 0) { + String source = packageDir.getPath() + "/" + sourceName; + Class<?> javac = getClassLoader().loadClass("com.sun.tools.javac.Main"); + if (javac == null) { + return null; + } + Integer r = (Integer) jexl.invokeMethod(javac, "compile", source); + if (r >= 0) { return getClassLoader().loadClass("org.apache.commons.jexl.generated." + className); } return null; Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreatorTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreatorTest.java?rev=834631&r1=834630&r2=834631&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreatorTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreatorTest.java Tue Nov 10 19:52:51 2009 @@ -33,13 +33,14 @@ static final Log logger = LogFactory.getLog(JexlTestCase.class); static final int LOOPS = 8; private File base = null; + private JexlEngine jexl = null; @Override public void setUp() throws Exception { - base = new File(System.getProperty("java.io.tmpdir") - + File.pathSeparator - + "jexl" - + System.currentTimeMillis()); + base = new File(System.getProperty("java.io.tmpdir") + File.pathSeparator + "jexl" + System.currentTimeMillis()); + jexl = new JexlEngine(); + jexl.setCache(512); + } @Override @@ -72,7 +73,7 @@ return id; } } - + // A soft reference on class static final class ClassReference extends WeakReference<Class<?>> { ClassReference(Class<?> clazz, ReferenceQueue<Object> queue) { @@ -87,7 +88,11 @@ } public void testOne() throws Exception { - ClassCreator cctor = new ClassCreator(base); + // abort test if class creator can not run + if (!ClassCreator.canRun) { + return; + } + ClassCreator cctor = new ClassCreator(jexl, base); cctor.setSeed(1); Class<?> foo1 = cctor.createClass(); assertEquals("foo1", foo1.getSimpleName()); @@ -95,23 +100,25 @@ } public void testMany() throws Exception { + // abort test if class creator can not run + if (!ClassCreator.canRun) { + return; + } int pass = 0; int gced = -1; ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); List<Reference<?>> stuff = new ArrayList<Reference<?>>(); // keeping a reference on methods prevent classes from being GCed // List<Object> mm = new ArrayList<Object>(); - JexlEngine jexl = new JexlEngine(); - jexl.setCache(512); Expression expr = jexl.createExpression("foo.value"); Expression newx = jexl.createExpression("foo = new(clazz)"); JexlContext context = JexlHelper.createContext(); - ClassCreator cctor = new ClassCreator(base); + ClassCreator cctor = new ClassCreator(jexl, base); for (int i = 0; i < LOOPS && gced < 0; ++i) { cctor.setSeed(i); Class<?> clazz; - if (pass ==0) { + if (pass == 0) { clazz = cctor.createClass(); } else { clazz = cctor.getClassInstance(); Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/Foo.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/Foo.java?rev=834631&r1=834630&r2=834631&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/Foo.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/Foo.java Tue Nov 10 19:52:51 2009 @@ -17,6 +17,7 @@ package org.apache.commons.jexl; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** @@ -31,6 +32,11 @@ private boolean beenModified = false; private String property1 = "some value"; public Foo() {} + public class Cheezy { + public Iterator<String> iterator() { + return getCheeseList().iterator(); + } + } public String bar() { @@ -70,6 +76,11 @@ return answer; } + public Cheezy getCheezy() + { + return new Cheezy(); + } + public String[] getArray() { return ArrayAccessTest.GET_METHOD_ARRAY; Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ForEachTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ForEachTest.java?rev=834631&r1=834630&r2=834631&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ForEachTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ForEachTest.java Tue Nov 10 19:52:51 2009 @@ -120,5 +120,16 @@ Object o = e.evaluate(jc); assertEquals("Result is not last evaluated expression", "brie", o); } + + public void testForEachWithIteratorMethod() throws Exception { + Expression e = JEXL.createExpression("for(item : list.cheezy) item"); + JexlContext jc = JexlHelper.createContext(); + jc.getVars().put("list", new Foo()); + Object o = e.evaluate(jc); + assertEquals("Result is not last evaluated expression", "brie", o); + } + static public void main(String[] args) throws Exception { + (new ForEachTest("*")).runTest("testForEachWithIteratorMethod"); + } } \ No newline at end of file Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/MethodTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/MethodTest.java?rev=834631&r1=834630&r2=834631&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/MethodTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/MethodTest.java Tue Nov 10 19:52:51 2009 @@ -48,6 +48,9 @@ public static int PLUS20(int num) { return num + 20; } + public static Class<?> NPEIfNull(Object x) { + return x.getClass(); + } } @Override @@ -60,6 +63,24 @@ asserter.assertExpression("test.testVarArgs(1,2,3,4,5)", "Test"); } + public void testInvoke() throws Exception { + Functor func = new Functor(); + assertEquals(Integer.valueOf(10), JEXL.invokeMethod(func, "ten")); + assertEquals(Integer.valueOf(42), JEXL.invokeMethod(func, "PLUS20", 22)); + try { + JEXL.invokeMethod(func, "nonExistentMethod"); + fail("method does not exist!"); + } catch(Exception xj0) { + // ignore + } + try { + JEXL.invokeMethod(func, "NPEIfNull", null); + fail("method should have thrown!"); + } catch(Exception xj0) { + // ignore + } + } + /** * test a simple method expression */ Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ScriptTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ScriptTest.java?rev=834631&r1=834630&r2=834631&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ScriptTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ScriptTest.java Tue Nov 10 19:52:51 2009 @@ -52,8 +52,14 @@ * Test creating a script from a string. */ public void testSimpleScript() throws Exception { + simpleScript(true); + } + public void testLegacySimpleScript() throws Exception { + simpleScript(false); + } + private void simpleScript(boolean jexl) throws Exception { String code = "while (x < 10) x = x + 1;"; - Script s = JEXL.createScript(code); + Script s = jexl? JEXL.createScript(code) : ScriptFactory.createScript(code); JexlContext jc = JexlHelper.createContext(); jc.getVars().put("x", new Integer(1)); @@ -61,10 +67,16 @@ assertEquals("Result is wrong", new Integer(10), o); assertEquals("getText is wrong", code, s.getText()); } - + public void testScriptFromFile() throws Exception { + scriptFromFile(true); + } + public void testLegacyScriptFromFile() throws Exception { + scriptFromFile(false); + } + private void scriptFromFile(boolean jexl) throws Exception { File testScript = new File(TEST1); - Script s = JEXL.createScript(testScript); + Script s = jexl? JEXL.createScript(testScript) : ScriptFactory.createScript(testScript); JexlContext jc = JexlHelper.createContext(); jc.getVars().put("out", System.out); Object result = s.execute(jc); @@ -73,19 +85,31 @@ } public void testScriptFromURL() throws Exception { + scriptFromURL(true); + } + public void testLegacyScriptFromURL() throws Exception { + scriptFromURL(false); + } + private void scriptFromURL(boolean jexl) throws Exception { URL testUrl = new File("src/test/scripts/test1.jexl").toURI().toURL(); - Script s = JEXL.createScript(testUrl); + Script s = jexl? JEXL.createScript(testUrl) : ScriptFactory.createScript(testUrl); JexlContext jc = JexlHelper.createContext(); jc.getVars().put("out", System.out); Object result = s.execute(jc); assertNotNull("No result", result); assertEquals("Wrong result", new Integer(7), result); } - + public void testScriptUpdatesContext() throws Exception { + scriptUpdatesContext(true); + } + public void testLegacyScriptUpdatesContext() throws Exception { + scriptUpdatesContext(false); + } + private void scriptUpdatesContext(boolean jexl) throws Exception { String jexlCode = "resultat.setCode('OK')"; - Expression e = JEXL.createExpression(jexlCode); - Script s = JEXL.createScript(jexlCode); + Expression e = jexl? JEXL.createExpression(jexlCode) : ExpressionFactory.createExpression(jexlCode); + Script s = jexl? JEXL.createScript(jexlCode) : ScriptFactory.createScript(jexlCode); Tester resultatJexl = new Tester(); JexlContext jc = JexlHelper.createContext(); @@ -98,4 +122,5 @@ s.execute(jc); assertEquals("OK", resultatJexl.getCode()); } + } Modified: commons/proper/jexl/trunk/xdocs/reference/examples.xml URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/xdocs/reference/examples.xml?rev=834631&r1=834630&r2=834631&view=diff ============================================================================== --- commons/proper/jexl/trunk/xdocs/reference/examples.xml (original) +++ commons/proper/jexl/trunk/xdocs/reference/examples.xml Tue Nov 10 19:52:51 2009 @@ -18,7 +18,7 @@ <document> <properties> - <title>Commons JEXL Examples</title> + <title>Commons JEXL 1.0 Examples</title> </properties> <body> Modified: commons/proper/jexl/trunk/xdocs/reference/syntax.xml URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/xdocs/reference/syntax.xml?rev=834631&r1=834630&r2=834631&view=diff ============================================================================== --- commons/proper/jexl/trunk/xdocs/reference/syntax.xml (original) +++ commons/proper/jexl/trunk/xdocs/reference/syntax.xml Tue Nov 10 19:52:51 2009 @@ -170,6 +170,13 @@ with <code>]</code>, e.g. <source>[ 1, 2, "three" ]</source> <p>This syntax creates an <code>Object[]</code>.</p> + <p> + JEXL will attempt to strongly type the array; if all entries are of the same class or if all + entries are Number instance, the array literal will be an <code>MyClass[]</code> in the former + case, a <code>Number[]</code> in the latter case.</p> + <p>Furthermore, if all entries in the array literal are of the same class + and that class has an equivalent primitive type, the array returned will be a primitive array. e.g. + <code>[1, 2, 3]</code> will be interpreted as <code>int[]</code>.</p> </td> </tr> <tr>