diff --git a/.gitignore b/.gitignore index d92172f..0cb5483 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -target/* -.pmd +target/* +.pmd +.scala_dependencies +.settings/ diff --git a/pom.xml b/pom.xml index a728c30..78e066b 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ UTF-8 2.7.7 + 3.0.6.RELEASE @@ -189,6 +190,7 @@ org.scala-lang scala-library ${scala.version} + test org.scalatest @@ -205,12 +207,12 @@ org.springframework spring-core - 3.0.2.RELEASE + ${spring.version} org.springframework spring-expression - 3.0.2.RELEASE + ${spring.version} diff --git a/src/main/java/net/abhinavsarkar/spelhelper/ExtensionFunctions.java b/src/main/java/net/abhinavsarkar/spelhelper/ExtensionFunctions.java index c54e42e..040c062 100644 --- a/src/main/java/net/abhinavsarkar/spelhelper/ExtensionFunctions.java +++ b/src/main/java/net/abhinavsarkar/spelhelper/ExtensionFunctions.java @@ -1,97 +1,97 @@ -/* Copyright 2010 Abhinav Sarkar - * - * This file is a part of SpelHelper library. - * - * SpelHelper library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License (GNU LGPL) as - * published by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * SpelHelper 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SpelHelper library. If not, see . - */ -package net.abhinavsarkar.spelhelper; - -import static java.util.Collections.unmodifiableList; -import static java.util.Collections.unmodifiableMap; -import static java.util.Collections.unmodifiableSet; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.util.Assert; - -/** - * Provides some extension functions to create some basic collection types - * inline in SpEL expressions. - * These functions are automatically registered with {@link SpelHelper}. - * - * **See Also:** - * [Spring Docs on extension functions](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html#expressions-ref-functions) - * @author Abhinav Sarkar _abhinav@abhinavsarkar.net_ - */ -public final class ExtensionFunctions { - - private ExtensionFunctions() { - } - - /** - * Creates an unmodifiable {@link List} of the arguments provided. - * - * Example use: `"#list('one', 'two', 'three')"` - * @param Type of the arguments provided. - * @param args Arguments to create list of. - * @return An unmodifiable list of the arguments provided. - */ - public static List list(final T... args) { - return unmodifiableList(Arrays.asList(args)); - } - - /** - * Creates an unmodifiable {@link Set} of the arguments provided. - * - * Example use: `"#set('one', 'two', 'three')"` - * @param Type of the arguments provided. - * @param args Arguments to create set of. - * @return An unmodifiable set of the arguments provided. - */ - public static Set set(final T... args) { - return unmodifiableSet(new HashSet(list(args))); - } - - /** - * Creates an unmodifiable {@link Map} using the {@link List} of keys - * provided as the first argument and the {@link List} of values provided - * as the second argument. - * - * Example use: `"#map(#list('one', 'two', 'three'), #list(1, 2, 3))"` - * @param Type of the keys of the map. - * @param Type of the values of map. - * @param keys List of the keys. - * @param values List of the values. - * @return A unmodifiable map created from the key and value lists. - * @throws IllegalArgumentException if the number of keys and the number of - * values is not equal. - */ - public static Map map(final List keys, - final List values) { - Assert.isTrue(keys.size() == values.size(), - "There should be equal number of keys and values"); - Map map = new HashMap(); - int length = keys.size(); - for (int i = 0; i < length; i++) { - map.put(keys.get(i), values.get(i)); - } - return unmodifiableMap(map); - } - -} +/* Copyright 2010 Abhinav Sarkar + * + * This file is a part of SpelHelper library. + * + * SpelHelper library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License (GNU LGPL) as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * SpelHelper 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SpelHelper library. If not, see . + */ +package net.abhinavsarkar.spelhelper; + +import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableMap; +import static java.util.Collections.unmodifiableSet; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.springframework.util.Assert; + +/** + * Provides some extension functions to create some basic collection types + * inline in SpEL expressions. + * These functions are automatically registered with {@link SpelHelper}. + * + * **See Also:** + * [Spring Docs on extension functions](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html#expressions-ref-functions) + * @author Abhinav Sarkar _abhinav@abhinavsarkar.net_ + */ +public final class ExtensionFunctions { + + private ExtensionFunctions() { + } + + /** + * Creates an unmodifiable {@link List} of the arguments provided. + * + * Example use: `"#list('one', 'two', 'three')"` + * @param Type of the arguments provided. + * @param args Arguments to create list of. + * @return An unmodifiable list of the arguments provided. + */ + public static List list(final T... args) { + return unmodifiableList(Arrays.asList(args)); + } + + /** + * Creates an unmodifiable {@link Set} of the arguments provided. + * + * Example use: `"#set('one', 'two', 'three')"` + * @param Type of the arguments provided. + * @param args Arguments to create set of. + * @return An unmodifiable set of the arguments provided. + */ + public static Set set(final T... args) { + return unmodifiableSet(new HashSet(list(args))); + } + + /** + * Creates an unmodifiable {@link Map} using the {@link List} of keys + * provided as the first argument and the {@link List} of values provided + * as the second argument. + * + * Example use: `"#map(#list('one', 'two', 'three'), #list(1, 2, 3))"` + * @param Type of the keys of the map. + * @param Type of the values of map. + * @param keys List of the keys. + * @param values List of the values. + * @return A unmodifiable map created from the key and value lists. + * @throws IllegalArgumentException if the number of keys and the number of + * values is not equal. + */ + public static Map map(final List keys, + final List values) { + Assert.isTrue(keys.size() == values.size(), + "There should be equal number of keys and values"); + Map map = new HashMap(); + int length = keys.size(); + for (int i = 0; i < length; i++) { + map.put(keys.get(i), values.get(i)); + } + return unmodifiableMap(map); + } + +} diff --git a/src/main/java/net/abhinavsarkar/spelhelper/ImplicitConstructorResolver.java b/src/main/java/net/abhinavsarkar/spelhelper/ImplicitConstructorResolver.java index 2e78791..352bce6 100644 --- a/src/main/java/net/abhinavsarkar/spelhelper/ImplicitConstructorResolver.java +++ b/src/main/java/net/abhinavsarkar/spelhelper/ImplicitConstructorResolver.java @@ -1,48 +1,52 @@ -/* Copyright 2010 Abhinav Sarkar - * - * This file is a part of SpelHelper library. - * - * SpelHelper library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License (GNU LGPL) as - * published by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * SpelHelper 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SpelHelper library. If not, see . - */ -package net.abhinavsarkar.spelhelper; - -import java.lang.reflect.Constructor; -import java.util.Arrays; - -import org.springframework.expression.AccessException; -import org.springframework.expression.ConstructorExecutor; -import org.springframework.expression.ConstructorResolver; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.spel.support.ReflectiveConstructorResolver; - -final class ImplicitConstructorResolver implements - ConstructorResolver { - - private final ReflectiveConstructorResolver delegate = new ReflectiveConstructorResolver(); - - public ConstructorExecutor resolve(final EvaluationContext context, - final String typeName, final Class[] argumentTypes) throws AccessException { - try { - return delegate.resolve(context, typeName, argumentTypes); - } catch (AccessException ex) { - Object variable = ((SpelHelper) context.lookupVariable(SpelHelper.CONTEXT_LOOKUP_KEY)) - .lookupImplicitConstructor(typeName + Arrays.toString(argumentTypes)); - if (variable instanceof Constructor) { - Constructor constructor = (Constructor) variable; - return delegate.resolve(context, constructor.getDeclaringClass().getName(), argumentTypes); - } - return null; - } - } +/* Copyright 2010 Abhinav Sarkar + * + * This file is a part of SpelHelper library. + * + * SpelHelper library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License (GNU LGPL) as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * SpelHelper 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SpelHelper library. If not, see . + */ +package net.abhinavsarkar.spelhelper; + +import java.lang.reflect.Constructor; +import java.util.List; + +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.expression.AccessException; +import org.springframework.expression.ConstructorExecutor; +import org.springframework.expression.ConstructorResolver; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.spel.support.ReflectiveConstructorResolver; + +final class ImplicitConstructorResolver implements + ConstructorResolver { + + private final ReflectiveConstructorResolver delegate = new ReflectiveConstructorResolver(); + + @Override + public ConstructorExecutor resolve( + final EvaluationContext context, final String typeName, + final List argumentTypes) + throws AccessException { + try { + return delegate.resolve(context, typeName, argumentTypes); + } catch (AccessException ex) { + Object variable = ((SpelHelper) context.lookupVariable(SpelHelper.CONTEXT_LOOKUP_KEY)) + .lookupImplicitConstructor(typeName + argumentTypes.toString()); + if (variable instanceof Constructor) { + Constructor constructor = (Constructor) variable; + return delegate.resolve(context, constructor.getDeclaringClass().getName(), argumentTypes); + } + return null; + } + } } \ No newline at end of file diff --git a/src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethodResolver.java b/src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethodResolver.java index af1de31..7614aef 100644 --- a/src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethodResolver.java +++ b/src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethodResolver.java @@ -1,113 +1,120 @@ -/* Copyright 2010 Abhinav Sarkar - * - * This file is a part of SpelHelper library. - * - * SpelHelper library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License (GNU LGPL) as - * published by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * SpelHelper 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SpelHelper library. If not, see . - */ -package net.abhinavsarkar.spelhelper; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.expression.AccessException; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.MethodExecutor; -import org.springframework.expression.MethodResolver; -import org.springframework.expression.TypedValue; -import org.springframework.expression.spel.support.ReflectiveMethodResolver; - -final class ImplicitMethodResolver implements MethodResolver { - - private static final ConcurrentHashMap CACHE = - new ConcurrentHashMap(); - - private static final MethodExecutor NULL_ME = new MethodExecutor() { - public TypedValue execute(final EvaluationContext context, final Object target, - final Object... arguments) throws AccessException { - return null; - } - }; - - private static final class ImplicitMethodExecutor implements - MethodExecutor { - private final MethodExecutor executor; - - public ImplicitMethodExecutor(final MethodExecutor executor) { - this.executor = executor; - } - - public TypedValue execute(final EvaluationContext context, final Object target, - final Object... arguments) throws AccessException { - Object[] modifiedArguments = new Object[arguments.length + 1]; - modifiedArguments[0] = target; - System.arraycopy(arguments, 0, modifiedArguments, 1, arguments.length); - return executor.execute(context, null, modifiedArguments); - } - } - - public MethodExecutor resolve(final EvaluationContext context, - final Object targetObject, final String name, final Class[] argumentTypes) - throws AccessException { - if (targetObject == null) { - return null; - } - Class type = targetObject.getClass(); - String cacheKey = type.getName() + "." + name; - if (CACHE.containsKey(cacheKey)) { - MethodExecutor executor = CACHE.get(cacheKey); - return executor == NULL_ME ? null : executor; - } - - Method method = lookupMethod(context, type, name); - if (method != null) { - int modifiers = method.getModifiers(); - if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) { - Class[] parameterTypes = method.getParameterTypes(); - Class firstParamType = parameterTypes[0]; - if (parameterTypes.length > 0 - && firstParamType.isAssignableFrom(type)) { - Class[] newArgumentTypes = new Class[argumentTypes.length + 1]; - newArgumentTypes[0] = firstParamType; - System.arraycopy(argumentTypes, 0, newArgumentTypes, - 1, argumentTypes.length); - MethodExecutor executor = new ReflectiveMethodResolver() - .resolve(context, method.getDeclaringClass(), name, - newArgumentTypes); - MethodExecutor wrappedExecutor = executor == null ? null - : new ImplicitMethodExecutor(executor); - if (wrappedExecutor == null) { - CACHE.putIfAbsent(cacheKey, NULL_ME); - } - return wrappedExecutor; - } - } - } - CACHE.putIfAbsent(cacheKey, NULL_ME); - return null; - } - - private static Method lookupMethod(final EvaluationContext context, - final Class type, final String name) { - for (Class clazz : InheritenceUtil.getInheritance(type)) { - Object variable = ((SpelHelper) context.lookupVariable(SpelHelper.CONTEXT_LOOKUP_KEY)) - .lookupImplicitMethod(clazz.getName() + "." + name); - if (variable instanceof Method) { - return (Method) variable; - } - } - return null; - } - +/* Copyright 2010 Abhinav Sarkar + * + * This file is a part of SpelHelper library. + * + * SpelHelper library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License (GNU LGPL) as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * SpelHelper 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SpelHelper library. If not, see . + */ +package net.abhinavsarkar.spelhelper; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.expression.AccessException; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.MethodExecutor; +import org.springframework.expression.MethodResolver; +import org.springframework.expression.TypedValue; +import org.springframework.expression.spel.support.ReflectiveMethodResolver; + +final class ImplicitMethodResolver implements MethodResolver { + + private static final ConcurrentHashMap CACHE = + new ConcurrentHashMap(); + + private final ReflectiveMethodResolver delegate = new ReflectiveMethodResolver(); + + private static final MethodExecutor NULL_ME = new MethodExecutor() { + public TypedValue execute(final EvaluationContext context, final Object target, + final Object... arguments) throws AccessException { + return null; + } + }; + + private static final class ImplicitMethodExecutor implements + MethodExecutor { + private final MethodExecutor executor; + + public ImplicitMethodExecutor(final MethodExecutor executor) { + this.executor = executor; + } + + public TypedValue execute(final EvaluationContext context, final Object target, + final Object... arguments) throws AccessException { + Object[] modifiedArguments = new Object[arguments.length + 1]; + modifiedArguments[0] = target; + System.arraycopy(arguments, 0, modifiedArguments, 1, arguments.length); + return executor.execute(context, null, modifiedArguments); + } + } + + @Override + public MethodExecutor resolve( + final EvaluationContext context, final Object targetObject, + final String name, final List argumentTypes) + throws AccessException { + if (targetObject == null) { + return null; + } + Class type = targetObject.getClass(); + String cacheKey = type.getName() + "." + name; + if (CACHE.containsKey(cacheKey)) { + MethodExecutor executor = CACHE.get(cacheKey); + return executor == NULL_ME ? null : executor; + } + + Method method = lookupMethod(context, type, name); + if (method != null) { + int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) { + Class[] parameterTypes = method.getParameterTypes(); + Class firstParamType = parameterTypes[0]; + if (parameterTypes.length > 0 + && firstParamType.isAssignableFrom(type)) { + List newArgumentTypes = new ArrayList(); + newArgumentTypes.add(TypeDescriptor.valueOf(firstParamType)); + newArgumentTypes.addAll(argumentTypes); + + MethodExecutor executor = + delegate.resolve(context, method.getDeclaringClass(), + name, newArgumentTypes); + MethodExecutor wrappedExecutor = executor == null ? null + : new ImplicitMethodExecutor(executor); + if (wrappedExecutor == null) { + CACHE.putIfAbsent(cacheKey, NULL_ME); + } + return wrappedExecutor; + } + } + } + CACHE.putIfAbsent(cacheKey, NULL_ME); + return null; + } + + private static Method lookupMethod(final EvaluationContext context, + final Class type, final String name) { + for (Class clazz : InheritenceUtil.getInheritance(type)) { + Object variable = ((SpelHelper) context.lookupVariable(SpelHelper.CONTEXT_LOOKUP_KEY)) + .lookupImplicitMethod(clazz.getName() + "." + name); + if (variable instanceof Method) { + return (Method) variable; + } + } + return null; + } + } \ No newline at end of file diff --git a/src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethods.java b/src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethods.java index 4eb33d8..13133fb 100644 --- a/src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethods.java +++ b/src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethods.java @@ -1,129 +1,129 @@ -/* Copyright 2010 Abhinav Sarkar - * - * This file is a part of SpelHelper library. - * - * SpelHelper library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License (GNU LGPL) as - * published by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * SpelHelper 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SpelHelper library. If not, see . - */ -package net.abhinavsarkar.spelhelper; - -import static java.util.Collections.unmodifiableList; -import static java.util.Collections.unmodifiableSet; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Provides some implicit methods which can be invoked on the instances of - * class of the first parameter of the method inside a SpEL expression. - * @author Abhinav Sarkar _abhinav@abhinavsarkar.net_ - */ -public final class ImplicitMethods { - - private ImplicitMethods() { - } - - /** - * Provides implicit method `distinct` on the {@link List} class. - * - * Example: `"#list('a','b','a').distinct()" //should return List('a','b')` - * - * With implicit property support provided by {@link SpelHelper} this can - * also be written as: - * - * `"#list('a','b','a').distinct" //same output as earlier` - * @param Type of the list's elements. - * @param list The list to call this method upon. - * @return An unmodifiable {@link Set} containing the distinct items of the list. - */ - public static Set distinct(final List list) { - return unmodifiableSet(new HashSet(list)); - } - - /** - * Provides implicit method `sorted` on the {@link List} class. - * - * Example: `"#list('c','b','a').sorted()" //should return List('a','b','c')` - * - * With implicit property support provided by {@link SpelHelper} this can - * also be written as: - * - * `"#list('c','b','a').sorted" //same output as earlier` - * @param Type of the list's elements. - * @param list The list to call this method upon. - * @return An unmodifiable {@link List} containing the sorted items - * of the list. - * @see Collections#sort(List) - */ - public static > List sorted( - final List list) { - List temp = new ArrayList(list); - Collections.sort(temp); - return unmodifiableList(temp); - } - - /** - * Provides implicit method `reversed` on the {@link List} class. - * - * Example: `"#list('c','b','a').reversed()" //should return List('a','b','c')` - * - * With implicit property support provided by {@link SpelHelper} this can - * also be written as: - * - * `"#list('c','b','a').reversed" //same output as earlier` - * @param Type of the list's elements. - * @param list The list to call this method upon. - * @return An unmodifiable {@link List} containing the items of the - * list in reverse order. - * @see Collections#reverse(List) - */ - public static List reversed(final List list) { - List temp = new ArrayList(list); - Collections.reverse(temp); - return unmodifiableList(temp); - } - - /** - * Provides implicit method `take` on the {@link List} class. - * - * Example: `"#list('c','b','a').take(2)" //should return List('a','b')` - * - * @param Type of the list's elements. - * @param list The list to call this method upon. - * @param n Number of items to _take_ from the list. - * @return An unmodifiable {@link List} containing the first `n` items - * of the list. - */ - public static List take(final List list, final int n) { - return unmodifiableList(list.subList(0, n)); - } - - /** - * Provides implicit method `drop` on the {@link List} class. - * - * Example: `"#list('c','b','a').drop(2)" //should return List('a')` - * - * @param Type of the list's elements. - * @param list The list to call this method upon. - * @param n Number of items to _drop_ from the list. - * @return An unmodifiable {@link List} containing the items after the - * first `n` items of the list. - */ - public static List drop(final List list, final int n) { - return unmodifiableList(list.subList(n, list.size())); - } - -} +/* Copyright 2010 Abhinav Sarkar + * + * This file is a part of SpelHelper library. + * + * SpelHelper library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License (GNU LGPL) as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * SpelHelper 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SpelHelper library. If not, see . + */ +package net.abhinavsarkar.spelhelper; + +import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableSet; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Provides some implicit methods which can be invoked on the instances of + * class of the first parameter of the method inside a SpEL expression. + * @author Abhinav Sarkar _abhinav@abhinavsarkar.net_ + */ +public final class ImplicitMethods { + + private ImplicitMethods() { + } + + /** + * Provides implicit method `distinct` on the {@link List} class. + * + * Example: `"#list('a','b','a').distinct()" //should return List('a','b')` + * + * With implicit property support provided by {@link SpelHelper} this can + * also be written as: + * + * `"#list('a','b','a').distinct" //same output as earlier` + * @param Type of the list's elements. + * @param list The list to call this method upon. + * @return An unmodifiable {@link Set} containing the distinct items of the list. + */ + public static Set distinct(final List list) { + return unmodifiableSet(new HashSet(list)); + } + + /** + * Provides implicit method `sorted` on the {@link List} class. + * + * Example: `"#list('c','b','a').sorted()" //should return List('a','b','c')` + * + * With implicit property support provided by {@link SpelHelper} this can + * also be written as: + * + * `"#list('c','b','a').sorted" //same output as earlier` + * @param Type of the list's elements. + * @param list The list to call this method upon. + * @return An unmodifiable {@link List} containing the sorted items + * of the list. + * @see Collections#sort(List) + */ + public static > List sorted( + final List list) { + List temp = new ArrayList(list); + Collections.sort(temp); + return unmodifiableList(temp); + } + + /** + * Provides implicit method `reversed` on the {@link List} class. + * + * Example: `"#list('c','b','a').reversed()" //should return List('a','b','c')` + * + * With implicit property support provided by {@link SpelHelper} this can + * also be written as: + * + * `"#list('c','b','a').reversed" //same output as earlier` + * @param Type of the list's elements. + * @param list The list to call this method upon. + * @return An unmodifiable {@link List} containing the items of the + * list in reverse order. + * @see Collections#reverse(List) + */ + public static List reversed(final List list) { + List temp = new ArrayList(list); + Collections.reverse(temp); + return unmodifiableList(temp); + } + + /** + * Provides implicit method `take` on the {@link List} class. + * + * Example: `"#list('c','b','a').take(2)" //should return List('a','b')` + * + * @param Type of the list's elements. + * @param list The list to call this method upon. + * @param n Number of items to _take_ from the list. + * @return An unmodifiable {@link List} containing the first `n` items + * of the list. + */ + public static List take(final List list, final int n) { + return unmodifiableList(list.subList(0, n)); + } + + /** + * Provides implicit method `drop` on the {@link List} class. + * + * Example: `"#list('c','b','a').drop(2)" //should return List('a')` + * + * @param Type of the list's elements. + * @param list The list to call this method upon. + * @param n Number of items to _drop_ from the list. + * @return An unmodifiable {@link List} containing the items after the + * first `n` items of the list. + */ + public static List drop(final List list, final int n) { + return unmodifiableList(list.subList(n, list.size())); + } + +} diff --git a/src/main/java/net/abhinavsarkar/spelhelper/ImplicitPropertyAccessor.java b/src/main/java/net/abhinavsarkar/spelhelper/ImplicitPropertyAccessor.java index bdb98ce..63c7111 100644 --- a/src/main/java/net/abhinavsarkar/spelhelper/ImplicitPropertyAccessor.java +++ b/src/main/java/net/abhinavsarkar/spelhelper/ImplicitPropertyAccessor.java @@ -1,67 +1,70 @@ -/* Copyright 2010 Abhinav Sarkar - * - * This file is a part of SpelHelper library. - * - * SpelHelper library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License (GNU LGPL) as - * published by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * SpelHelper 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SpelHelper library. If not, see . - */ -package net.abhinavsarkar.spelhelper; - -import java.text.MessageFormat; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.expression.AccessException; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.MethodExecutor; -import org.springframework.expression.MethodResolver; -import org.springframework.expression.TypedValue; -import org.springframework.util.Assert; - -final class ImplicitPropertyAccessor extends ReadOnlyGenericPropertyAccessor { - - private static final ConcurrentHashMap CACHE = - new ConcurrentHashMap(); - - public boolean canRead(final EvaluationContext context, - final Object target, final String name) - throws AccessException { - Assert.notNull(target, "target is null"); - String cacheKey = target.getClass().getName() + "." + name; - if (CACHE.containsKey(cacheKey)) { - return CACHE.get(cacheKey) != null; - } - - for (MethodResolver mr : context.getMethodResolvers()) { - MethodExecutor me = mr.resolve(context, target, name, new Class[0]); - if (me != null) { - CACHE.putIfAbsent(cacheKey, me); - return true; - } - } - - CACHE.putIfAbsent(cacheKey, null); - return false; - } - - public TypedValue read(final EvaluationContext context, - final Object target, final String name) - throws AccessException { - if (canRead(context, target, name)) { - String cacheKey = target.getClass().getName() + "." + name; - return CACHE.get(cacheKey).execute(context, target, new Object[0]); - } - throw new AccessException(MessageFormat.format( - "Cannot read property: {0} of target: {1}", name, target)); - } - +/* Copyright 2010 Abhinav Sarkar + * + * This file is a part of SpelHelper library. + * + * SpelHelper library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License (GNU LGPL) as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * SpelHelper 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SpelHelper library. If not, see . + */ +package net.abhinavsarkar.spelhelper; + +import java.text.MessageFormat; +import java.util.Collections; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.expression.AccessException; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.MethodExecutor; +import org.springframework.expression.MethodResolver; +import org.springframework.expression.TypedValue; +import org.springframework.util.Assert; + +final class ImplicitPropertyAccessor extends ReadOnlyGenericPropertyAccessor { + + private static final ConcurrentHashMap CACHE = + new ConcurrentHashMap(); + + public boolean canRead(final EvaluationContext context, + final Object target, final String name) + throws AccessException { + Assert.notNull(target, "target is null"); + String cacheKey = target.getClass().getName() + "." + name; + if (CACHE.containsKey(cacheKey)) { + return CACHE.get(cacheKey) != null; + } + + for (MethodResolver mr : context.getMethodResolvers()) { + MethodExecutor me = + mr.resolve(context, target, name, Collections.emptyList()); + if (me != null) { + CACHE.putIfAbsent(cacheKey, me); + return true; + } + } + + CACHE.putIfAbsent(cacheKey, null); + return false; + } + + public TypedValue read(final EvaluationContext context, + final Object target, final String name) + throws AccessException { + if (canRead(context, target, name)) { + String cacheKey = target.getClass().getName() + "." + name; + return CACHE.get(cacheKey).execute(context, target, new Object[0]); + } + throw new AccessException(MessageFormat.format( + "Cannot read property: {0} of target: {1}", name, target)); + } + } \ No newline at end of file diff --git a/src/main/java/net/abhinavsarkar/spelhelper/InheritenceUtil.java b/src/main/java/net/abhinavsarkar/spelhelper/InheritenceUtil.java index 4a183a5..2bdc2af 100644 --- a/src/main/java/net/abhinavsarkar/spelhelper/InheritenceUtil.java +++ b/src/main/java/net/abhinavsarkar/spelhelper/InheritenceUtil.java @@ -1,86 +1,86 @@ -/* Copyright 2010 Abhinav Sarkar - * - * This file is a part of SpelHelper library. - * - * SpelHelper library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License (GNU LGPL) as - * published by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * SpelHelper 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SpelHelper library. If not, see . - */ -package net.abhinavsarkar.spelhelper; - -import java.util.LinkedHashSet; -import java.util.Set; - -final class InheritenceUtil { - - private InheritenceUtil() { - } - - public static Set> getInheritance(final Class clazz) { - LinkedHashSet> result = new LinkedHashSet>(); - result.add(clazz); - getInheritance(clazz, result); - return result; - } - - /** - * Get inheritance of type. - * - * @param clazz - * @param result - */ - private static void getInheritance(final Class clazz, final Set> result) { - Class superclass = getSuperclass(clazz); - - if (superclass != null) { - result.add(superclass); - getInheritance(superclass, result); - } - - getInterfaceInheritance(clazz, result); - } - - /** - * Get interfaces that the type inherits from. - * - * @param clazz - * @param result - */ - private static void getInterfaceInheritance(final Class clazz, - final Set> result) { - for (Class c : clazz.getInterfaces()) { - result.add(c); - getInterfaceInheritance(c, result); - } - } - - /** - * Get superclass of class. - * - * @param clazz - * @return - */ - private static Class getSuperclass(final Class clazz) { - if (clazz == null) { - return null; - } - if (clazz.isArray() && clazz != Object[].class) { - Class type = clazz.getComponentType(); - while (type.isArray()) { - type = type.getComponentType(); - } - return type; - } - return clazz.getSuperclass(); - } - -} +/* Copyright 2010 Abhinav Sarkar + * + * This file is a part of SpelHelper library. + * + * SpelHelper library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License (GNU LGPL) as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * SpelHelper 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SpelHelper library. If not, see . + */ +package net.abhinavsarkar.spelhelper; + +import java.util.LinkedHashSet; +import java.util.Set; + +final class InheritenceUtil { + + private InheritenceUtil() { + } + + public static Set> getInheritance(final Class clazz) { + LinkedHashSet> result = new LinkedHashSet>(); + result.add(clazz); + getInheritance(clazz, result); + return result; + } + + /** + * Get inheritance of type. + * + * @param clazz + * @param result + */ + private static void getInheritance(final Class clazz, final Set> result) { + Class superclass = getSuperclass(clazz); + + if (superclass != null) { + result.add(superclass); + getInheritance(superclass, result); + } + + getInterfaceInheritance(clazz, result); + } + + /** + * Get interfaces that the type inherits from. + * + * @param clazz + * @param result + */ + private static void getInterfaceInheritance(final Class clazz, + final Set> result) { + for (Class c : clazz.getInterfaces()) { + result.add(c); + getInterfaceInheritance(c, result); + } + } + + /** + * Get superclass of class. + * + * @param clazz + * @return + */ + private static Class getSuperclass(final Class clazz) { + if (clazz == null) { + return null; + } + if (clazz.isArray() && clazz != Object[].class) { + Class type = clazz.getComponentType(); + while (type.isArray()) { + type = type.getComponentType(); + } + return type; + } + return clazz.getSuperclass(); + } + +} diff --git a/src/main/java/net/abhinavsarkar/spelhelper/ReadOnlyGenericPropertyAccessor.java b/src/main/java/net/abhinavsarkar/spelhelper/ReadOnlyGenericPropertyAccessor.java index 6afee93..1931353 100644 --- a/src/main/java/net/abhinavsarkar/spelhelper/ReadOnlyGenericPropertyAccessor.java +++ b/src/main/java/net/abhinavsarkar/spelhelper/ReadOnlyGenericPropertyAccessor.java @@ -1,45 +1,45 @@ -/* Copyright 2010 Abhinav Sarkar - * - * This file is a part of SpelHelper library. - * - * SpelHelper library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License (GNU LGPL) as - * published by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * SpelHelper 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SpelHelper library. If not, see . - */ -package net.abhinavsarkar.spelhelper; - -import java.text.MessageFormat; - -import org.springframework.expression.AccessException; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.PropertyAccessor; - -abstract class ReadOnlyGenericPropertyAccessor implements - PropertyAccessor { - - public final boolean canWrite(final EvaluationContext context, - final Object target, final String name) throws AccessException { - return false; - } - - @SuppressWarnings("rawtypes") - public final Class[] getSpecificTargetClasses() { - return null; - } - - public final void write(final EvaluationContext context, final Object target, - final String name, final Object newValue) throws AccessException { - throw new AccessException(MessageFormat.format( - "Cannot write property: {0} of target: {1}", name, target)); - } - +/* Copyright 2010 Abhinav Sarkar + * + * This file is a part of SpelHelper library. + * + * SpelHelper library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License (GNU LGPL) as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * SpelHelper 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SpelHelper library. If not, see . + */ +package net.abhinavsarkar.spelhelper; + +import java.text.MessageFormat; + +import org.springframework.expression.AccessException; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.PropertyAccessor; + +abstract class ReadOnlyGenericPropertyAccessor implements + PropertyAccessor { + + public final boolean canWrite(final EvaluationContext context, + final Object target, final String name) throws AccessException { + return false; + } + + @SuppressWarnings("rawtypes") + public final Class[] getSpecificTargetClasses() { + return null; + } + + public final void write(final EvaluationContext context, final Object target, + final String name, final Object newValue) throws AccessException { + throw new AccessException(MessageFormat.format( + "Cannot write property: {0} of target: {1}", name, target)); + } + } \ No newline at end of file diff --git a/src/main/java/net/abhinavsarkar/spelhelper/SpelHelper.java b/src/main/java/net/abhinavsarkar/spelhelper/SpelHelper.java index 8b02365..743b6eb 100644 --- a/src/main/java/net/abhinavsarkar/spelhelper/SpelHelper.java +++ b/src/main/java/net/abhinavsarkar/spelhelper/SpelHelper.java @@ -1,349 +1,349 @@ -/* Copyright 2010 Abhinav Sarkar - * - * This file is a part of SpelHelper library. - * - * SpelHelper library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License (GNU LGPL) as - * published by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * SpelHelper 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SpelHelper library. If not, see . - */ -package net.abhinavsarkar.spelhelper; - -import static java.util.Arrays.asList; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.expression.ConstructorResolver; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.Expression; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.util.Assert; - -/** - * SpelHelper provides additional functionalities to work with - * [Spring Expression Language (SpEL)][1]. - * - * The addition functionalities provided are: - * - * 1. Implicit methods - * 2. Implicit properties - * 3. Simplified extension functions - * 4. Simplified constructors - * - * **Implicit Methods** - * - * Implicit methods allow one to registers methods with SpelHelper and attach - * them to particular classes. After that, when that method is called on an - * object of that particular class inside a SpEL expression, SpelHelper - * redirects the method call to the registered method. - * - * Example: {@link ImplicitMethods#sorted(List)} method is automatically - * registered by SpelHelper. The class that the method should be invoked for - * is the type of the first parameter of the method. In this case, the class is - * {@link List}. - * - * So when an expression like `"#list(1,4,2).sorted()"` is evaluated, the - * {@link ImplicitMethods#sorted(List)} method is invoked with the list as its - * first parameter and its return value is used in further evaluation of the - * expression. - * - * See {@link SpelHelper#registerImplicitMethodsFromClass(Class)}. - * - * **Implicit Properties** - * - * Implicit properties allow one to treat no argument methods of an object - * as properties of the object. SpelHelper intercepts the property resolution - * of SpEL and if the property name is same as some no-arg method of the target - * object then it invokes the method on the object and provides its return value - * as the property value for further evaluation of the expression. - * - * Example: Using implicit properties, the example of implicit methods can be - * written as: `"#list(1,4,2).sorted"` - dropping the parens - and it will return - * the same value as the last example. - * - * Implicit property resolution considers both the actual methods of the object - * and the implicit methods registered on the object's class. - * - * **Simplified extension functions** - * - * SpEL [allows][2] to register extension function on the context by providing a - * name and a {@link Method} object. SpelHelper simplifies this by taking a class - * and registering all the `public static` methods of the class which do not - * have a `void` return type. The methods are registered by their simple name. - * - * Example: All the methods of {@link ExtensionFunctions} class are automatically - * registered by SpelHelper. Hence the method {@link ExtensionFunctions#list(Object...)} - * can be called from inside a SpEL expression using the function call syntax: - * `"#list(1,2,3)`". - * - * See {@link SpelHelper#registerFunctionsFromClass(Class)}. - * - * **Simplified constructors** - * - * SpEL [allows][3] calling constructors from inside a SpEL expression using the - * `new` operator. But they have to be called with their full name like: - * `"new org.example.Foo('bar')"`. SpelHelper simplifies this by taking a class - * and registering all its public constructors to the SpEL context by their - * simple name. - * - * Example: After registering the `org.example.Foo` class with SpelHelper, its - * constructor can be called from inside a SpEL expression by: `"new Foo('bar')"`. - * - * See {@link SpelHelper#registerConstructorsFromClass(Class)}. - * - * In addition to all the above functionalities, SpelHelper automatically registers - * some extension functions and implicit methods which are always available in - * the SpEL expressions evaluated through SpelHelper. See {@link ExtensionFunctions} - * and {@link ImplicitMethods} for further details. - * - * [1]: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html - * [2]: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html#expressions-ref-functions - * [3]: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html#d0e11927 - * - * @author Abhinav Sarkar _abhinav@abhinavsarkar.net_ - */ -public final class SpelHelper { - - static final String CONTEXT_LOOKUP_KEY = SpelHelper.class.getName(); - - private final ExpressionParser PARSER = new SpelExpressionParser(); - private static final ThreadLocal CURRENT_CONTEXT = - new ThreadLocal(); - - private final Set registeredFunctions = new HashSet(); - private final Map registeredMethods = - new ConcurrentHashMap(); - private final Map> registeredConstructors = - new ConcurrentHashMap>(); - - /** - * Creates an instance of SpelHelper. - */ - public SpelHelper() { - registerFunctionsFromClass(ExtensionFunctions.class); - registerImplicitMethodsFromClass(ImplicitMethods.class); - } - - /** - * Registers the public static methods in the class `clazz` as implicit - * methods for the class of the first parameter of the methods. - * - * Only registers the public static methods with non void return type and at - * least one argument. - * @see ImplicitMethods - * @param clazz The class to register the methods from. - * @return The current instance of SpelHelper. This is for chaining - * the methods calls. - */ - public SpelHelper registerImplicitMethodsFromClass(final Class clazz) { - for (Method method : filterMethods(clazz)) { - registeredMethods.put(String.format( - "%s.%s", method.getParameterTypes()[0].getName(), method.getName()), - method); - } - return this; - } - - /** - * Registers the public static methods in the class `clazz` as functions - * which can be called from SpEL expressions. - * The functions are registered with the simple name of the methods. - * - * Only registers the public static methods with non void return type. - * @see ExtensionFunctions - * @param clazz The class to register the functions from. - * @return The current instance of SpelHelper. This is for chaining - * the methods calls. - */ - public SpelHelper registerFunctionsFromClass(final Class clazz) { - registeredFunctions.addAll(filterFunctions(clazz)); - return this; - } - - /** - * Registers the public constructors of the class `clazz` so that they - * can be called by their simple name from SpEL expressions. - * @param clazz The class to register the constructors from. - * @return The current instance of SpelHelper. This is for chaining - * the methods calls. - */ - public SpelHelper registerConstructorsFromClass(final Class clazz) { - for (Constructor constructor : asList(clazz.getConstructors())) { - registeredConstructors.put( - constructor.getDeclaringClass().getSimpleName() - + Arrays.toString(constructor.getParameterTypes()), - constructor); - } - return this; - } - - /** - * Evaluates a SpEL expression `expressionString` in the context - * of root element `rootElement` and gives back a result of type - * `desiredType`. - * @param The type of the result desired. - * @param expressionString The SpEL expression to evaluate. - * @param rootElement The root element in context of which the expression - * is to be evaluated. - * @param desiredType The class of the result desired. - * @return The result of the evaluation of the expression. - * @see ExpressionParser#parseExpression(String) - * @see Expression#getValue(EvaluationContext, Class) - */ - public T evalExpression(final String expressionString, - final Object rootElement, final Class desiredType) { - EvaluationContext evaluationContext = getEvaluationContext(rootElement); - CURRENT_CONTEXT.set(evaluationContext); - T value = evalExpression(expressionString, evaluationContext, desiredType); - CURRENT_CONTEXT.set(null); - return value; - } - - /** - * Evaluates a SpEL expression `expressionString` in the provided - * context `evaluationContext` and gives back a result of type - * `desiredType`. - * @param The type of the result desired. - * @param expressionString The SpEL expression to evaluate. - * @param evaluationContext The context in which the expression is to be evaluated. - * @param desiredType The class of the result desired. - * @return The result of the evaluation of the expression. - * @see ExpressionParser#parseExpression(String) - * @see Expression#getValue(EvaluationContext, Class) - */ - public T evalExpression(final String expressionString, - final EvaluationContext evaluationContext, final Class desiredType) { - return PARSER.parseExpression(expressionString) - .getValue(evaluationContext, desiredType); - } - - /** - * Evaluates multiple SpEL expressions and returns the result of the last - * expression. - * @param The type of the result desired. - * @param expressionStrings The SpEL expressions to evaluate. - * @param rootElement The root element in context of which the expressions - * are to be evaluated. - * @param desiredType The class of the result desired. - * @return The result of the evaluation of the last expression. - * @see SpelHelper#evalExpression(String, EvaluationContext, Class) - * @see SpelHelper#evalExpression(String, Object, Class) - */ - public T evalExpressions(final String[] expressionStrings, - final Object rootElement, final Class desiredType) { - return evalExpressions( - expressionStrings, getEvaluationContext(rootElement), desiredType); - } - - /** - * Evaluates multiple SpEL expressions and returns the result of the last - * expression. - * @param The type of the result desired. - * @param expressionStrings The SpEL expressions to evaluate. - * @param evaluationContext The context in which the expression is to be evaluated. - * @param desiredType The class of the result desired. - * @return The result of the evaluation of the last expression. - * @see SpelHelper#evalExpression(String, EvaluationContext, Class) - * @see SpelHelper#evalExpression(String, Object, Class) - */ - public T evalExpressions(final String[] expressionStrings, - final EvaluationContext evaluationContext, final Class desiredType) { - int length = expressionStrings.length; - Assert.isTrue(length > 0, - "expressionStrings should have length more than 0"); - for (int i = 0; i < length - 1; i++) { - evalExpression(expressionStrings[i], evaluationContext, Object.class); - } - return evalExpression(expressionStrings[length - 1], - evaluationContext, desiredType); - } - - private EvaluationContext getEvaluationContext(final Object rootObject) { - StandardEvaluationContext newContext = new StandardEvaluationContext(rootObject); - newContext.getMethodResolvers().add(new ImplicitMethodResolver()); - newContext.getPropertyAccessors().add(new ImplicitPropertyAccessor()); - newContext.setConstructorResolvers( - asList((ConstructorResolver) new ImplicitConstructorResolver())); - for (Method method : registeredFunctions) { - newContext.setVariable(method.getName(), method); - } - newContext.setVariable(CONTEXT_LOOKUP_KEY, this); - return newContext; - } - - /** - * Looks up an implicit method registered with this instance. - * @param lookup key to lookup which should be of form: - * `method.getParameterTypes()[0].getName() + "." + method.getName()` - * @return The registered method if found, else null. - */ - public Method lookupImplicitMethod(final String lookup) { - Assert.notNull(lookup); - return registeredMethods.get(lookup); - } - - /** - * Looks up an implicit constructor registered with this instance. - * @param lookup key to lookup which should be of form: - * `constructor.getDeclaringClass().getSimpleName()` - * `+ Arrays.toString(constructor.getParameterTypes())` - * @return The registered constructor if found, else null. - */ - public Constructor lookupImplicitConstructor(final String lookup) { - Assert.notNull(lookup); - return registeredConstructors.get(lookup); - } - - /** - * Returns the current evaluation context. Null if there is no context. - * @return The current evaluation context. - */ - public static EvaluationContext getCurrentContext() { - return CURRENT_CONTEXT.get(); - } - - private static List filterMethods(final Class clazz) { - List allowedMethods = new ArrayList(); - for (Method method : clazz.getMethods()) { - int modifiers = method.getModifiers(); - if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) - && !method.getReturnType().equals(Void.TYPE) - && method.getParameterTypes().length > 0) { - allowedMethods.add(method); - } - } - return allowedMethods; - } - - private static List filterFunctions(final Class clazz) { - List allowedMethods = new ArrayList(); - for (Method method : clazz.getMethods()) { - int modifiers = method.getModifiers(); - if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) - && !method.getReturnType().equals(Void.TYPE)) { - allowedMethods.add(method); - } - } - return allowedMethods; - } - -} +/* Copyright 2010 Abhinav Sarkar + * + * This file is a part of SpelHelper library. + * + * SpelHelper library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License (GNU LGPL) as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * SpelHelper 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SpelHelper library. If not, see . + */ +package net.abhinavsarkar.spelhelper; + +import static java.util.Arrays.asList; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.expression.ConstructorResolver; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.util.Assert; + +/** + * SpelHelper provides additional functionalities to work with + * [Spring Expression Language (SpEL)][1]. + * + * The addition functionalities provided are: + * + * 1. Implicit methods + * 2. Implicit properties + * 3. Simplified extension functions + * 4. Simplified constructors + * + * **Implicit Methods** + * + * Implicit methods allow one to registers methods with SpelHelper and attach + * them to particular classes. After that, when that method is called on an + * object of that particular class inside a SpEL expression, SpelHelper + * redirects the method call to the registered method. + * + * Example: {@link ImplicitMethods#sorted(List)} method is automatically + * registered by SpelHelper. The class that the method should be invoked for + * is the type of the first parameter of the method. In this case, the class is + * {@link List}. + * + * So when an expression like `"#list(1,4,2).sorted()"` is evaluated, the + * {@link ImplicitMethods#sorted(List)} method is invoked with the list as its + * first parameter and its return value is used in further evaluation of the + * expression. + * + * See {@link SpelHelper#registerImplicitMethodsFromClass(Class)}. + * + * **Implicit Properties** + * + * Implicit properties allow one to treat no argument methods of an object + * as properties of the object. SpelHelper intercepts the property resolution + * of SpEL and if the property name is same as some no-arg method of the target + * object then it invokes the method on the object and provides its return value + * as the property value for further evaluation of the expression. + * + * Example: Using implicit properties, the example of implicit methods can be + * written as: `"#list(1,4,2).sorted"` - dropping the parens - and it will return + * the same value as the last example. + * + * Implicit property resolution considers both the actual methods of the object + * and the implicit methods registered on the object's class. + * + * **Simplified extension functions** + * + * SpEL [allows][2] to register extension function on the context by providing a + * name and a {@link Method} object. SpelHelper simplifies this by taking a class + * and registering all the `public static` methods of the class which do not + * have a `void` return type. The methods are registered by their simple name. + * + * Example: All the methods of {@link ExtensionFunctions} class are automatically + * registered by SpelHelper. Hence the method {@link ExtensionFunctions#list(Object...)} + * can be called from inside a SpEL expression using the function call syntax: + * `"#list(1,2,3)`". + * + * See {@link SpelHelper#registerFunctionsFromClass(Class)}. + * + * **Simplified constructors** + * + * SpEL [allows][3] calling constructors from inside a SpEL expression using the + * `new` operator. But they have to be called with their full name like: + * `"new org.example.Foo('bar')"`. SpelHelper simplifies this by taking a class + * and registering all its public constructors to the SpEL context by their + * simple name. + * + * Example: After registering the `org.example.Foo` class with SpelHelper, its + * constructor can be called from inside a SpEL expression by: `"new Foo('bar')"`. + * + * See {@link SpelHelper#registerConstructorsFromClass(Class)}. + * + * In addition to all the above functionalities, SpelHelper automatically registers + * some extension functions and implicit methods which are always available in + * the SpEL expressions evaluated through SpelHelper. See {@link ExtensionFunctions} + * and {@link ImplicitMethods} for further details. + * + * [1]: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html + * [2]: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html#expressions-ref-functions + * [3]: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html#d0e11927 + * + * @author Abhinav Sarkar _abhinav@abhinavsarkar.net_ + */ +public final class SpelHelper { + + static final String CONTEXT_LOOKUP_KEY = SpelHelper.class.getName(); + + private final ExpressionParser PARSER = new SpelExpressionParser(); + private static final ThreadLocal CURRENT_CONTEXT = + new ThreadLocal(); + + private final Set registeredFunctions = new HashSet(); + private final Map registeredMethods = + new ConcurrentHashMap(); + private final Map> registeredConstructors = + new ConcurrentHashMap>(); + + /** + * Creates an instance of SpelHelper. + */ + public SpelHelper() { + registerFunctionsFromClass(ExtensionFunctions.class); + registerImplicitMethodsFromClass(ImplicitMethods.class); + } + + /** + * Registers the public static methods in the class `clazz` as implicit + * methods for the class of the first parameter of the methods. + * + * Only registers the public static methods with non void return type and at + * least one argument. + * @see ImplicitMethods + * @param clazz The class to register the methods from. + * @return The current instance of SpelHelper. This is for chaining + * the methods calls. + */ + public SpelHelper registerImplicitMethodsFromClass(final Class clazz) { + for (Method method : filterMethods(clazz)) { + registeredMethods.put(String.format( + "%s.%s", method.getParameterTypes()[0].getName(), method.getName()), + method); + } + return this; + } + + /** + * Registers the public static methods in the class `clazz` as functions + * which can be called from SpEL expressions. + * The functions are registered with the simple name of the methods. + * + * Only registers the public static methods with non void return type. + * @see ExtensionFunctions + * @param clazz The class to register the functions from. + * @return The current instance of SpelHelper. This is for chaining + * the methods calls. + */ + public SpelHelper registerFunctionsFromClass(final Class clazz) { + registeredFunctions.addAll(filterFunctions(clazz)); + return this; + } + + /** + * Registers the public constructors of the class `clazz` so that they + * can be called by their simple name from SpEL expressions. + * @param clazz The class to register the constructors from. + * @return The current instance of SpelHelper. This is for chaining + * the methods calls. + */ + public SpelHelper registerConstructorsFromClass(final Class clazz) { + for (Constructor constructor : asList(clazz.getConstructors())) { + registeredConstructors.put( + constructor.getDeclaringClass().getSimpleName() + + Arrays.toString(constructor.getParameterTypes()), + constructor); + } + return this; + } + + /** + * Evaluates a SpEL expression `expressionString` in the context + * of root element `rootElement` and gives back a result of type + * `desiredType`. + * @param The type of the result desired. + * @param expressionString The SpEL expression to evaluate. + * @param rootElement The root element in context of which the expression + * is to be evaluated. + * @param desiredType The class of the result desired. + * @return The result of the evaluation of the expression. + * @see ExpressionParser#parseExpression(String) + * @see Expression#getValue(EvaluationContext, Class) + */ + public T evalExpression(final String expressionString, + final Object rootElement, final Class desiredType) { + EvaluationContext evaluationContext = getEvaluationContext(rootElement); + CURRENT_CONTEXT.set(evaluationContext); + T value = evalExpression(expressionString, evaluationContext, desiredType); + CURRENT_CONTEXT.set(null); + return value; + } + + /** + * Evaluates a SpEL expression `expressionString` in the provided + * context `evaluationContext` and gives back a result of type + * `desiredType`. + * @param The type of the result desired. + * @param expressionString The SpEL expression to evaluate. + * @param evaluationContext The context in which the expression is to be evaluated. + * @param desiredType The class of the result desired. + * @return The result of the evaluation of the expression. + * @see ExpressionParser#parseExpression(String) + * @see Expression#getValue(EvaluationContext, Class) + */ + public T evalExpression(final String expressionString, + final EvaluationContext evaluationContext, final Class desiredType) { + return PARSER.parseExpression(expressionString) + .getValue(evaluationContext, desiredType); + } + + /** + * Evaluates multiple SpEL expressions and returns the result of the last + * expression. + * @param The type of the result desired. + * @param expressionStrings The SpEL expressions to evaluate. + * @param rootElement The root element in context of which the expressions + * are to be evaluated. + * @param desiredType The class of the result desired. + * @return The result of the evaluation of the last expression. + * @see SpelHelper#evalExpression(String, EvaluationContext, Class) + * @see SpelHelper#evalExpression(String, Object, Class) + */ + public T evalExpressions(final String[] expressionStrings, + final Object rootElement, final Class desiredType) { + return evalExpressions( + expressionStrings, getEvaluationContext(rootElement), desiredType); + } + + /** + * Evaluates multiple SpEL expressions and returns the result of the last + * expression. + * @param The type of the result desired. + * @param expressionStrings The SpEL expressions to evaluate. + * @param evaluationContext The context in which the expression is to be evaluated. + * @param desiredType The class of the result desired. + * @return The result of the evaluation of the last expression. + * @see SpelHelper#evalExpression(String, EvaluationContext, Class) + * @see SpelHelper#evalExpression(String, Object, Class) + */ + public T evalExpressions(final String[] expressionStrings, + final EvaluationContext evaluationContext, final Class desiredType) { + int length = expressionStrings.length; + Assert.isTrue(length > 0, + "expressionStrings should have length more than 0"); + for (int i = 0; i < length - 1; i++) { + evalExpression(expressionStrings[i], evaluationContext, Object.class); + } + return evalExpression(expressionStrings[length - 1], + evaluationContext, desiredType); + } + + private EvaluationContext getEvaluationContext(final Object rootObject) { + StandardEvaluationContext newContext = new StandardEvaluationContext(rootObject); + newContext.getMethodResolvers().add(new ImplicitMethodResolver()); + newContext.getPropertyAccessors().add(new ImplicitPropertyAccessor()); + newContext.setConstructorResolvers( + asList((ConstructorResolver) new ImplicitConstructorResolver())); + for (Method method : registeredFunctions) { + newContext.setVariable(method.getName(), method); + } + newContext.setVariable(CONTEXT_LOOKUP_KEY, this); + return newContext; + } + + /** + * Looks up an implicit method registered with this instance. + * @param lookup key to lookup which should be of form: + * `method.getParameterTypes()[0].getName() + "." + method.getName()` + * @return The registered method if found, else null. + */ + public Method lookupImplicitMethod(final String lookup) { + Assert.notNull(lookup); + return registeredMethods.get(lookup); + } + + /** + * Looks up an implicit constructor registered with this instance. + * @param lookup key to lookup which should be of form: + * `constructor.getDeclaringClass().getSimpleName()` + * `+ Arrays.toString(constructor.getParameterTypes())` + * @return The registered constructor if found, else null. + */ + public Constructor lookupImplicitConstructor(final String lookup) { + Assert.notNull(lookup); + return registeredConstructors.get(lookup); + } + + /** + * Returns the current evaluation context. Null if there is no context. + * @return The current evaluation context. + */ + public static EvaluationContext getCurrentContext() { + return CURRENT_CONTEXT.get(); + } + + private static List filterMethods(final Class clazz) { + List allowedMethods = new ArrayList(); + for (Method method : clazz.getMethods()) { + int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) + && !method.getReturnType().equals(Void.TYPE) + && method.getParameterTypes().length > 0) { + allowedMethods.add(method); + } + } + return allowedMethods; + } + + private static List filterFunctions(final Class clazz) { + List allowedMethods = new ArrayList(); + for (Method method : clazz.getMethods()) { + int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) + && !method.getReturnType().equals(Void.TYPE)) { + allowedMethods.add(method); + } + } + return allowedMethods; + } + +} diff --git a/src/test/java/net/abhinavsarkar/spelhelper/Functions.java b/src/test/java/net/abhinavsarkar/spelhelper/Functions.java index 76e3efc..bce69ae 100644 --- a/src/test/java/net/abhinavsarkar/spelhelper/Functions.java +++ b/src/test/java/net/abhinavsarkar/spelhelper/Functions.java @@ -1,36 +1,36 @@ -package net.abhinavsarkar.spelhelper; - -public final class Functions { - - public static String test(final String str) { - return str; - } - - static String testNonPublic(final String str) { - return str; - } - - public String testNonStatic(final String str) { - return str; - } - - public static void testVoid(final String str) { - return; - } - - public static String testNoArg() { - return "a"; - } - - public static String testContext(final String str) { - if (SpelHelper.getCurrentContext() == null) { - throw new AssertionError(); - } - return str; - } - - @Override - public boolean equals(final Object o) { - return o instanceof Functions; - } -} +package net.abhinavsarkar.spelhelper; + +public final class Functions { + + public static String test(final String str) { + return str; + } + + static String testNonPublic(final String str) { + return str; + } + + public String testNonStatic(final String str) { + return str; + } + + public static void testVoid(final String str) { + return; + } + + public static String testNoArg() { + return "a"; + } + + public static String testContext(final String str) { + if (SpelHelper.getCurrentContext() == null) { + throw new AssertionError(); + } + return str; + } + + @Override + public boolean equals(final Object o) { + return o instanceof Functions; + } +} diff --git a/src/test/scala/net/abhinavsarkar/spelhelper/ExtensionFunctionsSpec.scala b/src/test/scala/net/abhinavsarkar/spelhelper/ExtensionFunctionsSpec.scala index 574eb3f..d19d559 100644 --- a/src/test/scala/net/abhinavsarkar/spelhelper/ExtensionFunctionsSpec.scala +++ b/src/test/scala/net/abhinavsarkar/spelhelper/ExtensionFunctionsSpec.scala @@ -1,42 +1,42 @@ -package net.abhinavsarkar.spelhelper - -import org.scalatest.junit.JUnitRunner -import org.junit.runner.RunWith -import org.scalatest.FlatSpec -import org.scalatest.junit.ShouldMatchersForJUnit -import java.util.{Set => JSet, HashSet, - List => JList, ArrayList, - Map => JMap, HashMap} -import org.springframework.expression.spel.SpelEvaluationException - -@RunWith(classOf[JUnitRunner]) -class ExtensionFunctionsSpec extends FlatSpec with ShouldMatchersForJUnit { - - "Extension Function 'list'" should "return a java.util.List " in { - val list: JList[String] = new ArrayList - List("a", "b", "c") foreach { list add _ } - new SpelHelper().evalExpression("#list('a','b','c')", - new {}, classOf[JList[String]]) should equal(list) - } - - "Extension Function 'set'" should "return a java.util.Set " in { - val set: JSet[String] = new HashSet - List("a", "b", "c") foreach { set add _ } - new SpelHelper().evalExpression("#set('a','b','c')", - new {}, classOf[JSet[String]]) should equal(set) - } - - "Extension Function 'map'" should "return a java.util.Map " in { - val map: JMap[String,Int] = new HashMap - List("a", "b", "c").zipWithIndex.foreach { x => map.put(x._1, x._2) } - new SpelHelper().evalExpression("#map(#list('a','b','c'),#list(0,1,2))", - new {}, classOf[JMap[String,Int]]) should equal(map) - } - - "Extension Function 'map'" should "throw SpelEvaluationException " + - "if length of key and values lists is not same " in { - evaluating { new SpelHelper().evalExpression("#map(#list('a','b','c'),#list(1,2))", - new {}, classOf[JMap[String,Int]]) } should produce [SpelEvaluationException] - } - -} +package net.abhinavsarkar.spelhelper + +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith +import org.scalatest.FlatSpec +import org.scalatest.junit.ShouldMatchersForJUnit +import java.util.{Set => JSet, HashSet, + List => JList, ArrayList, + Map => JMap, HashMap} +import org.springframework.expression.spel.SpelEvaluationException + +@RunWith(classOf[JUnitRunner]) +class ExtensionFunctionsSpec extends FlatSpec with ShouldMatchersForJUnit { + + "Extension Function 'list'" should "return a java.util.List " in { + val list: JList[String] = new ArrayList + List("a", "b", "c") foreach { list add _ } + new SpelHelper().evalExpression("#list('a','b','c')", + new {}, classOf[JList[String]]) should equal(list) + } + + "Extension Function 'set'" should "return a java.util.Set " in { + val set: JSet[String] = new HashSet + List("a", "b", "c") foreach { set add _ } + new SpelHelper().evalExpression("#set('a','b','c')", + new {}, classOf[JSet[String]]) should equal(set) + } + + "Extension Function 'map'" should "return a java.util.Map " in { + val map: JMap[String,Int] = new HashMap + List("a", "b", "c").zipWithIndex.foreach { x => map.put(x._1, x._2) } + new SpelHelper().evalExpression("#map(#list('a','b','c'),#list(0,1,2))", + new {}, classOf[JMap[String,Int]]) should equal(map) + } + + "Extension Function 'map'" should "throw SpelEvaluationException " + + "if length of key and values lists is not same " in { + evaluating { new SpelHelper().evalExpression("#map(#list('a','b','c'),#list(1,2))", + new {}, classOf[JMap[String,Int]]) } should produce [SpelEvaluationException] + } + +} diff --git a/src/test/scala/net/abhinavsarkar/spelhelper/ImplicitMethodsSpec.scala b/src/test/scala/net/abhinavsarkar/spelhelper/ImplicitMethodsSpec.scala index 013573b..44702ec 100644 --- a/src/test/scala/net/abhinavsarkar/spelhelper/ImplicitMethodsSpec.scala +++ b/src/test/scala/net/abhinavsarkar/spelhelper/ImplicitMethodsSpec.scala @@ -1,52 +1,52 @@ -package net.abhinavsarkar.spelhelper - -import org.scalatest.junit.JUnitRunner -import org.junit.runner.RunWith -import org.scalatest.FlatSpec -import org.scalatest.junit.ShouldMatchersForJUnit -import java.util.{HashSet, Set => JSet, List => JList, ArrayList} - -@RunWith(classOf[JUnitRunner]) -class ImplicitMethodsSpec extends FlatSpec with ShouldMatchersForJUnit { - - "Implicit Function 'distinct' on List" should - "return distinct items in a list " in { - val set: JSet[String] = new HashSet - set add "a"; set add "b" - new SpelHelper().evalExpression("#list('a','b','a').distinct()", - new {}, classOf[JSet[String]]) should equal(set) - } - - "Implicit Function 'sorted' on List" should - "return a sorted list " in { - val list: JList[String] = new ArrayList - List("a", "b", "c") foreach { list add _ } - new SpelHelper().evalExpression("#list('c','b','a').sorted()", - new {}, classOf[JList[String]]) should equal(list) - } - - "Implicit Function 'reversed' on List" should - "return a reversed list " in { - val list: JList[String] = new ArrayList - List("a", "b", "c") foreach { list add _ } - new SpelHelper().evalExpression("#list('c','b','a').reversed()", - new {}, classOf[JList[String]]) should equal(list) - } - - "Implicit Function 'take' on List" should - "return a list containing first n items of a list " in { - val list: JList[String] = new ArrayList - List("a", "b", "c") foreach { list add _ } - new SpelHelper().evalExpression("#list('a','b','c','d').take(3)", - new {}, classOf[JList[String]]) should equal(list) - } - - "Implicit Function 'drop' on List" should - "return a list containing items after the first n items of a list " in { - val list: JList[String] = new ArrayList - List("c", "d") foreach { list add _ } - new SpelHelper().evalExpression("#list('a','b','c','d').drop(2)", - new {}, classOf[JList[String]]) should equal(list) - } - -} +package net.abhinavsarkar.spelhelper + +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith +import org.scalatest.FlatSpec +import org.scalatest.junit.ShouldMatchersForJUnit +import java.util.{HashSet, Set => JSet, List => JList, ArrayList} + +@RunWith(classOf[JUnitRunner]) +class ImplicitMethodsSpec extends FlatSpec with ShouldMatchersForJUnit { + + "Implicit Function 'distinct' on List" should + "return distinct items in a list " in { + val set: JSet[String] = new HashSet + set add "a"; set add "b" + new SpelHelper().evalExpression("#list('a','b','a').distinct()", + new {}, classOf[JSet[String]]) should equal(set) + } + + "Implicit Function 'sorted' on List" should + "return a sorted list " in { + val list: JList[String] = new ArrayList + List("a", "b", "c") foreach { list add _ } + new SpelHelper().evalExpression("#list('c','b','a').sorted()", + new {}, classOf[JList[String]]) should equal(list) + } + + "Implicit Function 'reversed' on List" should + "return a reversed list " in { + val list: JList[String] = new ArrayList + List("a", "b", "c") foreach { list add _ } + new SpelHelper().evalExpression("#list('c','b','a').reversed()", + new {}, classOf[JList[String]]) should equal(list) + } + + "Implicit Function 'take' on List" should + "return a list containing first n items of a list " in { + val list: JList[String] = new ArrayList + List("a", "b", "c") foreach { list add _ } + new SpelHelper().evalExpression("#list('a','b','c','d').take(3)", + new {}, classOf[JList[String]]) should equal(list) + } + + "Implicit Function 'drop' on List" should + "return a list containing items after the first n items of a list " in { + val list: JList[String] = new ArrayList + List("c", "d") foreach { list add _ } + new SpelHelper().evalExpression("#list('a','b','c','d').drop(2)", + new {}, classOf[JList[String]]) should equal(list) + } + +} diff --git a/src/test/scala/net/abhinavsarkar/spelhelper/SpelHelperSpec.scala b/src/test/scala/net/abhinavsarkar/spelhelper/SpelHelperSpec.scala index d62e6af..5477b2a 100644 --- a/src/test/scala/net/abhinavsarkar/spelhelper/SpelHelperSpec.scala +++ b/src/test/scala/net/abhinavsarkar/spelhelper/SpelHelperSpec.scala @@ -1,103 +1,103 @@ -package net.abhinavsarkar.spelhelper - -import org.scalatest.junit.JUnitRunner -import org.junit.runner.RunWith -import org.scalatest.FlatSpec -import org.scalatest.junit.ShouldMatchersForJUnit -import org.springframework.expression.spel.SpelEvaluationException - -@RunWith(classOf[JUnitRunner]) -class SpelHelperSpec extends FlatSpec with ShouldMatchersForJUnit { - - "SpelHelper" should "register and evaluate functions " in { - new SpelHelper() - .registerFunctionsFromClass(classOf[Functions]) - .evalExpression( - "#test('check')", new {}, classOf[String]) should equal ("check") - } - - it should "not register non public methods " in { - val spelHelper = new SpelHelper() - .registerFunctionsFromClass(classOf[Functions]) - evaluating { spelHelper.evalExpression("#testNonPublic('check')", - new {}, classOf[String]) } should produce [SpelEvaluationException] - } - - it should "not register non static methods " in { - val spelHelper = new SpelHelper() - .registerFunctionsFromClass(classOf[Functions]) - evaluating { spelHelper.evalExpression("#testNonStatic('check')", - new {}, classOf[String]) } should produce [SpelEvaluationException] - } - - it should "not register void methods " in { - val spelHelper = new SpelHelper() - .registerFunctionsFromClass(classOf[Functions]) - evaluating { spelHelper.evalExpression("#testVoid('check')", - new {}, classOf[String]) } should produce [SpelEvaluationException] - } - - it should "register implicit methods " in { - new SpelHelper() - .registerImplicitMethodsFromClass(classOf[Functions]) - .lookupImplicitMethod("java.lang.String.test") should equal( - classOf[Functions].getMethod("test", classOf[String])) - } - - it should "not register methods with no args as implicit methods " in { - new SpelHelper() - .registerImplicitMethodsFromClass(classOf[Functions]) - .lookupImplicitMethod("java.lang.String.testNoArg") should be (null); - } - - it should "register implicit constructors " in { - new SpelHelper() - .registerConstructorsFromClass(classOf[Functions]) - .lookupImplicitConstructor("Functions[]") should equal( - classOf[Functions].getConstructor()) - } - - it should "evaluate implicit methods " in { - new SpelHelper() - .registerImplicitMethodsFromClass(classOf[Functions]) - .evalExpression( - "'check'.test()", new {}, classOf[String]) should equal ("check") - } - - it should "evaluate implicit constructors " in { - new SpelHelper() - .registerConstructorsFromClass(classOf[Functions]) - .evalExpression( - "new Functions()", new {}, classOf[Functions]) should equal (new Functions) - } - - it should "evaluate implicit properties " in { - new SpelHelper().evalExpression( - "'abc'.hashCode", new {}, classOf[int]) should equal ("abc".hashCode) - } - - it should "evaluate multiple expressions " in { - new SpelHelper().evalExpressions( - Array("#s='check'", "#s"), new {}, classOf[String]) should equal ("check") - } - - it should "throw IllegalArgumentException when trying to evaluate " + - "blank multiple expressions " in { - evaluating { new SpelHelper().evalExpressions( - Array[String](), new {}, classOf[String]) } should produce [IllegalArgumentException] - } - - it should "return evaluation context inside a method called " + - "from SpEL expression " in { - new SpelHelper() - .registerFunctionsFromClass(classOf[Functions]) - .evalExpression( - "#testContext('check')", new {}, classOf[String]) should equal ("check") - } - - it should "not return evaluation context outside a method called " + - "from SpEL expression " in { - SpelHelper.getCurrentContext should be (null) - } - -} +package net.abhinavsarkar.spelhelper + +import org.scalatest.junit.JUnitRunner +import org.junit.runner.RunWith +import org.scalatest.FlatSpec +import org.scalatest.junit.ShouldMatchersForJUnit +import org.springframework.expression.spel.SpelEvaluationException + +@RunWith(classOf[JUnitRunner]) +class SpelHelperSpec extends FlatSpec with ShouldMatchersForJUnit { + + "SpelHelper" should "register and evaluate functions " in { + new SpelHelper() + .registerFunctionsFromClass(classOf[Functions]) + .evalExpression( + "#test('check')", new {}, classOf[String]) should equal ("check") + } + + it should "not register non public methods " in { + val spelHelper = new SpelHelper() + .registerFunctionsFromClass(classOf[Functions]) + evaluating { spelHelper.evalExpression("#testNonPublic('check')", + new {}, classOf[String]) } should produce [SpelEvaluationException] + } + + it should "not register non static methods " in { + val spelHelper = new SpelHelper() + .registerFunctionsFromClass(classOf[Functions]) + evaluating { spelHelper.evalExpression("#testNonStatic('check')", + new {}, classOf[String]) } should produce [SpelEvaluationException] + } + + it should "not register void methods " in { + val spelHelper = new SpelHelper() + .registerFunctionsFromClass(classOf[Functions]) + evaluating { spelHelper.evalExpression("#testVoid('check')", + new {}, classOf[String]) } should produce [SpelEvaluationException] + } + + it should "register implicit methods " in { + new SpelHelper() + .registerImplicitMethodsFromClass(classOf[Functions]) + .lookupImplicitMethod("java.lang.String.test") should equal( + classOf[Functions].getMethod("test", classOf[String])) + } + + it should "not register methods with no args as implicit methods " in { + new SpelHelper() + .registerImplicitMethodsFromClass(classOf[Functions]) + .lookupImplicitMethod("java.lang.String.testNoArg") should be (null); + } + + it should "register implicit constructors " in { + new SpelHelper() + .registerConstructorsFromClass(classOf[Functions]) + .lookupImplicitConstructor("Functions[]") should equal( + classOf[Functions].getConstructor()) + } + + it should "evaluate implicit methods " in { + new SpelHelper() + .registerImplicitMethodsFromClass(classOf[Functions]) + .evalExpression( + "'check'.test()", new {}, classOf[String]) should equal ("check") + } + + it should "evaluate implicit constructors " in { + new SpelHelper() + .registerConstructorsFromClass(classOf[Functions]) + .evalExpression( + "new Functions()", new {}, classOf[Functions]) should equal (new Functions) + } + + it should "evaluate implicit properties " in { + new SpelHelper().evalExpression( + "'abc'.hashCode", new {}, classOf[int]) should equal ("abc".hashCode) + } + + it should "evaluate multiple expressions " in { + new SpelHelper().evalExpressions( + Array("#s='check'", "#s"), new {}, classOf[String]) should equal ("check") + } + + it should "throw IllegalArgumentException when trying to evaluate " + + "blank multiple expressions " in { + evaluating { new SpelHelper().evalExpressions( + Array[String](), new {}, classOf[String]) } should produce [IllegalArgumentException] + } + + it should "return evaluation context inside a method called " + + "from SpEL expression " in { + new SpelHelper() + .registerFunctionsFromClass(classOf[Functions]) + .evalExpression( + "#testContext('check')", new {}, classOf[String]) should equal ("check") + } + + it should "not return evaluation context outside a method called " + + "from SpEL expression " in { + SpelHelper.getCurrentContext should be (null) + } + +}