From 3157b68e4a4b217082b9460ea653e62efcd012e4 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 28 Jun 2013 16:24:14 -0700 Subject: [PATCH] Polish MethodReference Polish MethodReference code formatting and remove duplicate code. --- .../expression/spel/ast/MethodReference.java | 318 ++++++++---------- 1 file changed, 140 insertions(+), 178 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java index 595bddad9de..e514529a030 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java @@ -42,122 +42,173 @@ import org.springframework.expression.spel.SpelMessage; */ public class MethodReference extends SpelNodeImpl { - private final String name; - private final boolean nullSafe; + private final String name; + private volatile CachedMethodExecutor cachedExecutor; - public MethodReference(boolean nullSafe, String methodName, int pos, SpelNodeImpl... arguments) { + + public MethodReference(boolean nullSafe, String methodName, int pos, + SpelNodeImpl... arguments) { super(pos, arguments); - this.name = methodName; this.nullSafe = nullSafe; + this.name = methodName; } + public final String getName() { return this.name; } @Override protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { - TypedValue currentContext = state.getActiveContextObject(); - Object[] arguments = new Object[getChildCount()]; - for (int i = 0; i < arguments.length; i++) { - // Make the root object the active context again for evaluating the parameter - // expressions - try { - state.pushActiveContextObject(state.getRootContextObject()); - arguments[i] = this.children[i].getValueInternal(state).getValue(); - } - finally { - state.popActiveContextObject(); - } + Object[] arguments = getArguments(state); + if (state.getActiveContextObject().getValue() == null) { + throwIfNotNullSafe(getArgumentTypes(arguments)); + return ValueRef.NullValueRef.instance; } - if (currentContext.getValue() == null) { - if (this.nullSafe) { - return ValueRef.NullValueRef.instance; - } - else { - throw new SpelEvaluationException(getStartPosition(), SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED, - FormatHelper.formatMethodForMessage(this.name, getTypes(arguments))); - } - } - return new MethodValueRef(state,state.getEvaluationContext(),state.getActiveContextObject().getValue(),arguments); + return new MethodValueRef(state); } @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { - TypedValue currentContext = state.getActiveContextObject(); - Object[] arguments = new Object[getChildCount()]; - for (int i = 0; i < arguments.length; i++) { - // Make the root object the active context again for evaluating the parameter - // expressions - try { - state.pushActiveContextObject(state.getRootContextObject()); - arguments[i] = this.children[i].getValueInternal(state).getValue(); - } - finally { - state.popActiveContextObject(); - } - } - TypedValue activeContextObject = state.getActiveContextObject(); - TypeDescriptor target = (activeContextObject == null ? null - : activeContextObject.getTypeDescriptor()); - List argumentTypes = getTypes(arguments); - if (currentContext.getValue() == null) { - if (this.nullSafe) { - return TypedValue.NULL; - } - else { - throw new SpelEvaluationException(getStartPosition(), SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED, - FormatHelper.formatMethodForMessage(this.name, argumentTypes)); - } + EvaluationContext evaluationContext = state.getEvaluationContext(); + Object value = state.getActiveContextObject().getValue(); + TypeDescriptor targetType = state.getActiveContextObject().getTypeDescriptor(); + Object[] arguments = getArguments(state); + return getValueInternal(evaluationContext, value, arguments, targetType); + } + + private TypedValue getValueInternal(EvaluationContext evaluationContext, + Object value, Object[] arguments, TypeDescriptor targetType) { + List argumentTypes = getArgumentTypes(arguments); + + if (value == null) { + throwIfNotNullSafe(argumentTypes); + return TypedValue.NULL; } - MethodExecutor executorToUse = getCachedExecutor(target, argumentTypes); + MethodExecutor executorToUse = getCachedExecutor(targetType, argumentTypes); + if (executorToUse != null) { try { - return executorToUse.execute(state.getEvaluationContext(), - state.getActiveContextObject().getValue(), arguments); + return executorToUse.execute(evaluationContext, value, arguments); } catch (AccessException ae) { // Two reasons this can occur: // 1. the method invoked actually threw a real exception - // 2. the method invoked was not passed the arguments it expected and has become 'stale' + // 2. the method invoked was not passed the arguments it expected and + // has become 'stale' - // In the first case we should not retry, in the second case we should see if there is a - // better suited method. + // In the first case we should not retry, in the second case we should see + // if there is a better suited method. - // To determine which situation it is, the AccessException will contain a cause. - // If the cause is an InvocationTargetException, a user exception was thrown inside the method. + // To determine the situation, the AccessException will contain a cause. + // If the cause is an InvocationTargetException, a user exception was + // thrown inside the method. // Otherwise the method could not be invoked. - throwSimpleExceptionIfPossible(state, ae); + throwSimpleExceptionIfPossible(value, ae); - // at this point we know it wasn't a user problem so worth a retry if a better candidate can be found + // at this point we know it wasn't a user problem so worth a retry if a + // better candidate can be found this.cachedExecutor = null; } } // either there was no accessor or it no longer existed - executorToUse = findAccessorForMethod(this.name, argumentTypes, state); - this.cachedExecutor = new CachedMethodExecutor(executorToUse, target, argumentTypes); + executorToUse = findAccessorForMethod(this.name, argumentTypes, value, evaluationContext); + this.cachedExecutor = new CachedMethodExecutor(executorToUse, targetType, + argumentTypes); try { - return executorToUse.execute(state.getEvaluationContext(), - state.getActiveContextObject().getValue(), arguments); + return executorToUse.execute(evaluationContext, + value, arguments); } - catch (AccessException ae) { + catch (AccessException ex) { // Same unwrapping exception handling as above in above catch block - throwSimpleExceptionIfPossible(state, ae); - throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION, - this.name, state.getActiveContextObject().getValue().getClass().getName(), ae.getMessage()); + throwSimpleExceptionIfPossible(value, ex); + throw new SpelEvaluationException(getStartPosition(), ex, + SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION, this.name, + value.getClass().getName(), + ex.getMessage()); } } + private void throwIfNotNullSafe(List argumentTypes) { + if (!this.nullSafe) { + throw new SpelEvaluationException(getStartPosition(), + SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED, + FormatHelper.formatMethodForMessage(this.name, argumentTypes)); + } + } + + private Object[] getArguments(ExpressionState state) { + Object[] arguments = new Object[getChildCount()]; + for (int i = 0; i < arguments.length; i++) { + // Make the root object the active context again for evaluating the parameter + // expressions + try { + state.pushActiveContextObject(state.getRootContextObject()); + arguments[i] = this.children[i].getValueInternal(state).getValue(); + } + finally { + state.popActiveContextObject(); + } + } + return arguments; + } + + private List getArgumentTypes(Object... arguments) { + List descriptors = new ArrayList(arguments.length); + for (Object argument : arguments) { + descriptors.add(TypeDescriptor.forObject(argument)); + } + return Collections.unmodifiableList(descriptors); + } + + private MethodExecutor getCachedExecutor(TypeDescriptor target, + List argumentTypes) { + if (this.cachedExecutor != null && this.cachedExecutor.isSuitable(target, argumentTypes)) { + return this.cachedExecutor.get(); + } + this.cachedExecutor = null; + return null; + } + + private MethodExecutor findAccessorForMethod(String name, + List argumentTypes, Object contextObject, + EvaluationContext evaluationContext) throws SpelEvaluationException { + + List methodResolvers = evaluationContext.getMethodResolvers(); + if (methodResolvers != null) { + for (MethodResolver methodResolver : methodResolvers) { + try { + MethodExecutor methodExecutor = methodResolver.resolve( + evaluationContext, contextObject, name, argumentTypes); + if (methodExecutor != null) { + return methodExecutor; + } + } + catch (AccessException ex) { + throw new SpelEvaluationException(getStartPosition(), ex, + SpelMessage.PROBLEM_LOCATING_METHOD, name, + contextObject.getClass()); + } + } + } + + throw new SpelEvaluationException(getStartPosition(), SpelMessage.METHOD_NOT_FOUND, + FormatHelper.formatMethodForMessage(name, argumentTypes), + FormatHelper.formatClassNameForMessage( + contextObject instanceof Class ? ((Class) contextObject) + : contextObject.getClass())); + } + /** - * Decode the AccessException, throwing a lightweight evaluation exception or, if the cause was a RuntimeException, - * throw the RuntimeException directly. + * Decode the AccessException, throwing a lightweight evaluation exception or, if the + * cause was a RuntimeException, throw the RuntimeException directly. */ - private void throwSimpleExceptionIfPossible(ExpressionState state, AccessException ae) { + private void throwSimpleExceptionIfPossible(Object value, AccessException ae) { if (ae.getCause() instanceof InvocationTargetException) { Throwable rootCause = ae.getCause().getCause(); if (rootCause instanceof RuntimeException) { @@ -165,19 +216,12 @@ public class MethodReference extends SpelNodeImpl { } throw new ExpressionInvocationTargetException(getStartPosition(), "A problem occurred when trying to execute method '" + this.name + - "' on object of type '" + state.getActiveContextObject().getValue().getClass().getName() + "'", + "' on object of type '" + + value.getClass().getName() + "'", rootCause); } } - private List getTypes(Object... arguments) { - List descriptors = new ArrayList(arguments.length); - for (Object argument : arguments) { - descriptors.add(TypeDescriptor.forObject(argument)); - } - return Collections.unmodifiableList(descriptors); - } - @Override public String toStringAST() { StringBuilder sb = new StringBuilder(); @@ -192,117 +236,31 @@ public class MethodReference extends SpelNodeImpl { return sb.toString(); } - private MethodExecutor findAccessorForMethod(String name, - List argumentTypes, ExpressionState state) - throws SpelEvaluationException { - return findAccessorForMethod(name, argumentTypes, - state.getActiveContextObject().getValue(), state.getEvaluationContext()); - } - - private MethodExecutor findAccessorForMethod(String name, - List argumentTypes, Object contextObject, EvaluationContext eContext) - throws SpelEvaluationException { - - List methodResolvers = eContext.getMethodResolvers(); - if (methodResolvers != null) { - for (MethodResolver methodResolver : methodResolvers) { - try { - MethodExecutor methodExecutor = methodResolver.resolve(eContext, - contextObject, name, argumentTypes); - if (methodExecutor != null) { - return methodExecutor; - } - } - catch (AccessException ex) { - throw new SpelEvaluationException(getStartPosition(), ex, - SpelMessage.PROBLEM_LOCATING_METHOD, name, - contextObject.getClass()); - } - } - } - throw new SpelEvaluationException( - getStartPosition(), - SpelMessage.METHOD_NOT_FOUND, - FormatHelper.formatMethodForMessage(name, argumentTypes), - FormatHelper.formatClassNameForMessage(contextObject instanceof Class ? ((Class) contextObject) - : contextObject.getClass())); - } - - private MethodExecutor getCachedExecutor(TypeDescriptor target, - List argumentTypes) { - if (this.cachedExecutor == null || !this.cachedExecutor.isSuitable(target, argumentTypes)) { - this.cachedExecutor = null; - return null; - } - return this.cachedExecutor.get(); - } private class MethodValueRef implements ValueRef { - private final ExpressionState state; - private final EvaluationContext evaluationContext; + private EvaluationContext evaluationContext; - private final Object target; + private Object value; private TypeDescriptor targetType; - private final Object[] arguments; - - private List argumentTypes; + private Object[] arguments; - MethodValueRef(ExpressionState state, EvaluationContext evaluationContext, - Object object, Object[] arguments) { - this.state = state; - this.evaluationContext = evaluationContext; - this.target = object; - this.targetType = TypeDescriptor.valueOf(target.getClass()); - this.arguments = arguments; - this.argumentTypes = getTypes(this.arguments); + MethodValueRef(ExpressionState state) { + this.evaluationContext = state.getEvaluationContext(); + this.value = state.getActiveContextObject().getValue(); + this.targetType = state.getActiveContextObject().getTypeDescriptor(); + this.arguments = getArguments(state); } @Override public TypedValue getValue() { - MethodExecutor executorToUse = getCachedExecutor(this.targetType, - this.argumentTypes); - if (executorToUse != null) { - try { - return executorToUse.execute(this.evaluationContext, this.target, this.arguments); - } - catch (AccessException ae) { - // Two reasons this can occur: - // 1. the method invoked actually threw a real exception - // 2. the method invoked was not passed the arguments it expected and has become 'stale' - - // In the first case we should not retry, in the second case we should see if there is a - // better suited method. - - // To determine which situation it is, the AccessException will contain a cause. - // If the cause is an InvocationTargetException, a user exception was thrown inside the method. - // Otherwise the method could not be invoked. - throwSimpleExceptionIfPossible(this.state, ae); - - // at this point we know it wasn't a user problem so worth a retry if a better candidate can be found - MethodReference.this.cachedExecutor = null; - } - } - - // either there was no accessor or it no longer existed - executorToUse = findAccessorForMethod(MethodReference.this.name, argumentTypes, this.target, this.evaluationContext); - MethodReference.this.cachedExecutor = new CachedMethodExecutor(executorToUse, this.targetType, this.argumentTypes); - try { - return executorToUse.execute(this.evaluationContext, this.target, this.arguments); - } - catch (AccessException ex) { - // Same unwrapping exception handling as above in above catch block - throwSimpleExceptionIfPossible(this.state, ex); - throw new SpelEvaluationException(getStartPosition(), ex, - SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION, - MethodReference.this.name, this.state.getActiveContextObject().getValue().getClass().getName(), - ex.getMessage()); - } + return MethodReference.this.getValueInternal(this.evaluationContext, + this.value, this.arguments, this.targetType); } @Override @@ -316,6 +274,7 @@ public class MethodReference extends SpelNodeImpl { } } + private static class CachedMethodExecutor { private final MethodExecutor methodExecutor; @@ -324,6 +283,7 @@ public class MethodReference extends SpelNodeImpl { private final List argumentTypes; + public CachedMethodExecutor(MethodExecutor methodExecutor, TypeDescriptor target, List argumentTypes) { this.methodExecutor = methodExecutor; @@ -331,7 +291,9 @@ public class MethodReference extends SpelNodeImpl { this.argumentTypes = argumentTypes; } - public boolean isSuitable(TypeDescriptor target, List argumentTypes) { + + public boolean isSuitable(TypeDescriptor target, + List argumentTypes) { return (this.methodExecutor != null && this.target != null && this.target.equals(target) && this.argumentTypes.equals(argumentTypes)); }