Updated to spring 3.0.6

master
Abhinav Sarkar 2011-10-12 18:36:00 +05:30
parent f39f6ff692
commit 9fe01a9403
14 changed files with 1185 additions and 1167 deletions

6
.gitignore vendored
View File

@ -1,2 +1,4 @@
target/* target/*
.pmd .pmd
.scala_dependencies
.settings/

View File

@ -19,6 +19,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<scala.version>2.7.7</scala.version> <scala.version>2.7.7</scala.version>
<spring.version>3.0.6.RELEASE</spring.version>
</properties> </properties>
<issueManagement> <issueManagement>
@ -189,6 +190,7 @@
<groupId>org.scala-lang</groupId> <groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId> <artifactId>scala-library</artifactId>
<version>${scala.version}</version> <version>${scala.version}</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.scalatest</groupId> <groupId>org.scalatest</groupId>
@ -205,12 +207,12 @@
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId> <artifactId>spring-core</artifactId>
<version>3.0.2.RELEASE</version> <version>${spring.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId> <artifactId>spring-expression</artifactId>
<version>3.0.2.RELEASE</version> <version>${spring.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -1,97 +1,97 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net> /* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
* *
* This file is a part of SpelHelper library. * This file is a part of SpelHelper library.
* *
* SpelHelper library is free software: you can redistribute it and/or modify * 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 * 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, * published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version. * or (at your option) any later version.
* *
* SpelHelper library is distributed in the hope that it will be useful, * SpelHelper library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>. * along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>.
*/ */
package net.abhinavsarkar.spelhelper; package net.abhinavsarkar.spelhelper;
import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet; import static java.util.Collections.unmodifiableSet;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* Provides some extension functions to create some basic collection types * Provides some extension functions to create some basic collection types
* inline in SpEL expressions. * inline in SpEL expressions.
* These functions are automatically registered with {@link SpelHelper}. * These functions are automatically registered with {@link SpelHelper}.
* *
* **See Also:** * **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) * [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_ * @author Abhinav Sarkar _abhinav@abhinavsarkar.net_
*/ */
public final class ExtensionFunctions { public final class ExtensionFunctions {
private ExtensionFunctions() { private ExtensionFunctions() {
} }
/** /**
* Creates an unmodifiable {@link List} of the arguments provided. * Creates an unmodifiable {@link List} of the arguments provided.
* *
* Example use: `"#list('one', 'two', 'three')"` * Example use: `"#list('one', 'two', 'three')"`
* @param <T> Type of the arguments provided. * @param <T> Type of the arguments provided.
* @param args Arguments to create list of. * @param args Arguments to create list of.
* @return An unmodifiable list of the arguments provided. * @return An unmodifiable list of the arguments provided.
*/ */
public static <T> List<T> list(final T... args) { public static <T> List<T> list(final T... args) {
return unmodifiableList(Arrays.asList(args)); return unmodifiableList(Arrays.asList(args));
} }
/** /**
* Creates an unmodifiable {@link Set} of the arguments provided. * Creates an unmodifiable {@link Set} of the arguments provided.
* *
* Example use: `"#set('one', 'two', 'three')"` * Example use: `"#set('one', 'two', 'three')"`
* @param <T> Type of the arguments provided. * @param <T> Type of the arguments provided.
* @param args Arguments to create set of. * @param args Arguments to create set of.
* @return An unmodifiable set of the arguments provided. * @return An unmodifiable set of the arguments provided.
*/ */
public static <T> Set<T> set(final T... args) { public static <T> Set<T> set(final T... args) {
return unmodifiableSet(new HashSet<T>(list(args))); return unmodifiableSet(new HashSet<T>(list(args)));
} }
/** /**
* Creates an unmodifiable {@link Map} using the {@link List} of keys * Creates an unmodifiable {@link Map} using the {@link List} of keys
* provided as the first argument and the {@link List} of values provided * provided as the first argument and the {@link List} of values provided
* as the second argument. * as the second argument.
* *
* Example use: `"#map(#list('one', 'two', 'three'), #list(1, 2, 3))"` * Example use: `"#map(#list('one', 'two', 'three'), #list(1, 2, 3))"`
* @param <K> Type of the keys of the map. * @param <K> Type of the keys of the map.
* @param <V> Type of the values of map. * @param <V> Type of the values of map.
* @param keys List of the keys. * @param keys List of the keys.
* @param values List of the values. * @param values List of the values.
* @return A unmodifiable map created from the key and value lists. * @return A unmodifiable map created from the key and value lists.
* @throws IllegalArgumentException if the number of keys and the number of * @throws IllegalArgumentException if the number of keys and the number of
* values is not equal. * values is not equal.
*/ */
public static <K,V> Map<K,V> map(final List<? extends K> keys, public static <K,V> Map<K,V> map(final List<? extends K> keys,
final List<? extends V> values) { final List<? extends V> values) {
Assert.isTrue(keys.size() == values.size(), Assert.isTrue(keys.size() == values.size(),
"There should be equal number of keys and values"); "There should be equal number of keys and values");
Map<K,V> map = new HashMap<K,V>(); Map<K,V> map = new HashMap<K,V>();
int length = keys.size(); int length = keys.size();
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
map.put(keys.get(i), values.get(i)); map.put(keys.get(i), values.get(i));
} }
return unmodifiableMap(map); return unmodifiableMap(map);
} }
} }

View File

@ -1,48 +1,52 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net> /* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
* *
* This file is a part of SpelHelper library. * This file is a part of SpelHelper library.
* *
* SpelHelper library is free software: you can redistribute it and/or modify * 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 * 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, * published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version. * or (at your option) any later version.
* *
* SpelHelper library is distributed in the hope that it will be useful, * SpelHelper library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>. * along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>.
*/ */
package net.abhinavsarkar.spelhelper; package net.abhinavsarkar.spelhelper;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.Arrays; import java.util.List;
import org.springframework.expression.AccessException; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.ConstructorExecutor; import org.springframework.expression.AccessException;
import org.springframework.expression.ConstructorResolver; import org.springframework.expression.ConstructorExecutor;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.spel.support.ReflectiveConstructorResolver; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.ReflectiveConstructorResolver;
final class ImplicitConstructorResolver implements
ConstructorResolver { final class ImplicitConstructorResolver implements
ConstructorResolver {
private final ReflectiveConstructorResolver delegate = new ReflectiveConstructorResolver();
private final ReflectiveConstructorResolver delegate = new ReflectiveConstructorResolver();
public ConstructorExecutor resolve(final EvaluationContext context,
final String typeName, final Class<?>[] argumentTypes) throws AccessException { @Override
try { public ConstructorExecutor resolve(
return delegate.resolve(context, typeName, argumentTypes); final EvaluationContext context, final String typeName,
} catch (AccessException ex) { final List<TypeDescriptor> argumentTypes)
Object variable = ((SpelHelper) context.lookupVariable(SpelHelper.CONTEXT_LOOKUP_KEY)) throws AccessException {
.lookupImplicitConstructor(typeName + Arrays.toString(argumentTypes)); try {
if (variable instanceof Constructor<?>) { return delegate.resolve(context, typeName, argumentTypes);
Constructor<?> constructor = (Constructor<?>) variable; } catch (AccessException ex) {
return delegate.resolve(context, constructor.getDeclaringClass().getName(), argumentTypes); Object variable = ((SpelHelper) context.lookupVariable(SpelHelper.CONTEXT_LOOKUP_KEY))
} .lookupImplicitConstructor(typeName + argumentTypes.toString());
return null; if (variable instanceof Constructor<?>) {
} Constructor<?> constructor = (Constructor<?>) variable;
} return delegate.resolve(context, constructor.getDeclaringClass().getName(), argumentTypes);
}
return null;
}
}
} }

View File

@ -1,113 +1,120 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net> /* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
* *
* This file is a part of SpelHelper library. * This file is a part of SpelHelper library.
* *
* SpelHelper library is free software: you can redistribute it and/or modify * 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 * 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, * published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version. * or (at your option) any later version.
* *
* SpelHelper library is distributed in the hope that it will be useful, * SpelHelper library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>. * along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>.
*/ */
package net.abhinavsarkar.spelhelper; package net.abhinavsarkar.spelhelper;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap; import java.util.ArrayList;
import java.util.List;
import org.springframework.expression.AccessException; import java.util.concurrent.ConcurrentHashMap;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.MethodExecutor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.MethodResolver; import org.springframework.expression.AccessException;
import org.springframework.expression.TypedValue; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.ReflectiveMethodResolver; import org.springframework.expression.MethodExecutor;
import org.springframework.expression.MethodResolver;
final class ImplicitMethodResolver implements MethodResolver { import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.support.ReflectiveMethodResolver;
private static final ConcurrentHashMap<String, MethodExecutor> CACHE =
new ConcurrentHashMap<String, MethodExecutor>(); final class ImplicitMethodResolver implements MethodResolver {
private static final MethodExecutor NULL_ME = new MethodExecutor() { private static final ConcurrentHashMap<String, MethodExecutor> CACHE =
public TypedValue execute(final EvaluationContext context, final Object target, new ConcurrentHashMap<String, MethodExecutor>();
final Object... arguments) throws AccessException {
return null; private final ReflectiveMethodResolver delegate = new ReflectiveMethodResolver();
}
}; private static final MethodExecutor NULL_ME = new MethodExecutor() {
public TypedValue execute(final EvaluationContext context, final Object target,
private static final class ImplicitMethodExecutor implements final Object... arguments) throws AccessException {
MethodExecutor { return null;
private final MethodExecutor executor; }
};
public ImplicitMethodExecutor(final MethodExecutor executor) {
this.executor = executor; private static final class ImplicitMethodExecutor implements
} MethodExecutor {
private final MethodExecutor executor;
public TypedValue execute(final EvaluationContext context, final Object target,
final Object... arguments) throws AccessException { public ImplicitMethodExecutor(final MethodExecutor executor) {
Object[] modifiedArguments = new Object[arguments.length + 1]; this.executor = executor;
modifiedArguments[0] = target; }
System.arraycopy(arguments, 0, modifiedArguments, 1, arguments.length);
return executor.execute(context, null, modifiedArguments); public TypedValue execute(final EvaluationContext context, final Object target,
} final Object... arguments) throws AccessException {
} Object[] modifiedArguments = new Object[arguments.length + 1];
modifiedArguments[0] = target;
public MethodExecutor resolve(final EvaluationContext context, System.arraycopy(arguments, 0, modifiedArguments, 1, arguments.length);
final Object targetObject, final String name, final Class<?>[] argumentTypes) return executor.execute(context, null, modifiedArguments);
throws AccessException { }
if (targetObject == null) { }
return null;
} @Override
Class<?> type = targetObject.getClass(); public MethodExecutor resolve(
String cacheKey = type.getName() + "." + name; final EvaluationContext context, final Object targetObject,
if (CACHE.containsKey(cacheKey)) { final String name, final List<TypeDescriptor> argumentTypes)
MethodExecutor executor = CACHE.get(cacheKey); throws AccessException {
return executor == NULL_ME ? null : executor; if (targetObject == null) {
} return null;
}
Method method = lookupMethod(context, type, name); Class<?> type = targetObject.getClass();
if (method != null) { String cacheKey = type.getName() + "." + name;
int modifiers = method.getModifiers(); if (CACHE.containsKey(cacheKey)) {
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) { MethodExecutor executor = CACHE.get(cacheKey);
Class<?>[] parameterTypes = method.getParameterTypes(); return executor == NULL_ME ? null : executor;
Class<?> firstParamType = parameterTypes[0]; }
if (parameterTypes.length > 0
&& firstParamType.isAssignableFrom(type)) { Method method = lookupMethod(context, type, name);
Class<?>[] newArgumentTypes = new Class[argumentTypes.length + 1]; if (method != null) {
newArgumentTypes[0] = firstParamType; int modifiers = method.getModifiers();
System.arraycopy(argumentTypes, 0, newArgumentTypes, if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) {
1, argumentTypes.length); Class<?>[] parameterTypes = method.getParameterTypes();
MethodExecutor executor = new ReflectiveMethodResolver() Class<?> firstParamType = parameterTypes[0];
.resolve(context, method.getDeclaringClass(), name, if (parameterTypes.length > 0
newArgumentTypes); && firstParamType.isAssignableFrom(type)) {
MethodExecutor wrappedExecutor = executor == null ? null List<TypeDescriptor> newArgumentTypes = new ArrayList<TypeDescriptor>();
: new ImplicitMethodExecutor(executor); newArgumentTypes.add(TypeDescriptor.valueOf(firstParamType));
if (wrappedExecutor == null) { newArgumentTypes.addAll(argumentTypes);
CACHE.putIfAbsent(cacheKey, NULL_ME);
} MethodExecutor executor =
return wrappedExecutor; delegate.resolve(context, method.getDeclaringClass(),
} name, newArgumentTypes);
} MethodExecutor wrappedExecutor = executor == null ? null
} : new ImplicitMethodExecutor(executor);
CACHE.putIfAbsent(cacheKey, NULL_ME); if (wrappedExecutor == null) {
return null; CACHE.putIfAbsent(cacheKey, NULL_ME);
} }
return wrappedExecutor;
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)) CACHE.putIfAbsent(cacheKey, NULL_ME);
.lookupImplicitMethod(clazz.getName() + "." + name); return null;
if (variable instanceof Method) { }
return (Method) variable;
} private static Method lookupMethod(final EvaluationContext context,
} final Class<?> type, final String name) {
return null; 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;
}
} }

View File

@ -1,129 +1,129 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net> /* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
* *
* This file is a part of SpelHelper library. * This file is a part of SpelHelper library.
* *
* SpelHelper library is free software: you can redistribute it and/or modify * 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 * 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, * published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version. * or (at your option) any later version.
* *
* SpelHelper library is distributed in the hope that it will be useful, * SpelHelper library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>. * along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>.
*/ */
package net.abhinavsarkar.spelhelper; package net.abhinavsarkar.spelhelper;
import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableSet; import static java.util.Collections.unmodifiableSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
/** /**
* Provides some implicit methods which can be invoked on the instances of * Provides some implicit methods which can be invoked on the instances of
* class of the first parameter of the method inside a SpEL expression. * class of the first parameter of the method inside a SpEL expression.
* @author Abhinav Sarkar _abhinav@abhinavsarkar.net_ * @author Abhinav Sarkar _abhinav@abhinavsarkar.net_
*/ */
public final class ImplicitMethods { public final class ImplicitMethods {
private ImplicitMethods() { private ImplicitMethods() {
} }
/** /**
* Provides implicit method `distinct` on the {@link List} class. * Provides implicit method `distinct` on the {@link List} class.
* *
* Example: `"#list('a','b','a').distinct()" //should return List('a','b')` * Example: `"#list('a','b','a').distinct()" //should return List('a','b')`
* *
* With implicit property support provided by {@link SpelHelper} this can * With implicit property support provided by {@link SpelHelper} this can
* also be written as: * also be written as:
* *
* `"#list('a','b','a').distinct" //same output as earlier` * `"#list('a','b','a').distinct" //same output as earlier`
* @param <T> Type of the list's elements. * @param <T> Type of the list's elements.
* @param list The list to call this method upon. * @param list The list to call this method upon.
* @return An unmodifiable {@link Set} containing the distinct items of the list. * @return An unmodifiable {@link Set} containing the distinct items of the list.
*/ */
public static <T> Set<T> distinct(final List<? extends T> list) { public static <T> Set<T> distinct(final List<? extends T> list) {
return unmodifiableSet(new HashSet<T>(list)); return unmodifiableSet(new HashSet<T>(list));
} }
/** /**
* Provides implicit method `sorted` on the {@link List} class. * Provides implicit method `sorted` on the {@link List} class.
* *
* Example: `"#list('c','b','a').sorted()" //should return List('a','b','c')` * Example: `"#list('c','b','a').sorted()" //should return List('a','b','c')`
* *
* With implicit property support provided by {@link SpelHelper} this can * With implicit property support provided by {@link SpelHelper} this can
* also be written as: * also be written as:
* *
* `"#list('c','b','a').sorted" //same output as earlier` * `"#list('c','b','a').sorted" //same output as earlier`
* @param <T> Type of the list's elements. * @param <T> Type of the list's elements.
* @param list The list to call this method upon. * @param list The list to call this method upon.
* @return An unmodifiable {@link List} containing the sorted items * @return An unmodifiable {@link List} containing the sorted items
* of the list. * of the list.
* @see Collections#sort(List) * @see Collections#sort(List)
*/ */
public static <T extends Comparable<? super T>> List<T> sorted( public static <T extends Comparable<? super T>> List<T> sorted(
final List<? extends T> list) { final List<? extends T> list) {
List<T> temp = new ArrayList<T>(list); List<T> temp = new ArrayList<T>(list);
Collections.sort(temp); Collections.sort(temp);
return unmodifiableList(temp); return unmodifiableList(temp);
} }
/** /**
* Provides implicit method `reversed` on the {@link List} class. * Provides implicit method `reversed` on the {@link List} class.
* *
* Example: `"#list('c','b','a').reversed()" //should return List('a','b','c')` * Example: `"#list('c','b','a').reversed()" //should return List('a','b','c')`
* *
* With implicit property support provided by {@link SpelHelper} this can * With implicit property support provided by {@link SpelHelper} this can
* also be written as: * also be written as:
* *
* `"#list('c','b','a').reversed" //same output as earlier` * `"#list('c','b','a').reversed" //same output as earlier`
* @param <T> Type of the list's elements. * @param <T> Type of the list's elements.
* @param list The list to call this method upon. * @param list The list to call this method upon.
* @return An unmodifiable {@link List} containing the items of the * @return An unmodifiable {@link List} containing the items of the
* list in reverse order. * list in reverse order.
* @see Collections#reverse(List) * @see Collections#reverse(List)
*/ */
public static <T> List<T> reversed(final List<? extends T> list) { public static <T> List<T> reversed(final List<? extends T> list) {
List<T> temp = new ArrayList<T>(list); List<T> temp = new ArrayList<T>(list);
Collections.reverse(temp); Collections.reverse(temp);
return unmodifiableList(temp); return unmodifiableList(temp);
} }
/** /**
* Provides implicit method `take` on the {@link List} class. * Provides implicit method `take` on the {@link List} class.
* *
* Example: `"#list('c','b','a').take(2)" //should return List('a','b')` * Example: `"#list('c','b','a').take(2)" //should return List('a','b')`
* *
* @param <T> Type of the list's elements. * @param <T> Type of the list's elements.
* @param list The list to call this method upon. * @param list The list to call this method upon.
* @param n Number of items to _take_ from the list. * @param n Number of items to _take_ from the list.
* @return An unmodifiable {@link List} containing the first `n` items * @return An unmodifiable {@link List} containing the first `n` items
* of the list. * of the list.
*/ */
public static <T> List<T> take(final List<T> list, final int n) { public static <T> List<T> take(final List<T> list, final int n) {
return unmodifiableList(list.subList(0, n)); return unmodifiableList(list.subList(0, n));
} }
/** /**
* Provides implicit method `drop` on the {@link List} class. * Provides implicit method `drop` on the {@link List} class.
* *
* Example: `"#list('c','b','a').drop(2)" //should return List('a')` * Example: `"#list('c','b','a').drop(2)" //should return List('a')`
* *
* @param <T> Type of the list's elements. * @param <T> Type of the list's elements.
* @param list The list to call this method upon. * @param list The list to call this method upon.
* @param n Number of items to _drop_ from the list. * @param n Number of items to _drop_ from the list.
* @return An unmodifiable {@link List} containing the items after the * @return An unmodifiable {@link List} containing the items after the
* first `n` items of the list. * first `n` items of the list.
*/ */
public static <T> List<T> drop(final List<T> list, final int n) { public static <T> List<T> drop(final List<T> list, final int n) {
return unmodifiableList(list.subList(n, list.size())); return unmodifiableList(list.subList(n, list.size()));
} }
} }

View File

@ -1,67 +1,70 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net> /* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
* *
* This file is a part of SpelHelper library. * This file is a part of SpelHelper library.
* *
* SpelHelper library is free software: you can redistribute it and/or modify * 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 * 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, * published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version. * or (at your option) any later version.
* *
* SpelHelper library is distributed in the hope that it will be useful, * SpelHelper library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>. * along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>.
*/ */
package net.abhinavsarkar.spelhelper; package net.abhinavsarkar.spelhelper;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.concurrent.ConcurrentHashMap; import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.MethodExecutor; import org.springframework.expression.AccessException;
import org.springframework.expression.MethodResolver; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.TypedValue; import org.springframework.expression.MethodExecutor;
import org.springframework.util.Assert; import org.springframework.expression.MethodResolver;
import org.springframework.expression.TypedValue;
final class ImplicitPropertyAccessor extends ReadOnlyGenericPropertyAccessor { import org.springframework.util.Assert;
private static final ConcurrentHashMap<String, MethodExecutor> CACHE = final class ImplicitPropertyAccessor extends ReadOnlyGenericPropertyAccessor {
new ConcurrentHashMap<String, MethodExecutor>();
private static final ConcurrentHashMap<String, MethodExecutor> CACHE =
public boolean canRead(final EvaluationContext context, new ConcurrentHashMap<String, MethodExecutor>();
final Object target, final String name)
throws AccessException { public boolean canRead(final EvaluationContext context,
Assert.notNull(target, "target is null"); final Object target, final String name)
String cacheKey = target.getClass().getName() + "." + name; throws AccessException {
if (CACHE.containsKey(cacheKey)) { Assert.notNull(target, "target is null");
return CACHE.get(cacheKey) != 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) { for (MethodResolver mr : context.getMethodResolvers()) {
CACHE.putIfAbsent(cacheKey, me); MethodExecutor me =
return true; mr.resolve(context, target, name, Collections.<TypeDescriptor>emptyList());
} if (me != null) {
} CACHE.putIfAbsent(cacheKey, me);
return true;
CACHE.putIfAbsent(cacheKey, null); }
return false; }
}
CACHE.putIfAbsent(cacheKey, null);
public TypedValue read(final EvaluationContext context, return false;
final Object target, final String name) }
throws AccessException {
if (canRead(context, target, name)) { public TypedValue read(final EvaluationContext context,
String cacheKey = target.getClass().getName() + "." + name; final Object target, final String name)
return CACHE.get(cacheKey).execute(context, target, new Object[0]); throws AccessException {
} if (canRead(context, target, name)) {
throw new AccessException(MessageFormat.format( String cacheKey = target.getClass().getName() + "." + name;
"Cannot read property: {0} of target: {1}", name, target)); return CACHE.get(cacheKey).execute(context, target, new Object[0]);
} }
throw new AccessException(MessageFormat.format(
"Cannot read property: {0} of target: {1}", name, target));
}
} }

View File

@ -1,86 +1,86 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net> /* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
* *
* This file is a part of SpelHelper library. * This file is a part of SpelHelper library.
* *
* SpelHelper library is free software: you can redistribute it and/or modify * 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 * 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, * published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version. * or (at your option) any later version.
* *
* SpelHelper library is distributed in the hope that it will be useful, * SpelHelper library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>. * along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>.
*/ */
package net.abhinavsarkar.spelhelper; package net.abhinavsarkar.spelhelper;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
final class InheritenceUtil { final class InheritenceUtil {
private InheritenceUtil() { private InheritenceUtil() {
} }
public static Set<Class<?>> getInheritance(final Class<?> clazz) { public static Set<Class<?>> getInheritance(final Class<?> clazz) {
LinkedHashSet<Class<?>> result = new LinkedHashSet<Class<?>>(); LinkedHashSet<Class<?>> result = new LinkedHashSet<Class<?>>();
result.add(clazz); result.add(clazz);
getInheritance(clazz, result); getInheritance(clazz, result);
return result; return result;
} }
/** /**
* Get inheritance of type. * Get inheritance of type.
* *
* @param clazz * @param clazz
* @param result * @param result
*/ */
private static void getInheritance(final Class<?> clazz, final Set<Class<?>> result) { private static void getInheritance(final Class<?> clazz, final Set<Class<?>> result) {
Class<?> superclass = getSuperclass(clazz); Class<?> superclass = getSuperclass(clazz);
if (superclass != null) { if (superclass != null) {
result.add(superclass); result.add(superclass);
getInheritance(superclass, result); getInheritance(superclass, result);
} }
getInterfaceInheritance(clazz, result); getInterfaceInheritance(clazz, result);
} }
/** /**
* Get interfaces that the type inherits from. * Get interfaces that the type inherits from.
* *
* @param clazz * @param clazz
* @param result * @param result
*/ */
private static void getInterfaceInheritance(final Class<?> clazz, private static void getInterfaceInheritance(final Class<?> clazz,
final Set<Class<?>> result) { final Set<Class<?>> result) {
for (Class<?> c : clazz.getInterfaces()) { for (Class<?> c : clazz.getInterfaces()) {
result.add(c); result.add(c);
getInterfaceInheritance(c, result); getInterfaceInheritance(c, result);
} }
} }
/** /**
* Get superclass of class. * Get superclass of class.
* *
* @param clazz * @param clazz
* @return * @return
*/ */
private static Class<?> getSuperclass(final Class<?> clazz) { private static Class<?> getSuperclass(final Class<?> clazz) {
if (clazz == null) { if (clazz == null) {
return null; return null;
} }
if (clazz.isArray() && clazz != Object[].class) { if (clazz.isArray() && clazz != Object[].class) {
Class<?> type = clazz.getComponentType(); Class<?> type = clazz.getComponentType();
while (type.isArray()) { while (type.isArray()) {
type = type.getComponentType(); type = type.getComponentType();
} }
return type; return type;
} }
return clazz.getSuperclass(); return clazz.getSuperclass();
} }
} }

View File

@ -1,45 +1,45 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net> /* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
* *
* This file is a part of SpelHelper library. * This file is a part of SpelHelper library.
* *
* SpelHelper library is free software: you can redistribute it and/or modify * 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 * 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, * published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version. * or (at your option) any later version.
* *
* SpelHelper library is distributed in the hope that it will be useful, * SpelHelper library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>. * along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>.
*/ */
package net.abhinavsarkar.spelhelper; package net.abhinavsarkar.spelhelper;
import java.text.MessageFormat; import java.text.MessageFormat;
import org.springframework.expression.AccessException; import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor; import org.springframework.expression.PropertyAccessor;
abstract class ReadOnlyGenericPropertyAccessor implements abstract class ReadOnlyGenericPropertyAccessor implements
PropertyAccessor { PropertyAccessor {
public final boolean canWrite(final EvaluationContext context, public final boolean canWrite(final EvaluationContext context,
final Object target, final String name) throws AccessException { final Object target, final String name) throws AccessException {
return false; return false;
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public final Class[] getSpecificTargetClasses() { public final Class[] getSpecificTargetClasses() {
return null; return null;
} }
public final void write(final EvaluationContext context, final Object target, public final void write(final EvaluationContext context, final Object target,
final String name, final Object newValue) throws AccessException { final String name, final Object newValue) throws AccessException {
throw new AccessException(MessageFormat.format( throw new AccessException(MessageFormat.format(
"Cannot write property: {0} of target: {1}", name, target)); "Cannot write property: {0} of target: {1}", name, target));
} }
} }

View File

@ -1,349 +1,349 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net> /* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
* *
* This file is a part of SpelHelper library. * This file is a part of SpelHelper library.
* *
* SpelHelper library is free software: you can redistribute it and/or modify * 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 * 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, * published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version. * or (at your option) any later version.
* *
* SpelHelper library is distributed in the hope that it will be useful, * SpelHelper library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>. * along with SpelHelper library. If not, see <http://www.gnu.org/licenses/>.
*/ */
package net.abhinavsarkar.spelhelper; package net.abhinavsarkar.spelhelper;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.springframework.expression.ConstructorResolver; import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser; import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* SpelHelper provides additional functionalities to work with * SpelHelper provides additional functionalities to work with
* [Spring Expression Language (SpEL)][1]. * [Spring Expression Language (SpEL)][1].
* *
* The addition functionalities provided are: * The addition functionalities provided are:
* *
* 1. Implicit methods * 1. Implicit methods
* 2. Implicit properties * 2. Implicit properties
* 3. Simplified extension functions * 3. Simplified extension functions
* 4. Simplified constructors * 4. Simplified constructors
* *
* **Implicit Methods** * **Implicit Methods**
* *
* Implicit methods allow one to registers methods with SpelHelper and attach * Implicit methods allow one to registers methods with SpelHelper and attach
* them to particular classes. After that, when that method is called on an * them to particular classes. After that, when that method is called on an
* object of that particular class inside a SpEL expression, SpelHelper * object of that particular class inside a SpEL expression, SpelHelper
* redirects the method call to the registered method. * redirects the method call to the registered method.
* *
* Example: {@link ImplicitMethods#sorted(List)} method is automatically * Example: {@link ImplicitMethods#sorted(List)} method is automatically
* registered by SpelHelper. The class that the method should be invoked for * 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 * is the type of the first parameter of the method. In this case, the class is
* {@link List}. * {@link List}.
* *
* So when an expression like `"#list(1,4,2).sorted()"` is evaluated, the * 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 * {@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 * first parameter and its return value is used in further evaluation of the
* expression. * expression.
* *
* See {@link SpelHelper#registerImplicitMethodsFromClass(Class)}. * See {@link SpelHelper#registerImplicitMethodsFromClass(Class)}.
* *
* **Implicit Properties** * **Implicit Properties**
* *
* Implicit properties allow one to treat no argument methods of an object * Implicit properties allow one to treat no argument methods of an object
* as properties of the object. SpelHelper intercepts the property resolution * 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 * 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 * object then it invokes the method on the object and provides its return value
* as the property value for further evaluation of the expression. * as the property value for further evaluation of the expression.
* *
* Example: Using implicit properties, the example of implicit methods can be * 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 * written as: `"#list(1,4,2).sorted"` - dropping the parens - and it will return
* the same value as the last example. * the same value as the last example.
* *
* Implicit property resolution considers both the actual methods of the object * Implicit property resolution considers both the actual methods of the object
* and the implicit methods registered on the object's class. * and the implicit methods registered on the object's class.
* *
* **Simplified extension functions** * **Simplified extension functions**
* *
* SpEL [allows][2] to register extension function on the context by providing a * 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 * 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 * 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. * have a `void` return type. The methods are registered by their simple name.
* *
* Example: All the methods of {@link ExtensionFunctions} class are automatically * Example: All the methods of {@link ExtensionFunctions} class are automatically
* registered by SpelHelper. Hence the method {@link ExtensionFunctions#list(Object...)} * registered by SpelHelper. Hence the method {@link ExtensionFunctions#list(Object...)}
* can be called from inside a SpEL expression using the function call syntax: * can be called from inside a SpEL expression using the function call syntax:
* `"#list(1,2,3)`". * `"#list(1,2,3)`".
* *
* See {@link SpelHelper#registerFunctionsFromClass(Class)}. * See {@link SpelHelper#registerFunctionsFromClass(Class)}.
* *
* **Simplified constructors** * **Simplified constructors**
* *
* SpEL [allows][3] calling constructors from inside a SpEL expression using the * 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` operator. But they have to be called with their full name like:
* `"new org.example.Foo('bar')"`. SpelHelper simplifies this by taking a class * `"new org.example.Foo('bar')"`. SpelHelper simplifies this by taking a class
* and registering all its public constructors to the SpEL context by their * and registering all its public constructors to the SpEL context by their
* simple name. * simple name.
* *
* Example: After registering the `org.example.Foo` class with SpelHelper, its * Example: After registering the `org.example.Foo` class with SpelHelper, its
* constructor can be called from inside a SpEL expression by: `"new Foo('bar')"`. * constructor can be called from inside a SpEL expression by: `"new Foo('bar')"`.
* *
* See {@link SpelHelper#registerConstructorsFromClass(Class)}. * See {@link SpelHelper#registerConstructorsFromClass(Class)}.
* *
* In addition to all the above functionalities, SpelHelper automatically registers * In addition to all the above functionalities, SpelHelper automatically registers
* some extension functions and implicit methods which are always available in * some extension functions and implicit methods which are always available in
* the SpEL expressions evaluated through SpelHelper. See {@link ExtensionFunctions} * the SpEL expressions evaluated through SpelHelper. See {@link ExtensionFunctions}
* and {@link ImplicitMethods} for further details. * and {@link ImplicitMethods} for further details.
* *
* [1]: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html * [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 * [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 * [3]: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html#d0e11927
* *
* @author Abhinav Sarkar _abhinav@abhinavsarkar.net_ * @author Abhinav Sarkar _abhinav@abhinavsarkar.net_
*/ */
public final class SpelHelper { public final class SpelHelper {
static final String CONTEXT_LOOKUP_KEY = SpelHelper.class.getName(); static final String CONTEXT_LOOKUP_KEY = SpelHelper.class.getName();
private final ExpressionParser PARSER = new SpelExpressionParser(); private final ExpressionParser PARSER = new SpelExpressionParser();
private static final ThreadLocal<EvaluationContext> CURRENT_CONTEXT = private static final ThreadLocal<EvaluationContext> CURRENT_CONTEXT =
new ThreadLocal<EvaluationContext>(); new ThreadLocal<EvaluationContext>();
private final Set<Method> registeredFunctions = new HashSet<Method>(); private final Set<Method> registeredFunctions = new HashSet<Method>();
private final Map<String,Method> registeredMethods = private final Map<String,Method> registeredMethods =
new ConcurrentHashMap<String, Method>(); new ConcurrentHashMap<String, Method>();
private final Map<String,Constructor<?>> registeredConstructors = private final Map<String,Constructor<?>> registeredConstructors =
new ConcurrentHashMap<String, Constructor<?>>(); new ConcurrentHashMap<String, Constructor<?>>();
/** /**
* Creates an instance of SpelHelper. * Creates an instance of SpelHelper.
*/ */
public SpelHelper() { public SpelHelper() {
registerFunctionsFromClass(ExtensionFunctions.class); registerFunctionsFromClass(ExtensionFunctions.class);
registerImplicitMethodsFromClass(ImplicitMethods.class); registerImplicitMethodsFromClass(ImplicitMethods.class);
} }
/** /**
* Registers the public static methods in the class `clazz` as implicit * Registers the public static methods in the class `clazz` as implicit
* methods for the class of the first parameter of the methods. * methods for the class of the first parameter of the methods.
* *
* Only registers the public static methods with non void return type and at * Only registers the public static methods with non void return type and at
* least one argument. * least one argument.
* @see ImplicitMethods * @see ImplicitMethods
* @param clazz The class to register the methods from. * @param clazz The class to register the methods from.
* @return The current instance of SpelHelper. This is for chaining * @return The current instance of SpelHelper. This is for chaining
* the methods calls. * the methods calls.
*/ */
public SpelHelper registerImplicitMethodsFromClass(final Class<?> clazz) { public SpelHelper registerImplicitMethodsFromClass(final Class<?> clazz) {
for (Method method : filterMethods(clazz)) { for (Method method : filterMethods(clazz)) {
registeredMethods.put(String.format( registeredMethods.put(String.format(
"%s.%s", method.getParameterTypes()[0].getName(), method.getName()), "%s.%s", method.getParameterTypes()[0].getName(), method.getName()),
method); method);
} }
return this; return this;
} }
/** /**
* Registers the public static methods in the class `clazz` as functions * Registers the public static methods in the class `clazz` as functions
* which can be called from SpEL expressions. * which can be called from SpEL expressions.
* The functions are registered with the simple name of the methods. * The functions are registered with the simple name of the methods.
* *
* Only registers the public static methods with non void return type. * Only registers the public static methods with non void return type.
* @see ExtensionFunctions * @see ExtensionFunctions
* @param clazz The class to register the functions from. * @param clazz The class to register the functions from.
* @return The current instance of SpelHelper. This is for chaining * @return The current instance of SpelHelper. This is for chaining
* the methods calls. * the methods calls.
*/ */
public SpelHelper registerFunctionsFromClass(final Class<?> clazz) { public SpelHelper registerFunctionsFromClass(final Class<?> clazz) {
registeredFunctions.addAll(filterFunctions(clazz)); registeredFunctions.addAll(filterFunctions(clazz));
return this; return this;
} }
/** /**
* Registers the public constructors of the class `clazz` so that they * Registers the public constructors of the class `clazz` so that they
* can be called by their simple name from SpEL expressions. * can be called by their simple name from SpEL expressions.
* @param clazz The class to register the constructors from. * @param clazz The class to register the constructors from.
* @return The current instance of SpelHelper. This is for chaining * @return The current instance of SpelHelper. This is for chaining
* the methods calls. * the methods calls.
*/ */
public SpelHelper registerConstructorsFromClass(final Class<?> clazz) { public SpelHelper registerConstructorsFromClass(final Class<?> clazz) {
for (Constructor<?> constructor : asList(clazz.getConstructors())) { for (Constructor<?> constructor : asList(clazz.getConstructors())) {
registeredConstructors.put( registeredConstructors.put(
constructor.getDeclaringClass().getSimpleName() constructor.getDeclaringClass().getSimpleName()
+ Arrays.toString(constructor.getParameterTypes()), + Arrays.toString(constructor.getParameterTypes()),
constructor); constructor);
} }
return this; return this;
} }
/** /**
* Evaluates a SpEL expression `expressionString` in the context * Evaluates a SpEL expression `expressionString` in the context
* of root element `rootElement` and gives back a result of type * of root element `rootElement` and gives back a result of type
* `desiredType`. * `desiredType`.
* @param <T> The type of the result desired. * @param <T> The type of the result desired.
* @param expressionString The SpEL expression to evaluate. * @param expressionString The SpEL expression to evaluate.
* @param rootElement The root element in context of which the expression * @param rootElement The root element in context of which the expression
* is to be evaluated. * is to be evaluated.
* @param desiredType The class of the result desired. * @param desiredType The class of the result desired.
* @return The result of the evaluation of the expression. * @return The result of the evaluation of the expression.
* @see ExpressionParser#parseExpression(String) * @see ExpressionParser#parseExpression(String)
* @see Expression#getValue(EvaluationContext, Class) * @see Expression#getValue(EvaluationContext, Class)
*/ */
public <T> T evalExpression(final String expressionString, public <T> T evalExpression(final String expressionString,
final Object rootElement, final Class<T> desiredType) { final Object rootElement, final Class<T> desiredType) {
EvaluationContext evaluationContext = getEvaluationContext(rootElement); EvaluationContext evaluationContext = getEvaluationContext(rootElement);
CURRENT_CONTEXT.set(evaluationContext); CURRENT_CONTEXT.set(evaluationContext);
T value = evalExpression(expressionString, evaluationContext, desiredType); T value = evalExpression(expressionString, evaluationContext, desiredType);
CURRENT_CONTEXT.set(null); CURRENT_CONTEXT.set(null);
return value; return value;
} }
/** /**
* Evaluates a SpEL expression `expressionString` in the provided * Evaluates a SpEL expression `expressionString` in the provided
* context `evaluationContext` and gives back a result of type * context `evaluationContext` and gives back a result of type
* `desiredType`. * `desiredType`.
* @param <T> The type of the result desired. * @param <T> The type of the result desired.
* @param expressionString The SpEL expression to evaluate. * @param expressionString The SpEL expression to evaluate.
* @param evaluationContext The context in which the expression is to be evaluated. * @param evaluationContext The context in which the expression is to be evaluated.
* @param desiredType The class of the result desired. * @param desiredType The class of the result desired.
* @return The result of the evaluation of the expression. * @return The result of the evaluation of the expression.
* @see ExpressionParser#parseExpression(String) * @see ExpressionParser#parseExpression(String)
* @see Expression#getValue(EvaluationContext, Class) * @see Expression#getValue(EvaluationContext, Class)
*/ */
public <T> T evalExpression(final String expressionString, public <T> T evalExpression(final String expressionString,
final EvaluationContext evaluationContext, final Class<T> desiredType) { final EvaluationContext evaluationContext, final Class<T> desiredType) {
return PARSER.parseExpression(expressionString) return PARSER.parseExpression(expressionString)
.getValue(evaluationContext, desiredType); .getValue(evaluationContext, desiredType);
} }
/** /**
* Evaluates multiple SpEL expressions and returns the result of the last * Evaluates multiple SpEL expressions and returns the result of the last
* expression. * expression.
* @param <T> The type of the result desired. * @param <T> The type of the result desired.
* @param expressionStrings The SpEL expressions to evaluate. * @param expressionStrings The SpEL expressions to evaluate.
* @param rootElement The root element in context of which the expressions * @param rootElement The root element in context of which the expressions
* are to be evaluated. * are to be evaluated.
* @param desiredType The class of the result desired. * @param desiredType The class of the result desired.
* @return The result of the evaluation of the last expression. * @return The result of the evaluation of the last expression.
* @see SpelHelper#evalExpression(String, EvaluationContext, Class) * @see SpelHelper#evalExpression(String, EvaluationContext, Class)
* @see SpelHelper#evalExpression(String, Object, Class) * @see SpelHelper#evalExpression(String, Object, Class)
*/ */
public <T> T evalExpressions(final String[] expressionStrings, public <T> T evalExpressions(final String[] expressionStrings,
final Object rootElement, final Class<T> desiredType) { final Object rootElement, final Class<T> desiredType) {
return evalExpressions( return evalExpressions(
expressionStrings, getEvaluationContext(rootElement), desiredType); expressionStrings, getEvaluationContext(rootElement), desiredType);
} }
/** /**
* Evaluates multiple SpEL expressions and returns the result of the last * Evaluates multiple SpEL expressions and returns the result of the last
* expression. * expression.
* @param <T> The type of the result desired. * @param <T> The type of the result desired.
* @param expressionStrings The SpEL expressions to evaluate. * @param expressionStrings The SpEL expressions to evaluate.
* @param evaluationContext The context in which the expression is to be evaluated. * @param evaluationContext The context in which the expression is to be evaluated.
* @param desiredType The class of the result desired. * @param desiredType The class of the result desired.
* @return The result of the evaluation of the last expression. * @return The result of the evaluation of the last expression.
* @see SpelHelper#evalExpression(String, EvaluationContext, Class) * @see SpelHelper#evalExpression(String, EvaluationContext, Class)
* @see SpelHelper#evalExpression(String, Object, Class) * @see SpelHelper#evalExpression(String, Object, Class)
*/ */
public <T> T evalExpressions(final String[] expressionStrings, public <T> T evalExpressions(final String[] expressionStrings,
final EvaluationContext evaluationContext, final Class<T> desiredType) { final EvaluationContext evaluationContext, final Class<T> desiredType) {
int length = expressionStrings.length; int length = expressionStrings.length;
Assert.isTrue(length > 0, Assert.isTrue(length > 0,
"expressionStrings should have length more than 0"); "expressionStrings should have length more than 0");
for (int i = 0; i < length - 1; i++) { for (int i = 0; i < length - 1; i++) {
evalExpression(expressionStrings[i], evaluationContext, Object.class); evalExpression(expressionStrings[i], evaluationContext, Object.class);
} }
return evalExpression(expressionStrings[length - 1], return evalExpression(expressionStrings[length - 1],
evaluationContext, desiredType); evaluationContext, desiredType);
} }
private EvaluationContext getEvaluationContext(final Object rootObject) { private EvaluationContext getEvaluationContext(final Object rootObject) {
StandardEvaluationContext newContext = new StandardEvaluationContext(rootObject); StandardEvaluationContext newContext = new StandardEvaluationContext(rootObject);
newContext.getMethodResolvers().add(new ImplicitMethodResolver()); newContext.getMethodResolvers().add(new ImplicitMethodResolver());
newContext.getPropertyAccessors().add(new ImplicitPropertyAccessor()); newContext.getPropertyAccessors().add(new ImplicitPropertyAccessor());
newContext.setConstructorResolvers( newContext.setConstructorResolvers(
asList((ConstructorResolver) new ImplicitConstructorResolver())); asList((ConstructorResolver) new ImplicitConstructorResolver()));
for (Method method : registeredFunctions) { for (Method method : registeredFunctions) {
newContext.setVariable(method.getName(), method); newContext.setVariable(method.getName(), method);
} }
newContext.setVariable(CONTEXT_LOOKUP_KEY, this); newContext.setVariable(CONTEXT_LOOKUP_KEY, this);
return newContext; return newContext;
} }
/** /**
* Looks up an implicit method registered with this instance. * Looks up an implicit method registered with this instance.
* @param lookup key to lookup which should be of form: * @param lookup key to lookup which should be of form:
* `method.getParameterTypes()[0].getName() + "." + method.getName()` * `method.getParameterTypes()[0].getName() + "." + method.getName()`
* @return The registered method if found, else null. * @return The registered method if found, else null.
*/ */
public Method lookupImplicitMethod(final String lookup) { public Method lookupImplicitMethod(final String lookup) {
Assert.notNull(lookup); Assert.notNull(lookup);
return registeredMethods.get(lookup); return registeredMethods.get(lookup);
} }
/** /**
* Looks up an implicit constructor registered with this instance. * Looks up an implicit constructor registered with this instance.
* @param lookup key to lookup which should be of form: * @param lookup key to lookup which should be of form:
* `constructor.getDeclaringClass().getSimpleName()` * `constructor.getDeclaringClass().getSimpleName()`
* `+ Arrays.toString(constructor.getParameterTypes())` * `+ Arrays.toString(constructor.getParameterTypes())`
* @return The registered constructor if found, else null. * @return The registered constructor if found, else null.
*/ */
public Constructor<?> lookupImplicitConstructor(final String lookup) { public Constructor<?> lookupImplicitConstructor(final String lookup) {
Assert.notNull(lookup); Assert.notNull(lookup);
return registeredConstructors.get(lookup); return registeredConstructors.get(lookup);
} }
/** /**
* Returns the current evaluation context. Null if there is no context. * Returns the current evaluation context. Null if there is no context.
* @return The current evaluation context. * @return The current evaluation context.
*/ */
public static EvaluationContext getCurrentContext() { public static EvaluationContext getCurrentContext() {
return CURRENT_CONTEXT.get(); return CURRENT_CONTEXT.get();
} }
private static List<Method> filterMethods(final Class<?> clazz) { private static List<Method> filterMethods(final Class<?> clazz) {
List<Method> allowedMethods = new ArrayList<Method>(); List<Method> allowedMethods = new ArrayList<Method>();
for (Method method : clazz.getMethods()) { for (Method method : clazz.getMethods()) {
int modifiers = method.getModifiers(); int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)
&& !method.getReturnType().equals(Void.TYPE) && !method.getReturnType().equals(Void.TYPE)
&& method.getParameterTypes().length > 0) { && method.getParameterTypes().length > 0) {
allowedMethods.add(method); allowedMethods.add(method);
} }
} }
return allowedMethods; return allowedMethods;
} }
private static List<Method> filterFunctions(final Class<?> clazz) { private static List<Method> filterFunctions(final Class<?> clazz) {
List<Method> allowedMethods = new ArrayList<Method>(); List<Method> allowedMethods = new ArrayList<Method>();
for (Method method : clazz.getMethods()) { for (Method method : clazz.getMethods()) {
int modifiers = method.getModifiers(); int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)
&& !method.getReturnType().equals(Void.TYPE)) { && !method.getReturnType().equals(Void.TYPE)) {
allowedMethods.add(method); allowedMethods.add(method);
} }
} }
return allowedMethods; return allowedMethods;
} }
} }

View File

@ -1,36 +1,36 @@
package net.abhinavsarkar.spelhelper; package net.abhinavsarkar.spelhelper;
public final class Functions { public final class Functions {
public static String test(final String str) { public static String test(final String str) {
return str; return str;
} }
static String testNonPublic(final String str) { static String testNonPublic(final String str) {
return str; return str;
} }
public String testNonStatic(final String str) { public String testNonStatic(final String str) {
return str; return str;
} }
public static void testVoid(final String str) { public static void testVoid(final String str) {
return; return;
} }
public static String testNoArg() { public static String testNoArg() {
return "a"; return "a";
} }
public static String testContext(final String str) { public static String testContext(final String str) {
if (SpelHelper.getCurrentContext() == null) { if (SpelHelper.getCurrentContext() == null) {
throw new AssertionError(); throw new AssertionError();
} }
return str; return str;
} }
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
return o instanceof Functions; return o instanceof Functions;
} }
} }

View File

@ -1,42 +1,42 @@
package net.abhinavsarkar.spelhelper package net.abhinavsarkar.spelhelper
import org.scalatest.junit.JUnitRunner import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.scalatest.FlatSpec import org.scalatest.FlatSpec
import org.scalatest.junit.ShouldMatchersForJUnit import org.scalatest.junit.ShouldMatchersForJUnit
import java.util.{Set => JSet, HashSet, import java.util.{Set => JSet, HashSet,
List => JList, ArrayList, List => JList, ArrayList,
Map => JMap, HashMap} Map => JMap, HashMap}
import org.springframework.expression.spel.SpelEvaluationException import org.springframework.expression.spel.SpelEvaluationException
@RunWith(classOf[JUnitRunner]) @RunWith(classOf[JUnitRunner])
class ExtensionFunctionsSpec extends FlatSpec with ShouldMatchersForJUnit { class ExtensionFunctionsSpec extends FlatSpec with ShouldMatchersForJUnit {
"Extension Function 'list'" should "return a java.util.List " in { "Extension Function 'list'" should "return a java.util.List " in {
val list: JList[String] = new ArrayList val list: JList[String] = new ArrayList
List("a", "b", "c") foreach { list add _ } List("a", "b", "c") foreach { list add _ }
new SpelHelper().evalExpression("#list('a','b','c')", new SpelHelper().evalExpression("#list('a','b','c')",
new {}, classOf[JList[String]]) should equal(list) new {}, classOf[JList[String]]) should equal(list)
} }
"Extension Function 'set'" should "return a java.util.Set " in { "Extension Function 'set'" should "return a java.util.Set " in {
val set: JSet[String] = new HashSet val set: JSet[String] = new HashSet
List("a", "b", "c") foreach { set add _ } List("a", "b", "c") foreach { set add _ }
new SpelHelper().evalExpression("#set('a','b','c')", new SpelHelper().evalExpression("#set('a','b','c')",
new {}, classOf[JSet[String]]) should equal(set) new {}, classOf[JSet[String]]) should equal(set)
} }
"Extension Function 'map'" should "return a java.util.Map " in { "Extension Function 'map'" should "return a java.util.Map " in {
val map: JMap[String,Int] = new HashMap val map: JMap[String,Int] = new HashMap
List("a", "b", "c").zipWithIndex.foreach { x => map.put(x._1, x._2) } 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 SpelHelper().evalExpression("#map(#list('a','b','c'),#list(0,1,2))",
new {}, classOf[JMap[String,Int]]) should equal(map) new {}, classOf[JMap[String,Int]]) should equal(map)
} }
"Extension Function 'map'" should "throw SpelEvaluationException " + "Extension Function 'map'" should "throw SpelEvaluationException " +
"if length of key and values lists is not same " in { "if length of key and values lists is not same " in {
evaluating { new SpelHelper().evalExpression("#map(#list('a','b','c'),#list(1,2))", evaluating { new SpelHelper().evalExpression("#map(#list('a','b','c'),#list(1,2))",
new {}, classOf[JMap[String,Int]]) } should produce [SpelEvaluationException] new {}, classOf[JMap[String,Int]]) } should produce [SpelEvaluationException]
} }
} }

View File

@ -1,52 +1,52 @@
package net.abhinavsarkar.spelhelper package net.abhinavsarkar.spelhelper
import org.scalatest.junit.JUnitRunner import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.scalatest.FlatSpec import org.scalatest.FlatSpec
import org.scalatest.junit.ShouldMatchersForJUnit import org.scalatest.junit.ShouldMatchersForJUnit
import java.util.{HashSet, Set => JSet, List => JList, ArrayList} import java.util.{HashSet, Set => JSet, List => JList, ArrayList}
@RunWith(classOf[JUnitRunner]) @RunWith(classOf[JUnitRunner])
class ImplicitMethodsSpec extends FlatSpec with ShouldMatchersForJUnit { class ImplicitMethodsSpec extends FlatSpec with ShouldMatchersForJUnit {
"Implicit Function 'distinct' on List" should "Implicit Function 'distinct' on List" should
"return distinct items in a list " in { "return distinct items in a list " in {
val set: JSet[String] = new HashSet val set: JSet[String] = new HashSet
set add "a"; set add "b" set add "a"; set add "b"
new SpelHelper().evalExpression("#list('a','b','a').distinct()", new SpelHelper().evalExpression("#list('a','b','a').distinct()",
new {}, classOf[JSet[String]]) should equal(set) new {}, classOf[JSet[String]]) should equal(set)
} }
"Implicit Function 'sorted' on List" should "Implicit Function 'sorted' on List" should
"return a sorted list " in { "return a sorted list " in {
val list: JList[String] = new ArrayList val list: JList[String] = new ArrayList
List("a", "b", "c") foreach { list add _ } List("a", "b", "c") foreach { list add _ }
new SpelHelper().evalExpression("#list('c','b','a').sorted()", new SpelHelper().evalExpression("#list('c','b','a').sorted()",
new {}, classOf[JList[String]]) should equal(list) new {}, classOf[JList[String]]) should equal(list)
} }
"Implicit Function 'reversed' on List" should "Implicit Function 'reversed' on List" should
"return a reversed list " in { "return a reversed list " in {
val list: JList[String] = new ArrayList val list: JList[String] = new ArrayList
List("a", "b", "c") foreach { list add _ } List("a", "b", "c") foreach { list add _ }
new SpelHelper().evalExpression("#list('c','b','a').reversed()", new SpelHelper().evalExpression("#list('c','b','a').reversed()",
new {}, classOf[JList[String]]) should equal(list) new {}, classOf[JList[String]]) should equal(list)
} }
"Implicit Function 'take' on List" should "Implicit Function 'take' on List" should
"return a list containing first n items of a list " in { "return a list containing first n items of a list " in {
val list: JList[String] = new ArrayList val list: JList[String] = new ArrayList
List("a", "b", "c") foreach { list add _ } List("a", "b", "c") foreach { list add _ }
new SpelHelper().evalExpression("#list('a','b','c','d').take(3)", new SpelHelper().evalExpression("#list('a','b','c','d').take(3)",
new {}, classOf[JList[String]]) should equal(list) new {}, classOf[JList[String]]) should equal(list)
} }
"Implicit Function 'drop' on List" should "Implicit Function 'drop' on List" should
"return a list containing items after the first n items of a list " in { "return a list containing items after the first n items of a list " in {
val list: JList[String] = new ArrayList val list: JList[String] = new ArrayList
List("c", "d") foreach { list add _ } List("c", "d") foreach { list add _ }
new SpelHelper().evalExpression("#list('a','b','c','d').drop(2)", new SpelHelper().evalExpression("#list('a','b','c','d').drop(2)",
new {}, classOf[JList[String]]) should equal(list) new {}, classOf[JList[String]]) should equal(list)
} }
} }

View File

@ -1,103 +1,103 @@
package net.abhinavsarkar.spelhelper package net.abhinavsarkar.spelhelper
import org.scalatest.junit.JUnitRunner import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.scalatest.FlatSpec import org.scalatest.FlatSpec
import org.scalatest.junit.ShouldMatchersForJUnit import org.scalatest.junit.ShouldMatchersForJUnit
import org.springframework.expression.spel.SpelEvaluationException import org.springframework.expression.spel.SpelEvaluationException
@RunWith(classOf[JUnitRunner]) @RunWith(classOf[JUnitRunner])
class SpelHelperSpec extends FlatSpec with ShouldMatchersForJUnit { class SpelHelperSpec extends FlatSpec with ShouldMatchersForJUnit {
"SpelHelper" should "register and evaluate functions " in { "SpelHelper" should "register and evaluate functions " in {
new SpelHelper() new SpelHelper()
.registerFunctionsFromClass(classOf[Functions]) .registerFunctionsFromClass(classOf[Functions])
.evalExpression( .evalExpression(
"#test('check')", new {}, classOf[String]) should equal ("check") "#test('check')", new {}, classOf[String]) should equal ("check")
} }
it should "not register non public methods " in { it should "not register non public methods " in {
val spelHelper = new SpelHelper() val spelHelper = new SpelHelper()
.registerFunctionsFromClass(classOf[Functions]) .registerFunctionsFromClass(classOf[Functions])
evaluating { spelHelper.evalExpression("#testNonPublic('check')", evaluating { spelHelper.evalExpression("#testNonPublic('check')",
new {}, classOf[String]) } should produce [SpelEvaluationException] new {}, classOf[String]) } should produce [SpelEvaluationException]
} }
it should "not register non static methods " in { it should "not register non static methods " in {
val spelHelper = new SpelHelper() val spelHelper = new SpelHelper()
.registerFunctionsFromClass(classOf[Functions]) .registerFunctionsFromClass(classOf[Functions])
evaluating { spelHelper.evalExpression("#testNonStatic('check')", evaluating { spelHelper.evalExpression("#testNonStatic('check')",
new {}, classOf[String]) } should produce [SpelEvaluationException] new {}, classOf[String]) } should produce [SpelEvaluationException]
} }
it should "not register void methods " in { it should "not register void methods " in {
val spelHelper = new SpelHelper() val spelHelper = new SpelHelper()
.registerFunctionsFromClass(classOf[Functions]) .registerFunctionsFromClass(classOf[Functions])
evaluating { spelHelper.evalExpression("#testVoid('check')", evaluating { spelHelper.evalExpression("#testVoid('check')",
new {}, classOf[String]) } should produce [SpelEvaluationException] new {}, classOf[String]) } should produce [SpelEvaluationException]
} }
it should "register implicit methods " in { it should "register implicit methods " in {
new SpelHelper() new SpelHelper()
.registerImplicitMethodsFromClass(classOf[Functions]) .registerImplicitMethodsFromClass(classOf[Functions])
.lookupImplicitMethod("java.lang.String.test") should equal( .lookupImplicitMethod("java.lang.String.test") should equal(
classOf[Functions].getMethod("test", classOf[String])) classOf[Functions].getMethod("test", classOf[String]))
} }
it should "not register methods with no args as implicit methods " in { it should "not register methods with no args as implicit methods " in {
new SpelHelper() new SpelHelper()
.registerImplicitMethodsFromClass(classOf[Functions]) .registerImplicitMethodsFromClass(classOf[Functions])
.lookupImplicitMethod("java.lang.String.testNoArg") should be (null); .lookupImplicitMethod("java.lang.String.testNoArg") should be (null);
} }
it should "register implicit constructors " in { it should "register implicit constructors " in {
new SpelHelper() new SpelHelper()
.registerConstructorsFromClass(classOf[Functions]) .registerConstructorsFromClass(classOf[Functions])
.lookupImplicitConstructor("Functions[]") should equal( .lookupImplicitConstructor("Functions[]") should equal(
classOf[Functions].getConstructor()) classOf[Functions].getConstructor())
} }
it should "evaluate implicit methods " in { it should "evaluate implicit methods " in {
new SpelHelper() new SpelHelper()
.registerImplicitMethodsFromClass(classOf[Functions]) .registerImplicitMethodsFromClass(classOf[Functions])
.evalExpression( .evalExpression(
"'check'.test()", new {}, classOf[String]) should equal ("check") "'check'.test()", new {}, classOf[String]) should equal ("check")
} }
it should "evaluate implicit constructors " in { it should "evaluate implicit constructors " in {
new SpelHelper() new SpelHelper()
.registerConstructorsFromClass(classOf[Functions]) .registerConstructorsFromClass(classOf[Functions])
.evalExpression( .evalExpression(
"new Functions()", new {}, classOf[Functions]) should equal (new Functions) "new Functions()", new {}, classOf[Functions]) should equal (new Functions)
} }
it should "evaluate implicit properties " in { it should "evaluate implicit properties " in {
new SpelHelper().evalExpression( new SpelHelper().evalExpression(
"'abc'.hashCode", new {}, classOf[int]) should equal ("abc".hashCode) "'abc'.hashCode", new {}, classOf[int]) should equal ("abc".hashCode)
} }
it should "evaluate multiple expressions " in { it should "evaluate multiple expressions " in {
new SpelHelper().evalExpressions( new SpelHelper().evalExpressions(
Array("#s='check'", "#s"), new {}, classOf[String]) should equal ("check") Array("#s='check'", "#s"), new {}, classOf[String]) should equal ("check")
} }
it should "throw IllegalArgumentException when trying to evaluate " + it should "throw IllegalArgumentException when trying to evaluate " +
"blank multiple expressions " in { "blank multiple expressions " in {
evaluating { new SpelHelper().evalExpressions( evaluating { new SpelHelper().evalExpressions(
Array[String](), new {}, classOf[String]) } should produce [IllegalArgumentException] Array[String](), new {}, classOf[String]) } should produce [IllegalArgumentException]
} }
it should "return evaluation context inside a method called " + it should "return evaluation context inside a method called " +
"from SpEL expression " in { "from SpEL expression " in {
new SpelHelper() new SpelHelper()
.registerFunctionsFromClass(classOf[Functions]) .registerFunctionsFromClass(classOf[Functions])
.evalExpression( .evalExpression(
"#testContext('check')", new {}, classOf[String]) should equal ("check") "#testContext('check')", new {}, classOf[String]) should equal ("check")
} }
it should "not return evaluation context outside a method called " + it should "not return evaluation context outside a method called " +
"from SpEL expression " in { "from SpEL expression " in {
SpelHelper.getCurrentContext should be (null) SpelHelper.getCurrentContext should be (null)
} }
} }