Browse Source

Updated to spring 3.0.6

master
Abhinav Sarkar 11 years ago
parent
commit
9fe01a9403
  1. 6
      .gitignore
  2. 6
      pom.xml
  3. 194
      src/main/java/net/abhinavsarkar/spelhelper/ExtensionFunctions.java
  4. 98
      src/main/java/net/abhinavsarkar/spelhelper/ImplicitConstructorResolver.java
  5. 231
      src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethodResolver.java
  6. 258
      src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethods.java
  7. 135
      src/main/java/net/abhinavsarkar/spelhelper/ImplicitPropertyAccessor.java
  8. 172
      src/main/java/net/abhinavsarkar/spelhelper/InheritenceUtil.java
  9. 88
      src/main/java/net/abhinavsarkar/spelhelper/ReadOnlyGenericPropertyAccessor.java
  10. 698
      src/main/java/net/abhinavsarkar/spelhelper/SpelHelper.java
  11. 72
      src/test/java/net/abhinavsarkar/spelhelper/Functions.java
  12. 84
      src/test/scala/net/abhinavsarkar/spelhelper/ExtensionFunctionsSpec.scala
  13. 104
      src/test/scala/net/abhinavsarkar/spelhelper/ImplicitMethodsSpec.scala
  14. 206
      src/test/scala/net/abhinavsarkar/spelhelper/SpelHelperSpec.scala

6
.gitignore vendored

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

6
pom.xml

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

194
src/main/java/net/abhinavsarkar/spelhelper/ExtensionFunctions.java

@ -1,97 +1,97 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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 <T> Type of the arguments provided.
* @param args Arguments to create list of.
* @return An unmodifiable list of the arguments provided.
*/
public static <T> List<T> 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 <T> Type of the arguments provided.
* @param args Arguments to create set of.
* @return An unmodifiable set of the arguments provided.
*/
public static <T> Set<T> set(final T... args) {
return unmodifiableSet(new HashSet<T>(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 <K> Type of the keys of the map.
* @param <V> 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 <K,V> Map<K,V> map(final List<? extends K> keys,
final List<? extends V> values) {
Assert.isTrue(keys.size() == values.size(),
"There should be equal number of keys and values");
Map<K,V> map = new HashMap<K,V>();
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 <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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 <T> Type of the arguments provided.
* @param args Arguments to create list of.
* @return An unmodifiable list of the arguments provided.
*/
public static <T> List<T> 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 <T> Type of the arguments provided.
* @param args Arguments to create set of.
* @return An unmodifiable set of the arguments provided.
*/
public static <T> Set<T> set(final T... args) {
return unmodifiableSet(new HashSet<T>(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 <K> Type of the keys of the map.
* @param <V> 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 <K,V> Map<K,V> map(final List<? extends K> keys,
final List<? extends V> values) {
Assert.isTrue(keys.size() == values.size(),
"There should be equal number of keys and values");
Map<K,V> map = new HashMap<K,V>();
int length = keys.size();
for (int i = 0; i < length; i++) {
map.put(keys.get(i), values.get(i));
}
return unmodifiableMap(map);
}
}

98
src/main/java/net/abhinavsarkar/spelhelper/ImplicitConstructorResolver.java

@ -1,48 +1,52 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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 <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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<TypeDescriptor> 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;
}
}
}

231
src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethodResolver.java

@ -1,113 +1,120 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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<String, MethodExecutor> CACHE =
new ConcurrentHashMap<String, MethodExecutor>();
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 <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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<String, MethodExecutor> CACHE =
new ConcurrentHashMap<String, MethodExecutor>();
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<TypeDescriptor> 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<TypeDescriptor> newArgumentTypes = new ArrayList<TypeDescriptor>();
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;
}
}

258
src/main/java/net/abhinavsarkar/spelhelper/ImplicitMethods.java

@ -1,129 +1,129 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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 <T> 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 <T> Set<T> distinct(final List<? extends T> list) {
return unmodifiableSet(new HashSet<T>(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 <T> 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 <T extends Comparable<? super T>> List<T> sorted(
final List<? extends T> list) {
List<T> temp = new ArrayList<T>(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 <T> 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 <T> List<T> reversed(final List<? extends T> list) {
List<T> temp = new ArrayList<T>(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 <T> 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 <T> List<T> take(final List<T> 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 <T> 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 <T> List<T> drop(final List<T> list, final int n) {
return unmodifiableList(list.subList(n, list.size()));
}
}
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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 <T> 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 <T> Set<T> distinct(final List<? extends T> list) {
return unmodifiableSet(new HashSet<T>(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 <T> 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 <T extends Comparable<? super T>> List<T> sorted(
final List<? extends T> list) {
List<T> temp = new ArrayList<T>(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 <T> 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 <T> List<T> reversed(final List<? extends T> list) {
List<T> temp = new ArrayList<T>(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 <T> 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 <T> List<T> take(final List<T> 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 <T> 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 <T> List<T> drop(final List<T> list, final int n) {
return unmodifiableList(list.subList(n, list.size()));
}
}

135
src/main/java/net/abhinavsarkar/spelhelper/ImplicitPropertyAccessor.java

@ -1,67 +1,70 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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<String, MethodExecutor> CACHE =
new ConcurrentHashMap<String, MethodExecutor>();
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 <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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<String, MethodExecutor> CACHE =
new ConcurrentHashMap<String, MethodExecutor>();
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.<TypeDescriptor>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));
}
}

172
src/main/java/net/abhinavsarkar/spelhelper/InheritenceUtil.java

@ -1,86 +1,86 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package net.abhinavsarkar.spelhelper;
import java.util.LinkedHashSet;
import java.util.Set;
final class InheritenceUtil {
private InheritenceUtil() {
}
public static Set<Class<?>> getInheritance(final Class<?> clazz) {
LinkedHashSet<Class<?>> result = new LinkedHashSet<Class<?>>();
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<Class<?>> 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<Class<?>> 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 <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package net.abhinavsarkar.spelhelper;
import java.util.LinkedHashSet;
import java.util.Set;
final class InheritenceUtil {
private InheritenceUtil() {
}
public static Set<Class<?>> getInheritance(final Class<?> clazz) {
LinkedHashSet<Class<?>> result = new LinkedHashSet<Class<?>>();
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<Class<?>> 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<Class<?>> 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();
}
}

88
src/main/java/net/abhinavsarkar/spelhelper/ReadOnlyGenericPropertyAccessor.java

@ -1,45 +1,45 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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 <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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));
}
}

698
src/main/java/net/abhinavsarkar/spelhelper/SpelHelper.java

@ -1,349 +1,349 @@
/* Copyright 2010 Abhinav Sarkar <abhinav@abhinavsarkar.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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.
*