diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java index 9d006be914b..529bf0587c2 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java @@ -86,10 +86,10 @@ public class CodeFlow implements Opcodes { } /** - * @return the descriptor for the item currently on top of the stack (in the current scope) + * Return the descriptor for the item currently on top of the stack (in the current scope). */ public String lastDescriptor() { - if (this.compilationScopes.peek().size() == 0) { + if (this.compilationScopes.peek().isEmpty()) { return null; } return this.compilationScopes.peek().get(this.compilationScopes.peek().size() - 1); @@ -106,6 +106,7 @@ public class CodeFlow implements Opcodes { } } + /** * Insert any necessary cast and value call to convert from a boxed type to a * primitive value @@ -115,56 +116,56 @@ public class CodeFlow implements Opcodes { */ public static void insertUnboxInsns(MethodVisitor mv, char ch, String stackDescriptor) { switch (ch) { - case 'I': - if (!stackDescriptor.equals("Ljava/lang/Integer")) { - mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); - } - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); - break; - case 'Z': - if (!stackDescriptor.equals("Ljava/lang/Boolean")) { - mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean"); - } - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false); - break; - case 'B': - if (!stackDescriptor.equals("Ljava/lang/Byte")) { - mv.visitTypeInsn(CHECKCAST, "java/lang/Byte"); - } - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false); - break; - case 'C': - if (!stackDescriptor.equals("Ljava/lang/Character")) { - mv.visitTypeInsn(CHECKCAST, "java/lang/Character"); - } - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false); - break; - case 'D': - if (!stackDescriptor.equals("Ljava/lang/Double")) { - mv.visitTypeInsn(CHECKCAST, "java/lang/Double"); - } - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false); - break; - case 'S': - if (!stackDescriptor.equals("Ljava/lang/Short")) { - mv.visitTypeInsn(CHECKCAST, "java/lang/Short"); - } - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false); - break; - case 'F': - if (!stackDescriptor.equals("Ljava/lang/Float")) { - mv.visitTypeInsn(CHECKCAST, "java/lang/Float"); - } - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false); - break; - case 'J': - if (!stackDescriptor.equals("Ljava/lang/Long")) { - mv.visitTypeInsn(CHECKCAST, "java/lang/Long"); - } - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false); - break; - default: - throw new IllegalArgumentException("Unboxing should not be attempted for descriptor '" + ch + "'"); + case 'Z': + if (!stackDescriptor.equals("Ljava/lang/Boolean")) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean"); + } + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false); + break; + case 'B': + if (!stackDescriptor.equals("Ljava/lang/Byte")) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Byte"); + } + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false); + break; + case 'C': + if (!stackDescriptor.equals("Ljava/lang/Character")) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Character"); + } + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false); + break; + case 'D': + if (!stackDescriptor.equals("Ljava/lang/Double")) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Double"); + } + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false); + break; + case 'F': + if (!stackDescriptor.equals("Ljava/lang/Float")) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Float"); + } + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false); + break; + case 'I': + if (!stackDescriptor.equals("Ljava/lang/Integer")) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); + } + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); + break; + case 'J': + if (!stackDescriptor.equals("Ljava/lang/Long")) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Long"); + } + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false); + break; + case 'S': + if (!stackDescriptor.equals("Ljava/lang/Short")) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Short"); + } + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false); + break; + default: + throw new IllegalArgumentException("Unboxing should not be attempted for descriptor '" + ch + "'"); } } @@ -182,10 +183,10 @@ public class CodeFlow implements Opcodes { StringBuilder sb = new StringBuilder(); sb.append("("); for (Class param : params) { - sb.append(toJVMDescriptor(param)); + sb.append(toJvmDescriptor(param)); } sb.append(")"); - sb.append(toJVMDescriptor(method.getReturnType())); + sb.append(toJvmDescriptor(method.getReturnType())); return sb.toString(); } @@ -202,7 +203,7 @@ public class CodeFlow implements Opcodes { StringBuilder sb = new StringBuilder(); sb.append("("); for (Class param : params) { - sb.append(toJVMDescriptor(param)); + sb.append(toJvmDescriptor(param)); } sb.append(")V"); return sb.toString(); @@ -216,7 +217,7 @@ public class CodeFlow implements Opcodes { * @param clazz a class * @return the JVM descriptor for the class */ - public static String toJVMDescriptor(Class clazz) { + public static String toJvmDescriptor(Class clazz) { StringBuilder sb = new StringBuilder(); if (clazz.isArray()) { while (clazz.isArray()) { @@ -319,37 +320,37 @@ public class CodeFlow implements Opcodes { return true; } if (desc1.length() == 1) { - if (desc1.equals("D")) { + if (desc1.equals("Z")) { + return desc2.equals("Ljava/lang/Boolean"); + } + else if (desc1.equals("D")) { return desc2.equals("Ljava/lang/Double"); } else if (desc1.equals("F")) { return desc2.equals("Ljava/lang/Float"); } - else if (desc1.equals("J")) { - return desc2.equals("Ljava/lang/Long"); - } else if (desc1.equals("I")) { return desc2.equals("Ljava/lang/Integer"); } - else if (desc1.equals("Z")) { - return desc2.equals("Ljava/lang/Boolean"); + else if (desc1.equals("J")) { + return desc2.equals("Ljava/lang/Long"); } } else if (desc2.length() == 1) { - if (desc2.equals("D")) { + if (desc2.equals("Z")) { + return desc1.equals("Ljava/lang/Boolean"); + } + else if (desc2.equals("D")) { return desc1.equals("Ljava/lang/Double"); } else if (desc2.equals("F")) { return desc1.equals("Ljava/lang/Float"); } - else if (desc2.equals("J")) { - return desc1.equals("Ljava/lang/Long"); - } else if (desc2.equals("I")) { return desc1.equals("Ljava/lang/Integer"); } - else if (desc2.equals("Z")) { - return desc1.equals("Ljava/lang/Boolean"); + else if (desc2.equals("J")) { + return desc1.equals("Ljava/lang/Long"); } } return false; @@ -366,17 +367,10 @@ public class CodeFlow implements Opcodes { if (descriptor == null) { return false; } - if (descriptor.length( )== 1) { - return ("DFJZI".indexOf(descriptor.charAt(0)) != -1); + if (isPrimitiveOrUnboxableSupportedNumber(descriptor)) { + return true; } - if (descriptor.startsWith("Ljava/lang/")) { - if (descriptor.equals("Ljava/lang/Double") || descriptor.equals("Ljava/lang/Integer") || - descriptor.equals("Ljava/lang/Float") || descriptor.equals("Ljava/lang/Long") || - descriptor.equals("Ljava/lang/Boolean")) { - return true; - } - } - return false; + return ("Z".equals(descriptor) || descriptor.equals("Ljava/lang/Boolean")); } /** @@ -391,17 +385,27 @@ public class CodeFlow implements Opcodes { return false; } if (descriptor.length() == 1) { - return ("DFJI".indexOf(descriptor.charAt(0)) != -1); + return "DFIJ".contains(descriptor); } if (descriptor.startsWith("Ljava/lang/")) { - if (descriptor.equals("Ljava/lang/Double") || descriptor.equals("Ljava/lang/Integer") || - descriptor.equals("Ljava/lang/Float") || descriptor.equals("Ljava/lang/Long")) { + String name = descriptor.substring("Ljava/lang/".length()); + if (name.equals("Double") || name.equals("Float") || name.equals("Integer") || name.equals("Long")) { return true; } } return false; } + /** + * Determine whether the given number is to be considered as an integer + * for the purposes of a numeric operation at the bytecode level. + * @param number the number to check + * @return {@code true} if it is an {@link Integer}, {@link Short} or {@link Byte} + */ + public static boolean isIntegerForNumericOp(Number number) { + return (number instanceof Integer || number instanceof Short || number instanceof Byte); + } + /** * @param descriptor a descriptor for a type that should have a primitive representation * @return the single character descriptor for a primitive input descriptor @@ -410,23 +414,32 @@ public class CodeFlow implements Opcodes { if (descriptor.length() == 1) { return descriptor.charAt(0); } - if (descriptor.equals("Ljava/lang/Double")) { - return 'D'; + else if (descriptor.equals("Ljava/lang/Boolean")) { + return 'Z'; } - else if (descriptor.equals("Ljava/lang/Integer")) { - return 'I'; + else if (descriptor.equals("Ljava/lang/Byte")) { + return 'B'; + } + else if (descriptor.equals("Ljava/lang/Character")) { + return 'C'; + } + else if (descriptor.equals("Ljava/lang/Double")) { + return 'D'; } else if (descriptor.equals("Ljava/lang/Float")) { return 'F'; } + else if (descriptor.equals("Ljava/lang/Integer")) { + return 'I'; + } else if (descriptor.equals("Ljava/lang/Long")) { return 'J'; } - else if (descriptor.equals("Ljava/lang/Boolean")) { - return 'Z'; + else if (descriptor.equals("Ljava/lang/Short")) { + return 'S'; } else { - throw new IllegalStateException("No primitive for '"+descriptor+"'"); + throw new IllegalStateException("No primitive for '" + descriptor + "'"); } } @@ -474,37 +487,37 @@ public class CodeFlow implements Opcodes { */ public static void insertBoxIfNecessary(MethodVisitor mv, char ch) { switch (ch) { - case 'I': - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); - break; - case 'F': - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); - break; - case 'S': - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); - break; - case 'Z': - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); - break; - case 'J': - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); - break; - case 'D': - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); - break; - case 'C': - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); - break; - case 'B': - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); - break; - case 'L': + case 'Z': + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); + break; + case 'B': + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); + break; + case 'C': + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); + break; + case 'D': + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); + break; + case 'F': + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); + break; + case 'I': + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); + break; + case 'J': + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + break; + case 'S': + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); + break; + case 'L': case 'V': - case '[': - // no box needed - break; - default: - throw new IllegalArgumentException("Boxing should not be attempted for descriptor '" + ch + "'"); + case '[': + // no box needed + break; + default: + throw new IllegalArgumentException("Boxing should not be attempted for descriptor '" + ch + "'"); } } @@ -522,14 +535,14 @@ public class CodeFlow implements Opcodes { case 3: return "I"; case 4: - if (name.equals("long")) { - return "J"; + if (name.equals("byte")) { + return "B"; } else if (name.equals("char")) { return "C"; } - else if (name.equals("byte")) { - return "B"; + else if (name.equals("long")) { + return "J"; } else if (name.equals("void")) { return "V"; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/CompilablePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/CompilablePropertyAccessor.java index 6294a51b13c..7ad99309dca 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/CompilablePropertyAccessor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/CompilablePropertyAccessor.java @@ -45,8 +45,8 @@ public interface CompilablePropertyAccessor extends PropertyAccessor, Opcodes { * using context information from the codeflow where necessary. * @param propertyName the name of the property * @param mv the Asm method visitor into which code should be generated - * @param codeflow the current state of the expression compiler + * @param cf the current state of the expression compiler */ - void generateCode(String propertyName, MethodVisitor mv, CodeFlow codeflow); + void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/CompiledExpression.java b/spring-expression/src/main/java/org/springframework/expression/spel/CompiledExpression.java index 5ec0ee502c0..a6e37b49a00 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/CompiledExpression.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/CompiledExpression.java @@ -20,9 +20,9 @@ import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; /** - * Base superclass for compiled expressions. Each generated compiled expression class will - * extend this class and implement one of the getValue() methods. It is not intended - * to subclassed by user code. + * Base superclass for compiled expressions. Each generated compiled expression class + * will extend this class and implement the {@link #getValue} method. It is not intended + * to be subclassed by user code. * * @author Andy Clement * @since 4.1 diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/InternalParseException.java b/spring-expression/src/main/java/org/springframework/expression/spel/InternalParseException.java index 5a712417eb6..6ae226a5ced 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/InternalParseException.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/InternalParseException.java @@ -16,8 +16,6 @@ package org.springframework.expression.spel; -import org.springframework.expression.spel.SpelParseException; - /** * Wraps a real parse exception. This exception flows to the top parse method and then * the wrapped exception is thrown as the real problem. diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java index 06994457026..1088a98e4fa 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,16 +28,16 @@ import org.springframework.expression.TypedValue; public interface SpelNode { /** - * Evaluate the expression node in the context of the supplied expression state and - * return the value. + * Evaluate the expression node in the context of the supplied expression state + * and return the value. * @param expressionState the current expression state (includes the context) * @return the value of this node evaluated against the specified state */ Object getValue(ExpressionState expressionState) throws EvaluationException; /** - * Evaluate the expression node in the context of the supplied expression state and - * return the typed value. + * Evaluate the expression node in the context of the supplied expression state + * and return the typed value. * @param expressionState the current expression state (includes the context) * @return the type value of this node evaluated against the specified state */ @@ -45,21 +45,21 @@ public interface SpelNode { /** * Determine if this expression node will support a setValue() call. - * * @param expressionState the current expression state (includes the context) * @return true if the expression node will allow setValue() - * @throws EvaluationException if something went wrong trying to determine if the node supports writing + * @throws EvaluationException if something went wrong trying to determine + * if the node supports writing */ boolean isWritable(ExpressionState expressionState) throws EvaluationException; /** - * Evaluate the expression to a node and then set the new value on that node. For - * example, if the expression evaluates to a property reference then the property will - * be set to the new value. + * Evaluate the expression to a node and then set the new value on that node. + * For example, if the expression evaluates to a property reference, then the + * property will be set to the new value. * @param expressionState the current expression state (includes the context) * @param newValue the new value * @throws EvaluationException if any problem occurs evaluating the expression or - * setting the new value + * setting the new value */ void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException; @@ -82,8 +82,8 @@ public interface SpelNode { /** * Determine the class of the object passed in, unless it is already a class object. * @param obj the object that the caller wants the class of - * @return the class of the object if it is not already a class object, or null if the - * object is null + * @return the class of the object if it is not already a class object, + * or {@code null} if the object is {@code null} */ Class getObjectClass(Object obj); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java index 7d2c0451210..4b0398760ad 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,6 @@ import org.springframework.expression.spel.ExpressionState; */ public class Assign extends SpelNodeImpl { - public Assign(int pos,SpelNodeImpl... operands) { super(pos,operands); } @@ -46,8 +45,7 @@ public class Assign extends SpelNodeImpl { @Override public String toStringAST() { - return new StringBuilder().append(getChild(0).toStringAST()).append("=").append( - getChild(1).toStringAST()).toString(); + return getChild(0).toStringAST() + "=" + getChild(1).toStringAST(); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/AstUtils.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/AstUtils.java index 98cccc49851..9762bddbfbc 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/AstUtils.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/AstUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import org.springframework.expression.PropertyAccessor; @@ -27,7 +28,7 @@ import org.springframework.expression.PropertyAccessor; * @author Andy Clement * @since 3.0.2 */ -public class AstUtils { +public abstract class AstUtils { /** * Determines the set of property resolvers that should be used to try and access a @@ -36,28 +37,29 @@ public class AstUtils { * target type (as opposed to 'general' resolvers that could work for any type) are * placed at the start of the list. In addition, there are specific resolvers that * exactly name the class in question and resolvers that name a specific class but it - * is a supertype of the class we have. These are put at the end of the specific - * resolvers set and will be tried after exactly matching accessors but before generic - * accessors. + * is a supertype of the class we have. These are put at the end of the specific resolvers + * set and will be tried after exactly matching accessors but before generic accessors. * @param targetType the type upon which property access is being attempted * @return a list of resolvers that should be tried in order to access the property */ - public static List getPropertyAccessorsToTry(Class targetType, List propertyAccessors) { + public static List getPropertyAccessorsToTry( + Class targetType, List propertyAccessors) { + List specificAccessors = new ArrayList(); List generalAccessors = new ArrayList(); for (PropertyAccessor resolver : propertyAccessors) { Class[] targets = resolver.getSpecificTargetClasses(); - if (targets == null) { // generic resolver that says it can be used for any type + if (targets == null) { // generic resolver that says it can be used for any type generalAccessors.add(resolver); } else { if (targetType != null) { int pos = 0; for (Class clazz : targets) { - if (clazz == targetType) { // put exact matches on the front to be tried first? + if (clazz == targetType) { // put exact matches on the front to be tried first? specificAccessors.add(pos++, resolver); } - else if (clazz.isAssignableFrom(targetType)) { // put supertype matches at the end of the + else if (clazz.isAssignableFrom(targetType)) { // put supertype matches at the end of the // specificAccessor list generalAccessors.add(resolver); } @@ -65,9 +67,10 @@ public class AstUtils { } } } - List resolvers = new ArrayList(); + List resolvers = new LinkedList(); resolvers.addAll(specificAccessors); resolvers.addAll(generalAccessors); return resolvers; } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java index b0be644e1f9..5f0c84046ed 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,43 +31,40 @@ import org.springframework.expression.spel.SpelMessage; */ public class BeanReference extends SpelNodeImpl { - private final String beanname; + private final String beanName; - public BeanReference(int pos,String beanname) { + public BeanReference(int pos,String beanName) { super(pos); - this.beanname = beanname; + this.beanName = beanName; } @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { BeanResolver beanResolver = state.getEvaluationContext().getBeanResolver(); - if (beanResolver==null) { - throw new SpelEvaluationException(getStartPosition(), - SpelMessage.NO_BEAN_RESOLVER_REGISTERED, this.beanname); + if (beanResolver == null) { + throw new SpelEvaluationException( + getStartPosition(), SpelMessage.NO_BEAN_RESOLVER_REGISTERED, this.beanName); } try { - TypedValue bean = new TypedValue(beanResolver.resolve( - state.getEvaluationContext(), this.beanname)); - return bean; + return new TypedValue(beanResolver.resolve(state.getEvaluationContext(), this.beanName)); } - catch (AccessException ae) { - throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_BEAN_RESOLUTION, - this.beanname, ae.getMessage()); + catch (AccessException ex) { + throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_BEAN_RESOLUTION, + this.beanName, ex.getMessage()); } } @Override public String toStringAST() { - StringBuilder sb = new StringBuilder(); - sb.append("@"); - if (this.beanname.indexOf('.') == -1) { - sb.append(this.beanname); + StringBuilder sb = new StringBuilder("@"); + if (!this.beanName.contains(".")) { + sb.append(this.beanName); } else { - sb.append("'").append(this.beanname).append("'"); + sb.append("'").append(this.beanName).append("'"); } return sb.toString(); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java index 827e01c50ea..59afccb0556 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java @@ -48,14 +48,14 @@ public class BooleanLiteral extends Literal { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { if (this.value == BooleanTypedValue.TRUE) { mv.visitLdcInsn(1); } else { mv.visitLdcInsn(0); } - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java index e14f9977456..c934c631828 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java @@ -34,7 +34,7 @@ public class CompoundExpression extends SpelNodeImpl { public CompoundExpression(int pos,SpelNodeImpl... expressionComponents) { super(pos, expressionComponents); if (expressionComponents.length < 2) { - throw new IllegalStateException("Do not build compound expression less than one entry: " + + throw new IllegalStateException("Do not build compound expressions with less than two entries: " + expressionComponents.length); } } @@ -45,6 +45,7 @@ public class CompoundExpression extends SpelNodeImpl { if (getChildCount() == 1) { return this.children[0].getValueRef(state); } + SpelNodeImpl nextNode = this.children[0]; try { TypedValue result = nextNode.getValueInternal(state); @@ -68,10 +69,10 @@ public class CompoundExpression extends SpelNodeImpl { state.popActiveContextObject(); } } - catch (SpelEvaluationException ee) { + catch (SpelEvaluationException ex) { // Correct the position for the error before re-throwing - ee.setPosition(nextNode.getStartPosition()); - throw ee; + ex.setPosition(nextNode.getStartPosition()); + throw ex; } } @@ -85,7 +86,7 @@ public class CompoundExpression extends SpelNodeImpl { public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { ValueRef ref = getValueRef(state); TypedValue result = ref.getValue(); - this.exitTypeDescriptor = this.children[this.children.length - 1].getExitDescriptor(); + this.exitTypeDescriptor = this.children[this.children.length - 1].exitTypeDescriptor; return result; } @@ -122,17 +123,17 @@ public class CompoundExpression extends SpelNodeImpl { } @Override - public void generateCode(MethodVisitor mv,CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { // TODO could optimize T(SomeType).staticMethod - no need to generate the T() part for (int i = 0; i < this.children.length;i++) { SpelNodeImpl child = this.children[i]; if (child instanceof TypeReference && (i + 1) < this.children.length && - this.children[i+1] instanceof MethodReference) { + this.children[i + 1] instanceof MethodReference) { continue; } - child.generateCode(mv, codeflow); + child.generateCode(mv, cf); } - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java index 3b08361cde0..7b72627195d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java @@ -44,8 +44,7 @@ import org.springframework.expression.spel.support.ReflectiveConstructorExecutor * Represents the invocation of a constructor. Either a constructor on a regular type or * construction of an array. When an array is constructed, an initializer can be specified. * - *

- * Examples:
+ *

Examples:
* new String('hello world')
* new int[]{1,2,3,4}
* new int[3] new int[3]{1,2,3} @@ -121,7 +120,7 @@ public class ConstructorReference extends SpelNodeImpl { try { return executorToUse.execute(state.getEvaluationContext(), arguments); } - catch (AccessException ae) { + catch (AccessException ex) { // 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' @@ -132,83 +131,81 @@ public class ConstructorReference extends SpelNodeImpl { // 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 constructor. // Otherwise the constructor could not be invoked. - if (ae.getCause() instanceof InvocationTargetException) { + if (ex.getCause() instanceof InvocationTargetException) { // User exception was the root cause - exit now - Throwable rootCause = ae.getCause().getCause(); + Throwable rootCause = ex.getCause().getCause(); if (rootCause instanceof RuntimeException) { throw (RuntimeException) rootCause; } else { - String typename = (String) this.children[0].getValueInternal(state).getValue(); + String typeName = (String) this.children[0].getValueInternal(state).getValue(); throw new SpelEvaluationException(getStartPosition(), rootCause, - SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename, FormatHelper - .formatMethodForMessage("", argumentTypes)); + SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typeName, + FormatHelper.formatMethodForMessage("", argumentTypes)); } } - // 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 exists - String typename = (String) this.children[0].getValueInternal(state).getValue(); - executorToUse = findExecutorForConstructor(typename, argumentTypes, state); + // Either there was no accessor or it no longer exists + String typeName = (String) this.children[0].getValueInternal(state).getValue(); + executorToUse = findExecutorForConstructor(typeName, argumentTypes, state); try { this.cachedExecutor = executorToUse; if (this.cachedExecutor instanceof ReflectiveConstructorExecutor) { - this.exitTypeDescriptor = CodeFlow.toDescriptor(((ReflectiveConstructorExecutor)this.cachedExecutor).getConstructor().getDeclaringClass()); + this.exitTypeDescriptor = CodeFlow.toDescriptor( + ((ReflectiveConstructorExecutor) this.cachedExecutor).getConstructor().getDeclaringClass()); } return executorToUse.execute(state.getEvaluationContext(), arguments); } - catch (AccessException ae) { - throw new SpelEvaluationException(getStartPosition(), ae, - SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename, + catch (AccessException ex) { + throw new SpelEvaluationException(getStartPosition(), ex, + SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typeName, FormatHelper.formatMethodForMessage("", argumentTypes)); } } /** - * Go through the list of registered constructor resolvers and see if any can find a constructor that takes the - * specified set of arguments. - * @param typename the type trying to be constructed + * Go through the list of registered constructor resolvers and see if any can find a + * constructor that takes the specified set of arguments. + * @param typeName the type trying to be constructed * @param argumentTypes the types of the arguments supplied that the constructor must take * @param state the current state of the expression * @return a reusable ConstructorExecutor that can be invoked to run the constructor or null * @throws SpelEvaluationException if there is a problem locating the constructor */ - private ConstructorExecutor findExecutorForConstructor(String typename, + private ConstructorExecutor findExecutorForConstructor(String typeName, List argumentTypes, ExpressionState state) throws SpelEvaluationException { - EvaluationContext eContext = state.getEvaluationContext(); - List cResolvers = eContext.getConstructorResolvers(); - if (cResolvers != null) { - for (ConstructorResolver ctorResolver : cResolvers) { + EvaluationContext evalContext = state.getEvaluationContext(); + List ctorResolvers = evalContext.getConstructorResolvers(); + if (ctorResolvers != null) { + for (ConstructorResolver ctorResolver : ctorResolvers) { try { - ConstructorExecutor cEx = ctorResolver.resolve(state.getEvaluationContext(), typename, - argumentTypes); - if (cEx != null) { - return cEx; + ConstructorExecutor ce = ctorResolver.resolve(state.getEvaluationContext(), typeName, argumentTypes); + if (ce != null) { + return ce; } } catch (AccessException ex) { throw new SpelEvaluationException(getStartPosition(), ex, - SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename, + SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typeName, FormatHelper.formatMethodForMessage("", argumentTypes)); } } } - throw new SpelEvaluationException(getStartPosition(), SpelMessage.CONSTRUCTOR_NOT_FOUND, typename, FormatHelper - .formatMethodForMessage("", argumentTypes)); + throw new SpelEvaluationException(getStartPosition(), SpelMessage.CONSTRUCTOR_NOT_FOUND, typeName, + FormatHelper.formatMethodForMessage("", argumentTypes)); } @Override public String toStringAST() { - StringBuilder sb = new StringBuilder(); - sb.append("new "); - + StringBuilder sb = new StringBuilder("new "); int index = 0; sb.append(getChild(index++).toStringAST()); sb.append("("); @@ -328,17 +325,20 @@ public class ConstructorReference extends SpelNodeImpl { private void populateReferenceTypeArray(ExpressionState state, Object newArray, TypeConverter typeConverter, InlineList initializer, Class componentType) { + TypeDescriptor toTypeDescriptor = TypeDescriptor.valueOf(componentType); Object[] newObjectArray = (Object[]) newArray; for (int i = 0; i < newObjectArray.length; i++) { SpelNode elementNode = initializer.getChild(i); Object arrayEntry = elementNode.getValue(state); - newObjectArray[i] = typeConverter.convertValue(arrayEntry, TypeDescriptor.forObject(arrayEntry), toTypeDescriptor); + newObjectArray[i] = typeConverter.convertValue(arrayEntry, + TypeDescriptor.forObject(arrayEntry), toTypeDescriptor); } } private void populateByteArray(ExpressionState state, Object newArray, TypeConverter typeConverter, InlineList initializer) { + byte[] newByteArray = (byte[]) newArray; for (int i = 0; i < newByteArray.length; i++) { TypedValue typedValue = initializer.getChild(i).getTypedValue(state); @@ -348,6 +348,7 @@ public class ConstructorReference extends SpelNodeImpl { private void populateFloatArray(ExpressionState state, Object newArray, TypeConverter typeConverter, InlineList initializer) { + float[] newFloatArray = (float[]) newArray; for (int i = 0; i < newFloatArray.length; i++) { TypedValue typedValue = initializer.getChild(i).getTypedValue(state); @@ -357,6 +358,7 @@ public class ConstructorReference extends SpelNodeImpl { private void populateDoubleArray(ExpressionState state, Object newArray, TypeConverter typeConverter, InlineList initializer) { + double[] newDoubleArray = (double[]) newArray; for (int i = 0; i < newDoubleArray.length; i++) { TypedValue typedValue = initializer.getChild(i).getTypedValue(state); @@ -364,8 +366,9 @@ public class ConstructorReference extends SpelNodeImpl { } } - private void populateShortArray(ExpressionState state, Object newArray, - TypeConverter typeConverter, InlineList initializer) { + private void populateShortArray(ExpressionState state, Object newArray, TypeConverter typeConverter, + InlineList initializer) { + short[] newShortArray = (short[]) newArray; for (int i = 0; i < newShortArray.length; i++) { TypedValue typedValue = initializer.getChild(i).getTypedValue(state); @@ -375,6 +378,7 @@ public class ConstructorReference extends SpelNodeImpl { private void populateLongArray(ExpressionState state, Object newArray, TypeConverter typeConverter, InlineList initializer) { + long[] newLongArray = (long[]) newArray; for (int i = 0; i < newLongArray.length; i++) { TypedValue typedValue = initializer.getChild(i).getTypedValue(state); @@ -384,6 +388,7 @@ public class ConstructorReference extends SpelNodeImpl { private void populateCharArray(ExpressionState state, Object newArray, TypeConverter typeConverter, InlineList initializer) { + char[] newCharArray = (char[]) newArray; for (int i = 0; i < newCharArray.length; i++) { TypedValue typedValue = initializer.getChild(i).getTypedValue(state); @@ -393,6 +398,7 @@ public class ConstructorReference extends SpelNodeImpl { private void populateBooleanArray(ExpressionState state, Object newArray, TypeConverter typeConverter, InlineList initializer) { + boolean[] newBooleanArray = (boolean[]) newArray; for (int i = 0; i < newBooleanArray.length; i++) { TypedValue typedValue = initializer.getChild(i).getTypedValue(state); @@ -402,6 +408,7 @@ public class ConstructorReference extends SpelNodeImpl { private void populateIntArray(ExpressionState state, Object newArray, TypeConverter typeConverter, InlineList initializer) { + int[] newIntArray = (int[]) newArray; for (int i = 0; i < newIntArray.length; i++) { TypedValue typedValue = initializer.getChild(i).getTypedValue(state); @@ -410,7 +417,7 @@ public class ConstructorReference extends SpelNodeImpl { } private boolean hasInitializer() { - return getChildCount() > 1; + return (getChildCount() > 1); } @Override @@ -419,46 +426,43 @@ public class ConstructorReference extends SpelNodeImpl { this.exitTypeDescriptor == null) { return false; } + if (getChildCount() > 1) { for (int c = 1, max = getChildCount();c < max; c++) { - if (!children[c].isCompilable()) { + if (!this.children[c].isCompilable()) { return false; } } } - ReflectiveConstructorExecutor executor = (ReflectiveConstructorExecutor)this.cachedExecutor; + + ReflectiveConstructorExecutor executor = (ReflectiveConstructorExecutor) this.cachedExecutor; Constructor constructor = executor.getConstructor(); - if (!Modifier.isPublic(constructor.getModifiers()) || - !Modifier.isPublic(constructor.getDeclaringClass().getModifiers())) { - return false; - } - if (constructor.isVarArgs()) { - return false; - } - return true; + return (!constructor.isVarArgs() && Modifier.isPublic(constructor.getModifiers()) && + Modifier.isPublic(constructor.getDeclaringClass().getModifiers())); } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { ReflectiveConstructorExecutor executor = ((ReflectiveConstructorExecutor) this.cachedExecutor); Constructor constructor = executor.getConstructor(); - String classSlashedDescriptor = constructor.getDeclaringClass().getName().replace('.','/'); + String classSlashedDescriptor = constructor.getDeclaringClass().getName().replace('.', '/'); String[] paramDescriptors = CodeFlow.toParamDescriptors(constructor); - mv.visitTypeInsn(NEW,classSlashedDescriptor); + mv.visitTypeInsn(NEW, classSlashedDescriptor); mv.visitInsn(DUP); - for (int c = 1; c < children.length; c++) { // children[0] is the type of the constructor - SpelNodeImpl child = children[c]; - codeflow.enterCompilationScope(); - child.generateCode(mv, codeflow); + for (int c = 1; c < this.children.length; c++) { // children[0] is the type of the constructor + SpelNodeImpl child = this.children[c]; + cf.enterCompilationScope(); + child.generateCode(mv, cf); // Check if need to box it for the method reference? - if (CodeFlow.isPrimitive(codeflow.lastDescriptor()) && (paramDescriptors[c-1].charAt(0)=='L')) { - CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0)); + if (CodeFlow.isPrimitive(cf.lastDescriptor()) && paramDescriptors[c-1].charAt(0) == 'L') { + CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0)); } - codeflow.exitCompilationScope(); + cf.exitCompilationScope(); } - mv.visitMethodInsn(INVOKESPECIAL,classSlashedDescriptor,"",CodeFlow.createSignatureDescriptor(constructor),false); - codeflow.pushDescriptor(exitTypeDescriptor); + mv.visitMethodInsn(INVOKESPECIAL, classSlashedDescriptor, "", + CodeFlow.createSignatureDescriptor(constructor), false); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Elvis.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Elvis.java index 658ef860c38..77f342ec172 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Elvis.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Elvis.java @@ -69,14 +69,14 @@ public class Elvis extends SpelNodeImpl { SpelNodeImpl condition = this.children[0]; SpelNodeImpl ifNullValue = this.children[1]; return (condition.isCompilable() && ifNullValue.isCompilable() && - condition.getExitDescriptor() != null && ifNullValue.getExitDescriptor() != null); + condition.exitTypeDescriptor != null && ifNullValue.exitTypeDescriptor != null); } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { // exit type descriptor can be null if both components are literal expressions computeExitTypeDescriptor(); - this.children[0].generateCode(mv, codeflow); + this.children[0].generateCode(mv, cf); Label elseTarget = new Label(); Label endOfIf = new Label(); mv.visitInsn(DUP); @@ -84,17 +84,17 @@ public class Elvis extends SpelNodeImpl { mv.visitJumpInsn(GOTO, endOfIf); mv.visitLabel(elseTarget); mv.visitInsn(POP); - this.children[1].generateCode(mv, codeflow); - if (!CodeFlow.isPrimitive(getExitDescriptor())) { - CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0)); + this.children[1].generateCode(mv, cf); + if (!CodeFlow.isPrimitive(this.exitTypeDescriptor)) { + CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0)); } mv.visitLabel(endOfIf); - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } private void computeExitTypeDescriptor() { - if (this.exitTypeDescriptor == null && this.children[0].getExitDescriptor() != null && - this.children[1].getExitDescriptor() != null) { + if (this.exitTypeDescriptor == null && this.children[0].exitTypeDescriptor != null && + this.children[1].exitTypeDescriptor != null) { String conditionDescriptor = this.children[0].exitTypeDescriptor; String ifNullValueDescriptor = this.children[1].exitTypeDescriptor; if (conditionDescriptor.equals(ifNullValueDescriptor)) { diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java index 0f0dc5d8c80..4e70ae2ac2c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java @@ -49,8 +49,8 @@ public class FloatLiteral extends Literal { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { mv.visitLdcInsn(this.value.getValue()); - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java index 3494a1de90f..441e7e14749 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,8 +34,7 @@ public class FormatHelper { * @return nicely formatted string, eg. foo(String,int) */ public static String formatMethodForMessage(String name, List argumentTypes) { - StringBuilder sb = new StringBuilder(); - sb.append(name); + StringBuilder sb = new StringBuilder(name); sb.append("("); for (int i = 0; i < argumentTypes.size(); i++) { if (i > 0) { @@ -63,23 +62,23 @@ public class FormatHelper { if (clazz == null) { return "null"; } - StringBuilder fmtd = new StringBuilder(); if (clazz.isArray()) { + StringBuilder sb = new StringBuilder(); int dims = 1; Class baseClass = clazz.getComponentType(); while (baseClass.isArray()) { baseClass = baseClass.getComponentType(); dims++; } - fmtd.append(baseClass.getName()); + sb.append(baseClass.getName()); for (int i = 0; i < dims; i++) { - fmtd.append("[]"); + sb.append("[]"); } + return sb.toString(); } else { - fmtd.append(clazz.getName()); + return clazz.getName(); } - return fmtd.toString(); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java index 55d412728cd..4b3046d5140 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java @@ -103,6 +103,7 @@ public class FunctionReference extends SpelNodeImpl { SpelMessage.FUNCTION_MUST_BE_STATIC, method.getDeclaringClass().getName() + "." + method.getName(), this.name); } + boolean argumentConversionOccurred = false; // Convert arguments if necessary and remap them for varargs if required if (functionArgs != null) { @@ -120,7 +121,7 @@ public class FunctionReference extends SpelNodeImpl { this.method = method; this.exitTypeDescriptor = CodeFlow.toDescriptor(method.getReturnType()); } - return new TypedValue(result, new TypeDescriptor(new MethodParameter(method,-1)).narrow(result)); + return new TypedValue(result, new TypeDescriptor(new MethodParameter(method, -1)).narrow(result)); } catch (Exception ex) { throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL, @@ -142,8 +143,6 @@ public class FunctionReference extends SpelNodeImpl { return sb.toString(); } - // to 'assign' to a function don't use the () suffix and so it is just a variable reference - /** * Compute the arguments to the function, they are the children of this expression node. * @return an array of argument values for the function call @@ -164,26 +163,26 @@ public class FunctionReference extends SpelNodeImpl { } @Override - public void generateCode(MethodVisitor mv,CodeFlow codeflow) { - String methodDeclaringClassSlashedDescriptor = this.method.getDeclaringClass().getName().replace('.','/'); + public void generateCode(MethodVisitor mv,CodeFlow cf) { + String methodDeclaringClassSlashedDescriptor = this.method.getDeclaringClass().getName().replace('.', '/'); String[] paramDescriptors = CodeFlow.toParamDescriptors(this.method); for (int c = 0; c < this.children.length; c++) { SpelNodeImpl child = this.children[c]; - codeflow.enterCompilationScope(); - child.generateCode(mv, codeflow); + cf.enterCompilationScope(); + child.generateCode(mv, cf); // Check if need to box it for the method reference? - if (CodeFlow.isPrimitive(codeflow.lastDescriptor()) && paramDescriptors[c].charAt(0) == 'L') { - CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0)); + if (CodeFlow.isPrimitive(cf.lastDescriptor()) && paramDescriptors[c].charAt(0) == 'L') { + CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0)); } - else if (!codeflow.lastDescriptor().equals(paramDescriptors[c])) { + else if (!cf.lastDescriptor().equals(paramDescriptors[c])) { // This would be unnecessary in the case of subtyping (e.g. method takes a Number but passed in is an Integer) CodeFlow.insertCheckCast(mv, paramDescriptors[c]); } - codeflow.exitCompilationScope(); + cf.exitCompilationScope(); } mv.visitMethodInsn(INVOKESTATIC, methodDeclaringClassSlashedDescriptor, this.method.getName(), CodeFlow.createSignatureDescriptor(this.method),false); - codeflow.pushDescriptor(this.exitTypeDescriptor); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java index d258fbb993c..0555be88255 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ public class Identifier extends SpelNodeImpl { private final TypedValue id; - public Identifier(String payload,int pos) { + public Identifier(String payload, int pos) { super(pos); this.id = new TypedValue(payload); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java index cd06b31a283..7293d2c4acd 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java @@ -51,6 +51,9 @@ import org.springframework.expression.spel.support.ReflectivePropertyAccessor; // TODO support correct syntax for multidimensional [][][] and not [,,,] public class Indexer extends SpelNodeImpl { + private static enum IndexedType {ARRAY, LIST, MAP, STRING, OBJECT} + + // These fields are used when the indexer is being used as a property read accessor. // If the name and target type match these cached values then the cachedReadAccessor // is used to read the property. If they do not match, the correct accessor is @@ -73,8 +76,6 @@ public class Indexer extends SpelNodeImpl { private PropertyAccessor cachedWriteAccessor; - private static enum IndexedType { map, array, list, string, object; } - private IndexedType indexedType; @@ -103,7 +104,7 @@ public class Indexer extends SpelNodeImpl { protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { TypedValue context = state.getActiveContextObject(); Object targetObject = context.getValue(); - TypeDescriptor targetObjectTypeDescriptor = context.getTypeDescriptor(); + TypeDescriptor targetDescriptor = context.getTypeDescriptor(); TypedValue indexValue = null; Object index = null; @@ -130,12 +131,11 @@ public class Indexer extends SpelNodeImpl { // Indexing into a Map if (targetObject instanceof Map) { Object key = index; - if (targetObjectTypeDescriptor.getMapKeyTypeDescriptor() != null) { - key = state.convertValue(key, targetObjectTypeDescriptor.getMapKeyTypeDescriptor()); + if (targetDescriptor.getMapKeyTypeDescriptor() != null) { + key = state.convertValue(key, targetDescriptor.getMapKeyTypeDescriptor()); } - this.indexedType = IndexedType.map; - return new MapIndexingValueRef(state.getTypeConverter(), (Map) targetObject, key, - targetObjectTypeDescriptor); + this.indexedType = IndexedType.MAP; + return new MapIndexingValueRef(state.getTypeConverter(), (Map) targetObject, key, targetDescriptor); } if (targetObject == null) { @@ -147,47 +147,47 @@ public class Indexer extends SpelNodeImpl { if (targetObject.getClass().isArray() || targetObject instanceof Collection || targetObject instanceof String) { int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class)); if (targetObject.getClass().isArray()) { - this.indexedType = IndexedType.array; - return new ArrayIndexingValueRef(state.getTypeConverter(), targetObject, idx, targetObjectTypeDescriptor); + this.indexedType = IndexedType.ARRAY; + return new ArrayIndexingValueRef(state.getTypeConverter(), targetObject, idx, targetDescriptor); } else if (targetObject instanceof Collection) { if (targetObject instanceof List) { - this.indexedType = IndexedType.list; + this.indexedType = IndexedType.LIST; } - return new CollectionIndexingValueRef((Collection) targetObject, idx, targetObjectTypeDescriptor, + return new CollectionIndexingValueRef((Collection) targetObject, idx, targetDescriptor, state.getTypeConverter(), state.getConfiguration().isAutoGrowCollections(), state.getConfiguration().getMaximumAutoGrowSize()); } - else if (targetObject instanceof String) { - this.indexedType = IndexedType.string; - return new StringIndexingLValue((String) targetObject, idx, targetObjectTypeDescriptor); + else { + this.indexedType = IndexedType.STRING; + return new StringIndexingLValue((String) targetObject, idx, targetDescriptor); } } // Try and treat the index value as a property of the context object // TODO could call the conversion service to convert the value to a String - if (indexValue.getTypeDescriptor().getType() == String.class) { - this.indexedType = IndexedType.object; + if (String.class.equals(indexValue.getTypeDescriptor().getType())) { + this.indexedType = IndexedType.OBJECT; return new PropertyIndexingValueRef(targetObject, (String) indexValue.getValue(), - state.getEvaluationContext(), targetObjectTypeDescriptor); + state.getEvaluationContext(), targetDescriptor); } throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, - targetObjectTypeDescriptor.toString()); + targetDescriptor.toString()); } @Override public boolean isCompilable() { - if (this.indexedType == IndexedType.array) { + if (this.indexedType == IndexedType.ARRAY) { return (this.exitTypeDescriptor != null); } - else if (this.indexedType == IndexedType.list) { + else if (this.indexedType == IndexedType.LIST) { return this.children[0].isCompilable(); - } - else if (this.indexedType == IndexedType.map) { + } + else if (this.indexedType == IndexedType.MAP) { return (this.children[0] instanceof PropertyOrFieldReference || this.children[0].isCompilable()); } - else if (this.indexedType == IndexedType.object) { + else if (this.indexedType == IndexedType.OBJECT) { // If the string name is changing the accessor is clearly going to change (so compilation is not possible) if (this.cachedReadAccessor != null && (this.cachedReadAccessor instanceof ReflectivePropertyAccessor.OptimalPropertyAccessor) && @@ -199,89 +199,64 @@ public class Indexer extends SpelNodeImpl { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - String descriptor = codeflow.lastDescriptor(); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + String descriptor = cf.lastDescriptor(); if (descriptor == null) { // Stack is empty, should use context object - codeflow.loadTarget(mv); + cf.loadTarget(mv); } - - if (this.indexedType == IndexedType.array) { - if ("I".equals(this.exitTypeDescriptor)) { - mv.visitTypeInsn(CHECKCAST,"[I"); - SpelNodeImpl index = this.children[0]; - codeflow.enterCompilationScope(); - index.generateCode(mv, codeflow); - codeflow.exitCompilationScope(); - mv.visitInsn(IALOAD); - } - else if ("D".equals(this.exitTypeDescriptor)) { + + if (this.indexedType == IndexedType.ARRAY) { + int insn; + if ("D".equals(this.exitTypeDescriptor)) { mv.visitTypeInsn(CHECKCAST, "[D"); - SpelNodeImpl index = this.children[0]; - codeflow.enterCompilationScope(); - index.generateCode(mv, codeflow); - mv.visitInsn(DALOAD); - } - else if ("J".equals(this.exitTypeDescriptor)) { - mv.visitTypeInsn(CHECKCAST, "[J"); - SpelNodeImpl index = this.children[0]; - codeflow.enterCompilationScope(); - index.generateCode(mv, codeflow); - codeflow.exitCompilationScope(); - mv.visitInsn(LALOAD); + insn = DALOAD; } else if ("F".equals(this.exitTypeDescriptor)) { mv.visitTypeInsn(CHECKCAST, "[F"); - SpelNodeImpl index = this.children[0]; - codeflow.enterCompilationScope(); - index.generateCode(mv, codeflow); - codeflow.exitCompilationScope(); - mv.visitInsn(FALOAD); + insn = FALOAD; + } + else if ("J".equals(this.exitTypeDescriptor)) { + mv.visitTypeInsn(CHECKCAST, "[J"); + insn = LALOAD; + } + else if ("I".equals(this.exitTypeDescriptor)) { + mv.visitTypeInsn(CHECKCAST, "[I"); + insn = IALOAD; } else if ("S".equals(this.exitTypeDescriptor)) { mv.visitTypeInsn(CHECKCAST, "[S"); - SpelNodeImpl index = this.children[0]; - codeflow.enterCompilationScope(); - index.generateCode(mv, codeflow); - codeflow.exitCompilationScope(); - mv.visitInsn(SALOAD); + insn = SALOAD; } else if ("B".equals(this.exitTypeDescriptor)) { mv.visitTypeInsn(CHECKCAST, "[B"); - SpelNodeImpl index = this.children[0]; - codeflow.enterCompilationScope(); - index.generateCode(mv, codeflow); - codeflow.exitCompilationScope(); - mv.visitInsn(BALOAD); + insn = BALOAD; } else if ("C".equals(this.exitTypeDescriptor)) { mv.visitTypeInsn(CHECKCAST, "[C"); - SpelNodeImpl index = this.children[0]; - codeflow.enterCompilationScope(); - index.generateCode(mv, codeflow); - codeflow.exitCompilationScope(); - mv.visitInsn(CALOAD); + insn = CALOAD; } else { mv.visitTypeInsn(CHECKCAST, "["+ this.exitTypeDescriptor + (CodeFlow.isPrimitiveArray(this.exitTypeDescriptor) ? "" : ";")); //depthPlusOne(exitTypeDescriptor)+"Ljava/lang/Object;"); - SpelNodeImpl index = this.children[0]; - codeflow.enterCompilationScope(); - index.generateCode(mv, codeflow); - codeflow.exitCompilationScope(); - mv.visitInsn(AALOAD); + insn = AALOAD; } + SpelNodeImpl index = this.children[0]; + cf.enterCompilationScope(); + index.generateCode(mv, cf); + cf.exitCompilationScope(); + mv.visitInsn(insn); } - else if (this.indexedType == IndexedType.list) { + else if (this.indexedType == IndexedType.LIST) { mv.visitTypeInsn(CHECKCAST, "java/util/List"); - codeflow.enterCompilationScope(); - this.children[0].generateCode(mv, codeflow); - codeflow.exitCompilationScope(); + cf.enterCompilationScope(); + this.children[0].generateCode(mv, cf); + cf.exitCompilationScope(); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true); CodeFlow.insertCheckCast(mv, this.exitTypeDescriptor); } - else if (this.indexedType == IndexedType.map) { + else if (this.indexedType == IndexedType.MAP) { mv.visitTypeInsn(CHECKCAST, "java/util/Map"); // Special case when the key is an unquoted string literal that will be parsed as // a property/field reference @@ -291,22 +266,22 @@ public class Indexer extends SpelNodeImpl { mv.visitLdcInsn(mapKeyName); } else { - codeflow.enterCompilationScope(); - this.children[0].generateCode(mv, codeflow); - codeflow.exitCompilationScope(); + cf.enterCompilationScope(); + this.children[0].generateCode(mv, cf); + cf.exitCompilationScope(); } mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); CodeFlow.insertCheckCast(mv, this.exitTypeDescriptor); } - else if (this.indexedType == IndexedType.object) { + else if (this.indexedType == IndexedType.OBJECT) { ReflectivePropertyAccessor.OptimalPropertyAccessor accessor = (ReflectivePropertyAccessor.OptimalPropertyAccessor) this.cachedReadAccessor; Member member = accessor.member; boolean isStatic = Modifier.isStatic(member.getModifiers()); - String memberDeclaringClassSlashedDescriptor = member.getDeclaringClass().getName().replace('.','/'); + String memberDeclaringClassSlashedDescriptor = member.getDeclaringClass().getName().replace('.', '/'); if (!isStatic) { if (descriptor == null) { - codeflow.loadTarget(mv); + cf.loadTarget(mv); } if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) { mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor); @@ -314,7 +289,7 @@ public class Indexer extends SpelNodeImpl { } if (member instanceof Field) { mv.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, memberDeclaringClassSlashedDescriptor, - member.getName(), CodeFlow.toJVMDescriptor(((Field) member).getType())); + member.getName(), CodeFlow.toJvmDescriptor(((Field) member).getType())); } else { mv.visitMethodInsn(isStatic? INVOKESTATIC : INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, @@ -322,13 +297,12 @@ public class Indexer extends SpelNodeImpl { } } - codeflow.pushDescriptor(this.exitTypeDescriptor); + cf.pushDescriptor(this.exitTypeDescriptor); } @Override public String toStringAST() { - StringBuilder sb = new StringBuilder(); - sb.append("["); + StringBuilder sb = new StringBuilder("["); for (int i = 0; i < getChildCount(); i++) { if (i > 0) { sb.append(","); @@ -339,98 +313,60 @@ public class Indexer extends SpelNodeImpl { return sb.toString(); } + private void setArrayElement(TypeConverter converter, Object ctx, int idx, Object newValue, Class arrayComponentType) throws EvaluationException { - if (arrayComponentType == Integer.TYPE) { - int[] array = (int[]) ctx; - checkAccess(array.length, idx); - array[idx] = (Integer) converter.convertValue(newValue, TypeDescriptor.forObject(newValue), - TypeDescriptor.valueOf(Integer.class)); - } - else if (arrayComponentType == Boolean.TYPE) { - boolean[] array = (boolean[]) ctx; - checkAccess(array.length, idx); - array[idx] = (Boolean) converter.convertValue(newValue, TypeDescriptor.forObject(newValue), - TypeDescriptor.valueOf(Boolean.class)); - } - else if (arrayComponentType == Character.TYPE) { - char[] array = (char[]) ctx; - checkAccess(array.length, idx); - array[idx] = (Character) converter.convertValue(newValue, TypeDescriptor.forObject(newValue), - TypeDescriptor.valueOf(Character.class)); - } - else if (arrayComponentType == Long.TYPE) { - long[] array = (long[]) ctx; - checkAccess(array.length, idx); - array[idx] = (Long) converter.convertValue(newValue, TypeDescriptor.forObject(newValue), - TypeDescriptor.valueOf(Long.class)); - } - else if (arrayComponentType == Short.TYPE) { - short[] array = (short[]) ctx; - checkAccess(array.length, idx); - array[idx] = (Short) converter.convertValue(newValue, TypeDescriptor.forObject(newValue), - TypeDescriptor.valueOf(Short.class)); - } - else if (arrayComponentType == Double.TYPE) { + if (arrayComponentType == Double.TYPE) { double[] array = (double[]) ctx; checkAccess(array.length, idx); - array[idx] = (Double) converter.convertValue(newValue, TypeDescriptor.forObject(newValue), - TypeDescriptor.valueOf(Double.class)); + array[idx] = convertValue(converter, newValue, Double.class); } else if (arrayComponentType == Float.TYPE) { float[] array = (float[]) ctx; checkAccess(array.length, idx); - array[idx] = (Float) converter.convertValue(newValue, TypeDescriptor.forObject(newValue), - TypeDescriptor.valueOf(Float.class)); + array[idx] = convertValue(converter, newValue, Float.class); + } + else if (arrayComponentType == Long.TYPE) { + long[] array = (long[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, Long.class); + } + else if (arrayComponentType == Integer.TYPE) { + int[] array = (int[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, Integer.class); + } + else if (arrayComponentType == Short.TYPE) { + short[] array = (short[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, Short.class); } else if (arrayComponentType == Byte.TYPE) { byte[] array = (byte[]) ctx; checkAccess(array.length, idx); - array[idx] = (Byte) converter.convertValue(newValue, TypeDescriptor.forObject(newValue), - TypeDescriptor.valueOf(Byte.class)); + array[idx] = convertValue(converter, newValue, Byte.class); + } + else if (arrayComponentType == Character.TYPE) { + char[] array = (char[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, Character.class); + } + else if (arrayComponentType == Boolean.TYPE) { + boolean[] array = (boolean[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, Boolean.class); } else { Object[] array = (Object[]) ctx; checkAccess(array.length, idx); - array[idx] = converter.convertValue(newValue, TypeDescriptor.forObject(newValue), - TypeDescriptor.valueOf(arrayComponentType)); + array[idx] = convertValue(converter, newValue, arrayComponentType); } } private Object accessArrayElement(Object ctx, int idx) throws SpelEvaluationException { Class arrayComponentType = ctx.getClass().getComponentType(); - if (arrayComponentType == Integer.TYPE) { - int[] array = (int[]) ctx; - checkAccess(array.length, idx); - this.exitTypeDescriptor = "I"; - return array[idx]; - } - else if (arrayComponentType == Boolean.TYPE) { - boolean[] array = (boolean[]) ctx; - checkAccess(array.length, idx); - this.exitTypeDescriptor = "Z"; - return array[idx]; - } - else if (arrayComponentType == Character.TYPE) { - char[] array = (char[]) ctx; - checkAccess(array.length, idx); - this.exitTypeDescriptor = "C"; - return array[idx]; - } - else if (arrayComponentType == Long.TYPE) { - long[] array = (long[]) ctx; - checkAccess(array.length, idx); - this.exitTypeDescriptor = "J"; - return array[idx]; - } - else if (arrayComponentType == Short.TYPE) { - short[] array = (short[]) ctx; - checkAccess(array.length, idx); - this.exitTypeDescriptor = "S"; - return array[idx]; - } - else if (arrayComponentType == Double.TYPE) { + if (arrayComponentType == Double.TYPE) { double[] array = (double[]) ctx; checkAccess(array.length, idx); this.exitTypeDescriptor = "D"; @@ -442,12 +378,42 @@ public class Indexer extends SpelNodeImpl { this.exitTypeDescriptor = "F"; return array[idx]; } + else if (arrayComponentType == Long.TYPE) { + long[] array = (long[]) ctx; + checkAccess(array.length, idx); + this.exitTypeDescriptor = "J"; + return array[idx]; + } + else if (arrayComponentType == Integer.TYPE) { + int[] array = (int[]) ctx; + checkAccess(array.length, idx); + this.exitTypeDescriptor = "I"; + return array[idx]; + } + else if (arrayComponentType == Short.TYPE) { + short[] array = (short[]) ctx; + checkAccess(array.length, idx); + this.exitTypeDescriptor = "S"; + return array[idx]; + } else if (arrayComponentType == Byte.TYPE) { byte[] array = (byte[]) ctx; checkAccess(array.length, idx); this.exitTypeDescriptor = "B"; return array[idx]; } + else if (arrayComponentType == Character.TYPE) { + char[] array = (char[]) ctx; + checkAccess(array.length, idx); + this.exitTypeDescriptor = "C"; + return array[idx]; + } + else if (arrayComponentType == Boolean.TYPE) { + boolean[] array = (boolean[]) ctx; + checkAccess(array.length, idx); + this.exitTypeDescriptor = "Z"; + return array[idx]; + } else { Object[] array = (Object[]) ctx; checkAccess(array.length, idx); @@ -464,6 +430,11 @@ public class Indexer extends SpelNodeImpl { } } + @SuppressWarnings("unchecked") + private T convertValue(TypeConverter converter, Object value, Class targetType) { + return (T) converter.convertValue(value, TypeDescriptor.forObject(value), TypeDescriptor.valueOf(targetType)); + } + private class ArrayIndexingValueRef implements ValueRef { @@ -475,7 +446,6 @@ public class Indexer extends SpelNodeImpl { private final TypeDescriptor typeDescriptor; - ArrayIndexingValueRef(TypeConverter typeConverter, Object array, int index, TypeDescriptor typeDescriptor) { this.typeConverter = typeConverter; this.array = array; @@ -483,7 +453,6 @@ public class Indexer extends SpelNodeImpl { this.typeDescriptor = typeDescriptor; } - @Override public TypedValue getValue() { Object arrayElement = accessArrayElement(this.array, this.index); @@ -512,28 +481,27 @@ public class Indexer extends SpelNodeImpl { private final Object key; - private final TypeDescriptor mapEntryTypeDescriptor; + private final TypeDescriptor mapEntryDescriptor; - public MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, TypeDescriptor mapEntryTypeDescriptor) { + public MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, TypeDescriptor mapEntryDescriptor) { this.typeConverter = typeConverter; this.map = map; this.key = key; - this.mapEntryTypeDescriptor = mapEntryTypeDescriptor; + this.mapEntryDescriptor = mapEntryDescriptor; } @Override public TypedValue getValue() { Object value = this.map.get(this.key); exitTypeDescriptor = CodeFlow.toDescriptorFromObject(value); - return new TypedValue(value, - this.mapEntryTypeDescriptor.getMapValueTypeDescriptor(value)); + return new TypedValue(value, this.mapEntryDescriptor.getMapValueTypeDescriptor(value)); } @Override public void setValue(Object newValue) { - if (this.mapEntryTypeDescriptor.getMapValueTypeDescriptor() != null) { + if (this.mapEntryDescriptor.getMapValueTypeDescriptor() != null) { newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue), - this.mapEntryTypeDescriptor.getMapValueTypeDescriptor()); + this.mapEntryDescriptor.getMapValueTypeDescriptor()); } this.map.put(this.key, newValue); } @@ -563,7 +531,6 @@ public class Indexer extends SpelNodeImpl { this.targetObjectTypeDescriptor = targetObjectTypeDescriptor; } - @Override public TypedValue getValue() { Class targetObjectRuntimeClass = getObjectClass(this.targetObject); @@ -587,7 +554,8 @@ public class Indexer extends SpelNodeImpl { Indexer.this.cachedReadName = this.name; Indexer.this.cachedReadTargetType = targetObjectRuntimeClass; if (accessor instanceof ReflectivePropertyAccessor.OptimalPropertyAccessor) { - ReflectivePropertyAccessor.OptimalPropertyAccessor optimalAccessor = (ReflectivePropertyAccessor.OptimalPropertyAccessor)accessor; + ReflectivePropertyAccessor.OptimalPropertyAccessor optimalAccessor = + (ReflectivePropertyAccessor.OptimalPropertyAccessor) accessor; Member member = optimalAccessor.member; if (member instanceof Field) { Indexer.this.exitTypeDescriptor = CodeFlow.toDescriptor(((Field)member).getType()); @@ -672,7 +640,6 @@ public class Indexer extends SpelNodeImpl { this.maximumSize = maximumSize; } - @Override public TypedValue getValue() { growCollectionIfNecessary(); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java index 14afd4d16a7..bfda6129a7d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java @@ -33,8 +33,8 @@ import org.springframework.expression.spel.SpelNode; */ public class InlineList extends SpelNodeImpl { - // if the list is purely literals, it is a constant value and can be computed and cached - TypedValue constant = null; // TODO must be immutable list + // If the list is purely literals, it is a constant value and can be computed and cached + private TypedValue constant = null; // TODO must be immutable list public InlineList(int pos, SpelNodeImpl... args) { @@ -87,8 +87,8 @@ public class InlineList extends SpelNodeImpl { } else { List returnValue = new ArrayList(); - int childcount = getChildCount(); - for (int c = 0; c < childcount; c++) { + int childCount = getChildCount(); + for (int c = 0; c < childCount; c++) { returnValue.add(getChild(c).getValue(expressionState)); } return new TypedValue(returnValue); @@ -97,25 +97,24 @@ public class InlineList extends SpelNodeImpl { @Override public String toStringAST() { - StringBuilder s = new StringBuilder(); - // string ast matches input string, not the 'toString()' of the resultant collection, which would use [] - s.append('{'); + StringBuilder sb = new StringBuilder("{"); + // String ast matches input string, not the 'toString()' of the resultant collection, which would use [] int count = getChildCount(); for (int c = 0; c < count; c++) { if (c > 0) { - s.append(','); + sb.append(","); } - s.append(getChild(c).toStringAST()); + sb.append(getChild(c).toStringAST()); } - s.append('}'); - return s.toString(); + sb.append("}"); + return sb.toString(); } /** - * @return whether this list is a constant value + * Return whether this list is a constant value. */ public boolean isConstant() { - return this.constant != null; + return (this.constant != null); } @SuppressWarnings("unchecked") diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineMap.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineMap.java index 7b22ec822e7..aa1233b5f66 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineMap.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,8 +33,9 @@ import org.springframework.expression.spel.SpelNode; */ public class InlineMap extends SpelNodeImpl { - // if the map is purely literals, it is a constant value and can be computed and cached - TypedValue constant = null; + // If the map is purely literals, it is a constant value and can be computed and cached + private TypedValue constant = null; + public InlineMap(int pos, SpelNodeImpl... args) { super(pos, args); @@ -43,9 +44,9 @@ public class InlineMap extends SpelNodeImpl { /** - * If all the components of the list are constants, or lists/maps that themselves contain constants, then a constant list - * can be built to represent this node. This will speed up later getValue calls and reduce the amount of garbage - * created. + * If all the components of the list are constants, or lists/maps that themselves + * contain constants, then a constant list can be built to represent this node. + * This will speed up later getValue calls and reduce the amount of garbage created. */ private void checkIfConstant() { boolean isConstant = true; @@ -74,13 +75,13 @@ public class InlineMap extends SpelNodeImpl { } if (isConstant) { Map constantMap = new LinkedHashMap(); - int childcount = getChildCount(); - for (int c = 0; c < childcount; c++) { + int childCount = getChildCount(); + for (int c = 0; c < childCount; c++) { SpelNode keyChild = getChild(c++); SpelNode valueChild = getChild(c); Object key = null; Object value = null; - if ((keyChild instanceof Literal)) { + if (keyChild instanceof Literal) { key = ((Literal) keyChild).getLiteralValue().getValue(); } else if (keyChild instanceof PropertyOrFieldReference) { @@ -132,19 +133,18 @@ public class InlineMap extends SpelNodeImpl { @Override public String toStringAST() { - StringBuilder s = new StringBuilder(); - s.append('{'); + StringBuilder sb = new StringBuilder("{"); int count = getChildCount(); for (int c = 0; c < count; c++) { if (c > 0) { - s.append(','); + sb.append(","); } - s.append(getChild(c++).toStringAST()); - s.append(':'); - s.append(getChild(c).toStringAST()); + sb.append(getChild(c++).toStringAST()); + sb.append(":"); + sb.append(getChild(c).toStringAST()); } - s.append('}'); - return s.toString(); + sb.append("}"); + return sb.toString(); } /** diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java index 1f4436ecacc..bc65136ccf6 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java @@ -47,19 +47,19 @@ public class IntLiteral extends Literal { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - int intValue = ((Integer)this.value.getValue()).intValue(); - if (intValue==-1) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { + int intValue = (Integer) this.value.getValue(); + if (intValue == -1) { // Not sure we can get here because -1 is OpMinus mv.visitInsn(ICONST_M1); } else if (intValue >= 0 && intValue < 6) { - mv.visitInsn(ICONST_0+intValue); + mv.visitInsn(ICONST_0 + intValue); } else { mv.visitLdcInsn(intValue); } - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Literal.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Literal.java index 9b35a78a403..5fe5da6ea68 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Literal.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Literal.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,8 +64,9 @@ public abstract class Literal extends SpelNodeImpl { /** - * Process the string form of a number, using the specified base if supplied and return an appropriate literal to - * hold it. Any suffix to indicate a long will be taken into account (either 'l' or 'L' is supported). + * Process the string form of a number, using the specified base if supplied + * and return an appropriate literal to hold it. Any suffix to indicate a + * long will be taken into account (either 'l' or 'L' is supported). * @param numberToken the token holding the number as its payload (eg. 1234 or 0xCAFE) * @param radix the base of number * @return a subtype of Literal that can represent it @@ -75,8 +76,8 @@ public abstract class Literal extends SpelNodeImpl { int value = Integer.parseInt(numberToken, radix); return new IntLiteral(numberToken, pos, value); } - catch (NumberFormatException nfe) { - throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_AN_INTEGER, numberToken)); + catch (NumberFormatException ex) { + throw new InternalParseException(new SpelParseException(pos>>16, ex, SpelMessage.NOT_AN_INTEGER, numberToken)); } } @@ -85,8 +86,8 @@ public abstract class Literal extends SpelNodeImpl { long value = Long.parseLong(numberToken, radix); return new LongLiteral(numberToken, pos, value); } - catch (NumberFormatException nfe) { - throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_A_LONG, numberToken)); + catch (NumberFormatException ex) { + throw new InternalParseException(new SpelParseException(pos>>16, ex, SpelMessage.NOT_A_LONG, numberToken)); } } @@ -101,8 +102,8 @@ public abstract class Literal extends SpelNodeImpl { return new RealLiteral(numberToken, pos, value); } } - catch (NumberFormatException nfe) { - throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_A_REAL, numberToken)); + catch (NumberFormatException ex) { + throw new InternalParseException(new SpelParseException(pos>>16, ex, SpelMessage.NOT_A_REAL, numberToken)); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java index d59f6443d17..28e3892fce5 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java @@ -48,9 +48,9 @@ public class LongLiteral extends Literal { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { mv.visitLdcInsn(this.value.getValue()); - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } } 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 3b402966690..c23acbd8b12 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 @@ -71,7 +71,7 @@ public class MethodReference extends SpelNodeImpl { Object[] arguments = getArguments(state); if (state.getActiveContextObject().getValue() == null) { throwIfNotNullSafe(getArgumentTypes(arguments)); - return ValueRef.NullValueRef.instance; + return ValueRef.NullValueRef.INSTANCE; } return new MethodValueRef(state, arguments); } @@ -101,7 +101,7 @@ public class MethodReference extends SpelNodeImpl { try { return executorToUse.execute(evaluationContext, value, arguments); } - catch (AccessException ae) { + catch (AccessException ex) { // 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 @@ -113,7 +113,7 @@ public class MethodReference extends SpelNodeImpl { // 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(value, ae); + throwSimpleExceptionIfPossible(value, ex); // At this point we know it wasn't a user problem so worth a retry if a // better candidate can be found. @@ -216,9 +216,9 @@ public class MethodReference extends SpelNodeImpl { * Decode the AccessException, throwing a lightweight evaluation exception or, if the * cause was a RuntimeException, throw the RuntimeException directly. */ - private void throwSimpleExceptionIfPossible(Object value, AccessException ae) { - if (ae.getCause() instanceof InvocationTargetException) { - Throwable rootCause = ae.getCause().getCause(); + private void throwSimpleExceptionIfPossible(Object value, AccessException ex) { + if (ex.getCause() instanceof InvocationTargetException) { + Throwable rootCause = ex.getCause().getCause(); if (rootCause instanceof RuntimeException) { throw (RuntimeException) rootCause; } @@ -238,8 +238,8 @@ public class MethodReference extends SpelNodeImpl { @Override public String toStringAST() { - StringBuilder sb = new StringBuilder(); - sb.append(this.name).append("("); + StringBuilder sb = new StringBuilder(this.name); + sb.append("("); for (int i = 0; i < getChildCount(); i++) { if (i > 0) { sb.append(","); @@ -283,7 +283,7 @@ public class MethodReference extends SpelNodeImpl { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { CachedMethodExecutor executorToCheck = this.cachedExecutor; if (executorToCheck == null || !(executorToCheck.get() instanceof ReflectiveMethodExecutor)) { throw new IllegalStateException("No applicable cached executor found: " + executorToCheck); @@ -291,10 +291,10 @@ public class MethodReference extends SpelNodeImpl { Method method = ((ReflectiveMethodExecutor) executorToCheck.get()).getMethod(); boolean isStaticMethod = Modifier.isStatic(method.getModifiers()); - String descriptor = codeflow.lastDescriptor(); + String descriptor = cf.lastDescriptor(); if (descriptor == null && !isStaticMethod) { - codeflow.loadTarget(mv); + cf.loadTarget(mv); } boolean itf = method.getDeclaringClass().isInterface(); @@ -307,21 +307,21 @@ public class MethodReference extends SpelNodeImpl { String[] paramDescriptors = CodeFlow.toParamDescriptors(method); for (int i = 0; i < this.children.length;i++) { SpelNodeImpl child = this.children[i]; - codeflow.enterCompilationScope(); - child.generateCode(mv, codeflow); + cf.enterCompilationScope(); + child.generateCode(mv, cf); // Check if need to box it for the method reference? - if (CodeFlow.isPrimitive(codeflow.lastDescriptor()) && paramDescriptors[i].charAt(0) == 'L') { - CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0)); + if (CodeFlow.isPrimitive(cf.lastDescriptor()) && paramDescriptors[i].charAt(0) == 'L') { + CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0)); } - else if (!codeflow.lastDescriptor().equals(paramDescriptors[i])) { + else if (!cf.lastDescriptor().equals(paramDescriptors[i])) { // This would be unnecessary in the case of subtyping (e.g. method takes Number but Integer passed in) CodeFlow.insertCheckCast(mv, paramDescriptors[i]); } - codeflow.exitCompilationScope(); + cf.exitCompilationScope(); } mv.visitMethodInsn(isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL, methodDeclaringClassSlashedDescriptor, method.getName(), CodeFlow.createSignatureDescriptor(method), itf); - codeflow.pushDescriptor(this.exitTypeDescriptor); + cf.pushDescriptor(this.exitTypeDescriptor); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java index b3b9483654f..c766e757ff0 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java @@ -50,9 +50,9 @@ public class NullLiteral extends Literal { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { mv.visitInsn(ACONST_NULL); - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpAnd.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpAnd.java index 204b905aa3a..e46391f891d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpAnd.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpAnd.java @@ -72,34 +72,31 @@ public class OpAnd extends Operator { @Override public boolean isCompilable() { SpelNodeImpl left = getLeftOperand(); - SpelNodeImpl right= getRightOperand(); - if (!left.isCompilable() || !right.isCompilable()) { - return false; - } - return - CodeFlow.isBooleanCompatible(left.getExitDescriptor()) && - CodeFlow.isBooleanCompatible(right.getExitDescriptor()); + SpelNodeImpl right = getRightOperand(); + return (left.isCompilable() && right.isCompilable() && + CodeFlow.isBooleanCompatible(left.exitTypeDescriptor) && + CodeFlow.isBooleanCompatible(right.exitTypeDescriptor)); } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - // pseudo: if (!leftOperandValue) { result=false; } else { result=rightOperandValue; } + public void generateCode(MethodVisitor mv, CodeFlow cf) { + // Pseudo: if (!leftOperandValue) { result=false; } else { result=rightOperandValue; } Label elseTarget = new Label(); Label endOfIf = new Label(); - codeflow.enterCompilationScope(); - getLeftOperand().generateCode(mv, codeflow); - codeflow.unboxBooleanIfNecessary(mv); - codeflow.exitCompilationScope(); + cf.enterCompilationScope(); + getLeftOperand().generateCode(mv, cf); + cf.unboxBooleanIfNecessary(mv); + cf.exitCompilationScope(); mv.visitJumpInsn(IFNE, elseTarget); mv.visitLdcInsn(0); // FALSE mv.visitJumpInsn(GOTO,endOfIf); mv.visitLabel(elseTarget); - codeflow.enterCompilationScope(); - getRightOperand().generateCode(mv, codeflow); - codeflow.unboxBooleanIfNecessary(mv); - codeflow.exitCompilationScope(); + cf.enterCompilationScope(); + getRightOperand().generateCode(mv, cf); + cf.unboxBooleanIfNecessary(mv); + cf.exitCompilationScope(); mv.visitLabel(endOfIf); - codeflow.pushDescriptor(this.exitTypeDescriptor); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java index 895cfa3bc8a..18026780422 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.expression.EvaluationException; import org.springframework.expression.Operation; @@ -31,12 +32,13 @@ import org.springframework.util.Assert; * appropriate exceptions if the operand in question does not support decrement. * * @author Andy Clement + * @author Juergen Hoeller * @author Giovanni Dall'Oglio Risso * @since 3.2 */ public class OpDec extends Operator { - private final boolean postfix; // false means prefix + private final boolean postfix; // false means prefix public OpDec(int pos, boolean postfix, SpelNodeImpl... operands) { @@ -51,41 +53,45 @@ public class OpDec extends Operator { SpelNodeImpl operand = getLeftOperand(); // The operand is going to be read and then assigned to, we don't want to evaluate it twice. - ValueRef lvalue = operand.getValueRef(state); - final TypedValue operandTypedValue = lvalue.getValue();//operand.getValueInternal(state); - final Object operandValue = operandTypedValue.getValue(); + TypedValue operandTypedValue = lvalue.getValue(); //operand.getValueInternal(state); + Object operandValue = operandTypedValue.getValue(); TypedValue returnValue = operandTypedValue; TypedValue newValue = null; if (operandValue instanceof Number) { Number op1 = (Number) operandValue; if (op1 instanceof BigDecimal) { - newValue = new TypedValue(((BigDecimal) op1).subtract(BigDecimal.ONE), - operandTypedValue.getTypeDescriptor()); + newValue = new TypedValue(((BigDecimal) op1).subtract(BigDecimal.ONE), operandTypedValue.getTypeDescriptor()); } else if (op1 instanceof Double) { - newValue = new TypedValue(op1.doubleValue() - 1.0d, - operandTypedValue.getTypeDescriptor()); + newValue = new TypedValue(op1.doubleValue() - 1.0d, operandTypedValue.getTypeDescriptor()); } else if (op1 instanceof Float) { - newValue = new TypedValue(op1.floatValue() - 1.0f, - operandTypedValue.getTypeDescriptor()); + newValue = new TypedValue(op1.floatValue() - 1.0f, operandTypedValue.getTypeDescriptor()); + } + else if (op1 instanceof BigInteger) { + newValue = new TypedValue(((BigInteger) op1).subtract(BigInteger.ONE), operandTypedValue.getTypeDescriptor()); } else if (op1 instanceof Long) { - newValue = new TypedValue(op1.longValue() - 1L, - operandTypedValue.getTypeDescriptor()); + newValue = new TypedValue(op1.longValue() - 1L, operandTypedValue.getTypeDescriptor()); + } + else if (op1 instanceof Integer) { + newValue = new TypedValue(op1.intValue() - 1, operandTypedValue.getTypeDescriptor()); } else if (op1 instanceof Short) { - newValue = new TypedValue(op1.shortValue() - (short) 1, - operandTypedValue.getTypeDescriptor()); + newValue = new TypedValue(op1.shortValue() - (short) 1, operandTypedValue.getTypeDescriptor()); + } + else if (op1 instanceof Byte) { + newValue = new TypedValue(op1.byteValue() - (byte) 1, operandTypedValue.getTypeDescriptor()); } else { - newValue = new TypedValue(op1.intValue() - 1, - operandTypedValue.getTypeDescriptor()); + // Unknown Number subtype -> best guess is double decrement + newValue = new TypedValue(op1.doubleValue() - 1.0d, operandTypedValue.getTypeDescriptor()); } } + if (newValue == null) { try { newValue = state.operate(Operation.SUBTRACT, returnValue.getValue(), 1); @@ -93,7 +99,8 @@ public class OpDec extends Operator { catch (SpelEvaluationException ex) { if (ex.getMessageCode() == SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES) { // This means the operand is not decrementable - throw new SpelEvaluationException(operand.getStartPosition(),SpelMessage.OPERAND_NOT_DECREMENTABLE,operand.toStringAST()); + throw new SpelEvaluationException(operand.getStartPosition(), + SpelMessage.OPERAND_NOT_DECREMENTABLE, operand.toStringAST()); } else { throw ex; @@ -126,7 +133,7 @@ public class OpDec extends Operator { @Override public String toStringAST() { - return new StringBuilder().append(getLeftOperand().toStringAST()).append("--").toString(); + return getLeftOperand().toStringAST() + "--"; } @Override diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java index b1a6162d484..38860600f10 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import java.math.RoundingMode; import org.springframework.asm.MethodVisitor; @@ -37,7 +38,6 @@ import org.springframework.util.NumberUtils; */ public class OpDivide extends Operator { - public OpDivide(int pos, SpelNodeImpl... operands) { super("/", pos, operands); } @@ -58,64 +58,71 @@ public class OpDivide extends Operator { int scale = Math.max(leftBigDecimal.scale(), rightBigDecimal.scale()); return new TypedValue(leftBigDecimal.divide(rightBigDecimal, scale, RoundingMode.HALF_EVEN)); } - - if (leftNumber instanceof Double || rightNumber instanceof Double) { - if (leftNumber instanceof Double && rightNumber instanceof Double) { + else if (leftNumber instanceof Double || rightNumber instanceof Double) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "D"; } return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue()); } else if (leftNumber instanceof Float || rightNumber instanceof Float) { - if (leftNumber instanceof Float && rightNumber instanceof Float) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "F"; } return new TypedValue(leftNumber.floatValue() / rightNumber.floatValue()); } + else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); + return new TypedValue(leftBigInteger.divide(rightBigInteger)); + } else if (leftNumber instanceof Long || rightNumber instanceof Long) { - if (leftNumber instanceof Long && rightNumber instanceof Long) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "J"; } return new TypedValue(leftNumber.longValue() / rightNumber.longValue()); } - else { + else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) { if (leftNumber instanceof Integer && rightNumber instanceof Integer) { this.exitTypeDescriptor = "I"; } - // TODO what about non-int result of the division? return new TypedValue(leftNumber.intValue() / rightNumber.intValue()); } + else { + // Unknown Number subtypes -> best guess is double division + return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue()); + } } return state.operate(Operation.DIVIDE, leftOperand, rightOperand); } - + @Override public boolean isCompilable() { if (!getLeftOperand().isCompilable()) { return false; } - if (this.children.length>1) { + if (this.children.length > 1) { if (!getRightOperand().isCompilable()) { return false; } } - return this.exitTypeDescriptor!=null; + return (this.exitTypeDescriptor != null); } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - getLeftOperand().generateCode(mv, codeflow); - String leftdesc = getLeftOperand().getExitDescriptor(); - if (!CodeFlow.isPrimitive(leftdesc)) { - CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + getLeftOperand().generateCode(mv, cf); + String leftDesc = getLeftOperand().exitTypeDescriptor; + if (!CodeFlow.isPrimitive(leftDesc)) { + CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc); } if (this.children.length > 1) { - codeflow.enterCompilationScope(); - getRightOperand().generateCode(mv, codeflow); - String rightdesc = getRightOperand().getExitDescriptor(); - codeflow.exitCompilationScope(); - if (!CodeFlow.isPrimitive(rightdesc)) { - CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc); + cf.enterCompilationScope(); + getRightOperand().generateCode(mv, cf); + String rightDesc = getRightOperand().exitTypeDescriptor; + cf.exitCompilationScope(); + if (!CodeFlow.isPrimitive(rightDesc)) { + CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc); } switch (this.exitTypeDescriptor.charAt(0)) { case 'I': @@ -131,10 +138,11 @@ public class OpDivide extends Operator { mv.visitInsn(DDIV); break; default: - throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'"); + throw new IllegalStateException( + "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'"); } } - codeflow.pushDescriptor(this.exitTypeDescriptor); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java index 8a3b4207386..022e10c0b39 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java @@ -36,12 +36,13 @@ public class OpEQ extends Operator { this.exitTypeDescriptor = "Z"; } + @Override public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException { Object left = getLeftOperand().getValueInternal(state).getValue(); Object right = getRightOperand().getValueInternal(state).getValue(); - leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); - rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); + this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); + this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); return BooleanTypedValue.forValue(equalityCheck(state, left, right)); } @@ -54,38 +55,38 @@ public class OpEQ extends Operator { if (!left.isCompilable() || !right.isCompilable()) { return false; } - String leftdesc = left.getExitDescriptor(); - String rightdesc = right.getExitDescriptor(); - DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftdesc, rightdesc, leftActualDescriptor, rightActualDescriptor); - if (dc.areNumbers) { - return dc.areCompatible; - } - return true; + + String leftDesc = left.exitTypeDescriptor; + String rightDesc = right.exitTypeDescriptor; + DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, + this.leftActualDescriptor, this.rightActualDescriptor); + return (!dc.areNumbers || dc.areCompatible); } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - String leftDesc = getLeftOperand().getExitDescriptor(); - String rightDesc = getRightOperand().getExitDescriptor(); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + String leftDesc = getLeftOperand().exitTypeDescriptor; + String rightDesc = getRightOperand().exitTypeDescriptor; Label elseTarget = new Label(); Label endOfIf = new Label(); boolean leftPrim = CodeFlow.isPrimitive(leftDesc); boolean rightPrim = CodeFlow.isPrimitive(rightDesc); - DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor); + DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, + this.leftActualDescriptor, this.rightActualDescriptor); if (dc.areNumbers && dc.areCompatible) { char targetType = dc.compatibleType; - getLeftOperand().generateCode(mv, codeflow); + getLeftOperand().generateCode(mv, cf); if (!leftPrim) { CodeFlow.insertUnboxInsns(mv, targetType, leftDesc); } - codeflow.enterCompilationScope(); - getRightOperand().generateCode(mv, codeflow); - codeflow.exitCompilationScope(); + cf.enterCompilationScope(); + getRightOperand().generateCode(mv, cf); + cf.exitCompilationScope(); if (!rightPrim) { CodeFlow.insertUnboxInsns(mv, targetType, rightDesc); } @@ -110,28 +111,26 @@ public class OpEQ extends Operator { } } else { - getLeftOperand().generateCode(mv, codeflow); - getRightOperand().generateCode(mv, codeflow); + getLeftOperand().generateCode(mv, cf); + getRightOperand().generateCode(mv, cf); Label leftNotNull = new Label(); mv.visitInsn(DUP_X1); // Dup right on the top of the stack mv.visitJumpInsn(IFNONNULL,leftNotNull); - // Right is null! - mv.visitInsn(SWAP); - mv.visitInsn(POP); // remove it - Label rightNotNull = new Label(); - mv.visitJumpInsn(IFNONNULL, rightNotNull); - // Left is null too - mv.visitInsn(ICONST_1); - mv.visitJumpInsn(GOTO, endOfIf); - mv.visitLabel(rightNotNull); - mv.visitInsn(ICONST_0); - mv.visitJumpInsn(GOTO,endOfIf); - - + // Right is null! + mv.visitInsn(SWAP); + mv.visitInsn(POP); // remove it + Label rightNotNull = new Label(); + mv.visitJumpInsn(IFNONNULL, rightNotNull); + // Left is null too + mv.visitInsn(ICONST_1); + mv.visitJumpInsn(GOTO, endOfIf); + mv.visitLabel(rightNotNull); + mv.visitInsn(ICONST_0); + mv.visitJumpInsn(GOTO,endOfIf); mv.visitLabel(leftNotNull); - mv.visitMethodInsn(INVOKEVIRTUAL,"java/lang/Object","equals","(Ljava/lang/Object;)Z",false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false); mv.visitLabel(endOfIf); - codeflow.pushDescriptor("Z"); + cf.pushDescriptor("Z"); return; } mv.visitInsn(ICONST_1); @@ -139,7 +138,7 @@ public class OpEQ extends Operator { mv.visitLabel(elseTarget); mv.visitInsn(ICONST_0); mv.visitLabel(endOfIf); - codeflow.pushDescriptor("Z"); + cf.pushDescriptor("Z"); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java index ef39c604f26..8478ba140e8 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.asm.MethodVisitor; import org.springframework.expression.EvaluationException; @@ -28,15 +30,15 @@ import org.springframework.util.NumberUtils; * Implements greater-than-or-equal operator. * * @author Andy Clement + * @author Juergen Hoeller * @author Giovanni Dall'Oglio Risso * @since 3.0 */ public class OpGE extends Operator { - public OpGE(int pos, SpelNodeImpl... operands) { super(">=", pos, operands); - this.exitTypeDescriptor="Z"; + this.exitTypeDescriptor = "Z"; } @@ -45,8 +47,8 @@ public class OpGE extends Operator { Object left = getLeftOperand().getValueInternal(state).getValue(); Object right = getRightOperand().getValueInternal(state).getValue(); - leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); - rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); + this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); + this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); if (left instanceof Number && right instanceof Number) { Number leftNumber = (Number) left; @@ -57,20 +59,35 @@ public class OpGE extends Operator { BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) >= 0); } - - if (leftNumber instanceof Double || rightNumber instanceof Double) { + else if (leftNumber instanceof Double || rightNumber instanceof Double) { return BooleanTypedValue.forValue(leftNumber.doubleValue() >= rightNumber.doubleValue()); } - - if (leftNumber instanceof Float || rightNumber instanceof Float) { + else if (leftNumber instanceof Float || rightNumber instanceof Float) { return BooleanTypedValue.forValue(leftNumber.floatValue() >= rightNumber.floatValue()); } - - if (leftNumber instanceof Long || rightNumber instanceof Long) { + else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); + return BooleanTypedValue.forValue(leftBigInteger.compareTo(rightBigInteger) >= 0); + } + else if (leftNumber instanceof Long || rightNumber instanceof Long) { return BooleanTypedValue.forValue(leftNumber.longValue() >= rightNumber.longValue()); } - return BooleanTypedValue.forValue(leftNumber.intValue() >= rightNumber.intValue()); + else if (leftNumber instanceof Integer || rightNumber instanceof Integer) { + return BooleanTypedValue.forValue(leftNumber.intValue() >= rightNumber.intValue()); + } + else if (leftNumber instanceof Short || rightNumber instanceof Short) { + return BooleanTypedValue.forValue(leftNumber.shortValue() >= rightNumber.shortValue()); + } + else if (leftNumber instanceof Byte || rightNumber instanceof Byte) { + return BooleanTypedValue.forValue(leftNumber.byteValue() >= rightNumber.byteValue()); + } + else { + // Unknown Number subtypes -> best guess is double comparison + return BooleanTypedValue.forValue(leftNumber.doubleValue() >= rightNumber.doubleValue()); + } } + return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) >= 0); } @@ -80,8 +97,8 @@ public class OpGE extends Operator { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - generateComparisonCode(mv, codeflow, IFLT, IF_ICMPLT); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + generateComparisonCode(mv, cf, IFLT, IF_ICMPLT); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java index 7e022e6a499..9cb9ec2a8b9 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.asm.MethodVisitor; import org.springframework.expression.EvaluationException; @@ -40,13 +41,14 @@ public class OpGT extends Operator { this.exitTypeDescriptor = "Z"; } + @Override public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException { Object left = getLeftOperand().getValueInternal(state).getValue(); Object right = getRightOperand().getValueInternal(state).getValue(); - leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); - rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); + this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); + this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); if (left instanceof Number && right instanceof Number) { Number leftNumber = (Number) left; @@ -57,20 +59,33 @@ public class OpGT extends Operator { BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) > 0); } - - if (leftNumber instanceof Double || rightNumber instanceof Double) { + else if (leftNumber instanceof Double || rightNumber instanceof Double) { return BooleanTypedValue.forValue(leftNumber.doubleValue() > rightNumber.doubleValue()); } - - if (leftNumber instanceof Float || rightNumber instanceof Float) { + else if (leftNumber instanceof Float || rightNumber instanceof Float) { return BooleanTypedValue.forValue(leftNumber.floatValue() > rightNumber.floatValue()); } - - if (leftNumber instanceof Long || rightNumber instanceof Long) { + else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); + return BooleanTypedValue.forValue(leftBigInteger.compareTo(rightBigInteger) > 0); + } + else if (leftNumber instanceof Long || rightNumber instanceof Long) { return BooleanTypedValue.forValue(leftNumber.longValue() > rightNumber.longValue()); } - - return BooleanTypedValue.forValue(leftNumber.intValue() > rightNumber.intValue()); + else if (leftNumber instanceof Integer || rightNumber instanceof Integer) { + return BooleanTypedValue.forValue(leftNumber.intValue() > rightNumber.intValue()); + } + else if (leftNumber instanceof Short || rightNumber instanceof Short) { + return BooleanTypedValue.forValue(leftNumber.shortValue() > rightNumber.shortValue()); + } + else if (leftNumber instanceof Byte || rightNumber instanceof Byte) { + return BooleanTypedValue.forValue(leftNumber.byteValue() > rightNumber.byteValue()); + } + else { + // Unknown Number subtypes -> best guess is double comparison + return BooleanTypedValue.forValue(leftNumber.doubleValue() > rightNumber.doubleValue()); + } } if (left instanceof CharSequence && right instanceof CharSequence) { @@ -87,7 +102,8 @@ public class OpGT extends Operator { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - generateComparisonCode(mv, codeflow, IFLE, IF_ICMPLE); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + generateComparisonCode(mv, cf, IFLE, IF_ICMPLE); } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java index e1760e3bf8c..f7ec295e901 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.expression.EvaluationException; import org.springframework.expression.Operation; @@ -31,12 +32,13 @@ import org.springframework.util.Assert; * appropriate exceptions if the operand in question does not support increment. * * @author Andy Clement + * @author Juergen Hoeller * @author Giovanni Dall'Oglio Risso * @since 3.2 */ public class OpInc extends Operator { - private final boolean postfix; // false means prefix + private final boolean postfix; // false means prefix public OpInc(int pos, boolean postfix, SpelNodeImpl... operands) { @@ -49,41 +51,45 @@ public class OpInc extends Operator { @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { SpelNodeImpl operand = getLeftOperand(); - ValueRef valueRef = operand.getValueRef(state); - final TypedValue typedValue = valueRef.getValue(); - final Object value = typedValue.getValue(); + TypedValue typedValue = valueRef.getValue(); + Object value = typedValue.getValue(); TypedValue returnValue = typedValue; TypedValue newValue = null; if (value instanceof Number) { Number op1 = (Number) value; if (op1 instanceof BigDecimal) { - newValue = new TypedValue(((BigDecimal) op1).add(BigDecimal.ONE), - typedValue.getTypeDescriptor()); + newValue = new TypedValue(((BigDecimal) op1).add(BigDecimal.ONE), typedValue.getTypeDescriptor()); } else if (op1 instanceof Double) { - newValue = new TypedValue(op1.doubleValue() + 1.0d, - typedValue.getTypeDescriptor()); + newValue = new TypedValue(op1.doubleValue() + 1.0d, typedValue.getTypeDescriptor()); } else if (op1 instanceof Float) { - newValue = new TypedValue(op1.floatValue() + 1.0f, - typedValue.getTypeDescriptor()); + newValue = new TypedValue(op1.floatValue() + 1.0f, typedValue.getTypeDescriptor()); + } + else if (op1 instanceof BigInteger) { + newValue = new TypedValue(((BigInteger) op1).add(BigInteger.ONE), typedValue.getTypeDescriptor()); } else if (op1 instanceof Long) { - newValue = new TypedValue(op1.longValue() + 1L, - typedValue.getTypeDescriptor()); + newValue = new TypedValue(op1.longValue() + 1L, typedValue.getTypeDescriptor()); + } + else if (op1 instanceof Integer) { + newValue = new TypedValue(op1.intValue() + 1, typedValue.getTypeDescriptor()); } else if (op1 instanceof Short) { - newValue = new TypedValue(op1.shortValue() + (short) 1, - typedValue.getTypeDescriptor()); + newValue = new TypedValue(op1.shortValue() + (short) 1, typedValue.getTypeDescriptor()); + } + else if (op1 instanceof Byte) { + newValue = new TypedValue(op1.byteValue() + (byte) 1, typedValue.getTypeDescriptor()); } else { - newValue = new TypedValue(op1.intValue() + 1, - typedValue.getTypeDescriptor()); + // Unknown Number subtype -> best guess is double increment + newValue = new TypedValue(op1.doubleValue() + 1.0d, typedValue.getTypeDescriptor()); } } + if (newValue == null) { try { newValue = state.operate(Operation.ADD, returnValue.getValue(), 1); @@ -103,10 +109,9 @@ public class OpInc extends Operator { valueRef.setValue(newValue.getValue()); } catch (SpelEvaluationException see) { - // if unable to set the value the operand is not writable (e.g. 1++ ) + // If unable to set the value the operand is not writable (e.g. 1++ ) if (see.getMessageCode() == SpelMessage.SETVALUE_NOT_SUPPORTED) { - throw new SpelEvaluationException(operand.getStartPosition(), - SpelMessage.OPERAND_NOT_INCREMENTABLE); + throw new SpelEvaluationException(operand.getStartPosition(), SpelMessage.OPERAND_NOT_INCREMENTABLE); } else { throw see; @@ -114,7 +119,7 @@ public class OpInc extends Operator { } if (!this.postfix) { - // the return value is the new value, not the original value + // The return value is the new value, not the original value returnValue = newValue; } @@ -123,7 +128,7 @@ public class OpInc extends Operator { @Override public String toStringAST() { - return new StringBuilder().append(getLeftOperand().toStringAST()).append("++").toString(); + return getLeftOperand().toStringAST() + "++"; } @Override diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java index 90e656e5502..65079f0ba7d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.asm.MethodVisitor; import org.springframework.expression.EvaluationException; @@ -29,26 +30,25 @@ import org.springframework.util.NumberUtils; * Implements the less-than-or-equal operator. * * @author Andy Clement + * @author Juergen Hoeller * @author Giovanni Dall'Oglio Risso * @since 3.0 */ public class OpLE extends Operator { - public OpLE(int pos, SpelNodeImpl... operands) { super("<=", pos, operands); - this.exitTypeDescriptor="Z"; + this.exitTypeDescriptor = "Z"; } @Override - public BooleanTypedValue getValueInternal(ExpressionState state) - throws EvaluationException { + public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException { Object left = getLeftOperand().getValueInternal(state).getValue(); Object right = getRightOperand().getValueInternal(state).getValue(); - leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); - rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); + this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); + this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); if (left instanceof Number && right instanceof Number) { Number leftNumber = (Number) left; @@ -59,20 +59,33 @@ public class OpLE extends Operator { BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) <= 0); } - - if (leftNumber instanceof Double || rightNumber instanceof Double) { + else if (leftNumber instanceof Double || rightNumber instanceof Double) { return BooleanTypedValue.forValue(leftNumber.doubleValue() <= rightNumber.doubleValue()); } - - if (leftNumber instanceof Float || rightNumber instanceof Float) { + else if (leftNumber instanceof Float || rightNumber instanceof Float) { return BooleanTypedValue.forValue(leftNumber.floatValue() <= rightNumber.floatValue()); } - - if (leftNumber instanceof Long || rightNumber instanceof Long) { + else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); + return BooleanTypedValue.forValue(leftBigInteger.compareTo(rightBigInteger) <= 0); + } + else if (leftNumber instanceof Long || rightNumber instanceof Long) { return BooleanTypedValue.forValue(leftNumber.longValue() <= rightNumber.longValue()); } - - return BooleanTypedValue.forValue(leftNumber.intValue() <= rightNumber.intValue()); + else if (leftNumber instanceof Integer || rightNumber instanceof Integer) { + return BooleanTypedValue.forValue(leftNumber.intValue() <= rightNumber.intValue()); + } + else if (leftNumber instanceof Short || rightNumber instanceof Short) { + return BooleanTypedValue.forValue(leftNumber.shortValue() <= rightNumber.shortValue()); + } + else if (leftNumber instanceof Byte || rightNumber instanceof Byte) { + return BooleanTypedValue.forValue(leftNumber.byteValue() <= rightNumber.byteValue()); + } + else { + // Unknown Number subtypes -> best guess is double comparison + return BooleanTypedValue.forValue(leftNumber.doubleValue() <= rightNumber.doubleValue()); + } } return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) <= 0); @@ -84,8 +97,8 @@ public class OpLE extends Operator { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - generateComparisonCode(mv, codeflow, IFGT, IF_ICMPGT); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + generateComparisonCode(mv, cf, IFGT, IF_ICMPGT); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java index 615eda59733..4f0857595ef 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.asm.MethodVisitor; import org.springframework.expression.EvaluationException; @@ -40,13 +41,14 @@ public class OpLT extends Operator { this.exitTypeDescriptor = "Z"; } + @Override public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException { Object left = getLeftOperand().getValueInternal(state).getValue(); Object right = getRightOperand().getValueInternal(state).getValue(); - leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); - rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); + this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); + this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); if (left instanceof Number && right instanceof Number) { Number leftNumber = (Number) left; @@ -57,20 +59,33 @@ public class OpLT extends Operator { BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) < 0); } - - if (leftNumber instanceof Double || rightNumber instanceof Double) { + else if (leftNumber instanceof Double || rightNumber instanceof Double) { return BooleanTypedValue.forValue(leftNumber.doubleValue() < rightNumber.doubleValue()); } - - if (leftNumber instanceof Float || rightNumber instanceof Float) { + else if (leftNumber instanceof Float || rightNumber instanceof Float) { return BooleanTypedValue.forValue(leftNumber.floatValue() < rightNumber.floatValue()); } - - if (leftNumber instanceof Long || rightNumber instanceof Long) { + else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); + return BooleanTypedValue.forValue(leftBigInteger.compareTo(rightBigInteger) < 0); + } + else if (leftNumber instanceof Long || rightNumber instanceof Long) { return BooleanTypedValue.forValue(leftNumber.longValue() < rightNumber.longValue()); } - - return BooleanTypedValue.forValue(leftNumber.intValue() < rightNumber.intValue()); + else if (leftNumber instanceof Integer || rightNumber instanceof Integer) { + return BooleanTypedValue.forValue(leftNumber.intValue() < rightNumber.intValue()); + } + else if (leftNumber instanceof Short || rightNumber instanceof Short) { + return BooleanTypedValue.forValue(leftNumber.shortValue() < rightNumber.shortValue()); + } + else if (leftNumber instanceof Byte || rightNumber instanceof Byte) { + return BooleanTypedValue.forValue(leftNumber.byteValue() < rightNumber.byteValue()); + } + else { + // Unknown Number subtypes -> best guess is double comparison + return BooleanTypedValue.forValue(leftNumber.doubleValue() < rightNumber.doubleValue()); + } } if (left instanceof CharSequence && right instanceof CharSequence) { @@ -87,8 +102,8 @@ public class OpLT extends Operator { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - generateComparisonCode(mv, codeflow, IFGE, IF_ICMPGE); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + generateComparisonCode(mv, cf, IFGE, IF_ICMPGE); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java index ccff337d76e..e98763afe3f 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.asm.MethodVisitor; import org.springframework.expression.EvaluationException; @@ -29,24 +30,22 @@ import org.springframework.util.NumberUtils; /** * The minus operator supports: *
    - *
  • subtraction of {@code BigDecimal} - *
  • subtraction of doubles (floats are represented as doubles) - *
  • subtraction of longs - *
  • subtraction of integers - *
  • subtraction of an int from a string of one character (effectively decreasing that - * character), so 'd'-3='a' + *
  • subtraction of numbers + *
  • subtraction of an int from a string of one character + * (effectively decreasing that character), so 'd'-3='a' *
- * It can be used as a unary operator for numbers ({@code BigDecimal}/double/long/int). + * + *

It can be used as a unary operator for numbers. * The standard promotions are performed when the operand types vary (double-int=double). * For other options it defers to the registered overloader. * * @author Andy Clement + * @author Juergen Hoeller * @author Giovanni Dall'Oglio Risso * @since 3.0 */ public class OpMinus extends Operator { - public OpMinus(int pos, SpelNodeImpl... operands) { super("-", pos, operands); } @@ -54,38 +53,45 @@ public class OpMinus extends Operator { @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { - SpelNodeImpl leftOp = getLeftOperand(); SpelNodeImpl rightOp = getRightOperand(); - if (rightOp == null) {// If only one operand, then this is unary minus + if (rightOp == null) { // if only one operand, then this is unary minus Object operand = leftOp.getValueInternal(state).getValue(); if (operand instanceof Number) { - Number n = (Number) operand; - if (operand instanceof BigDecimal) { - BigDecimal bdn = (BigDecimal) n; - return new TypedValue(bdn.negate()); + return new TypedValue(((BigDecimal) operand).negate()); } - - if (operand instanceof Double) { + else if (operand instanceof Double) { this.exitTypeDescriptor = "D"; - return new TypedValue(0 - n.doubleValue()); + return new TypedValue(0 - ((Number) operand).doubleValue()); } - - if (operand instanceof Float) { + else if (operand instanceof Float) { this.exitTypeDescriptor = "F"; - return new TypedValue(0 - n.floatValue()); + return new TypedValue(0 - ((Number) operand).floatValue()); } - - if (operand instanceof Long) { + else if (operand instanceof BigInteger) { + return new TypedValue(((BigInteger) operand).negate()); + } + else if (operand instanceof Long) { this.exitTypeDescriptor = "J"; - return new TypedValue(0 - n.longValue()); + return new TypedValue(0 - ((Number) operand).longValue()); + } + else if (operand instanceof Integer) { + this.exitTypeDescriptor = "I"; + return new TypedValue(0 - ((Number) operand).intValue()); + } + else if (operand instanceof Short) { + return new TypedValue(0 - ((Number) operand).shortValue()); + } + else if (operand instanceof Byte) { + return new TypedValue(0 - ((Number) operand).byteValue()); + } + else { + // Unknown Number subtypes -> best guess is double subtraction + return new TypedValue(0 - ((Number) operand).doubleValue()); } - this.exitTypeDescriptor = "I"; - return new TypedValue(0 - n.intValue()); } - return state.operate(Operation.SUBTRACT, operand, null); } @@ -101,37 +107,46 @@ public class OpMinus extends Operator { BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return new TypedValue(leftBigDecimal.subtract(rightBigDecimal)); } - - if (leftNumber instanceof Double || rightNumber instanceof Double) { - if (leftNumber instanceof Double && rightNumber instanceof Double) { + else if (leftNumber instanceof Double || rightNumber instanceof Double) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "D"; } return new TypedValue(leftNumber.doubleValue() - rightNumber.doubleValue()); } - - if (leftNumber instanceof Float || rightNumber instanceof Float) { - if (leftNumber instanceof Float && rightNumber instanceof Float) { + else if (leftNumber instanceof Float || rightNumber instanceof Float) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "F"; } return new TypedValue(leftNumber.floatValue() - rightNumber.floatValue()); } - - if (leftNumber instanceof Long || rightNumber instanceof Long) { - if (leftNumber instanceof Long && rightNumber instanceof Long) { + else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); + return new TypedValue(leftBigInteger.subtract(rightBigInteger)); + } + else if (leftNumber instanceof Long || rightNumber instanceof Long) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "J"; } return new TypedValue(leftNumber.longValue() - rightNumber.longValue()); } - this.exitTypeDescriptor = "I"; - return new TypedValue(leftNumber.intValue() - rightNumber.intValue()); + else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) { + if (leftNumber instanceof Integer && rightNumber instanceof Integer) { + this.exitTypeDescriptor = "I"; + } + return new TypedValue(leftNumber.intValue() - rightNumber.intValue()); + } + else { + // Unknown Number subtypes -> best guess is double subtraction + return new TypedValue(leftNumber.doubleValue() - rightNumber.doubleValue()); + } } - else if (left instanceof String && right instanceof Integer - && ((String) left).length() == 1) { + + if (left instanceof String && right instanceof Integer && ((String) left).length() == 1) { String theString = (String) left; Integer theInteger = (Integer) right; - // implements character - int (ie. b - 1 = a) - return new TypedValue(Character.toString((char) - (theString.charAt(0) - theInteger))); + // Implements character - int (ie. b - 1 = a) + return new TypedValue(Character.toString((char) (theString.charAt(0) - theInteger))); } return state.operate(Operation.SUBTRACT, left, right); @@ -139,45 +154,47 @@ public class OpMinus extends Operator { @Override public String toStringAST() { - if (getRightOperand() == null) { // unary minus - return new StringBuilder().append("-").append(getLeftOperand().toStringAST()).toString(); + if (getRightOperand() == null) { // unary minus + return "-" + getLeftOperand().toStringAST(); } return super.toStringAST(); } @Override public SpelNodeImpl getRightOperand() { - if (this.children.length<2) {return null;} + if (this.children.length < 2) { + return null; + } return this.children[1]; } - + @Override public boolean isCompilable() { if (!getLeftOperand().isCompilable()) { return false; } - if (this.children.length>1) { - if (!getRightOperand().isCompilable()) { - return false; - } + if (this.children.length > 1) { + if (!getRightOperand().isCompilable()) { + return false; + } } - return this.exitTypeDescriptor!=null; + return (this.exitTypeDescriptor != null); } - + @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - getLeftOperand().generateCode(mv, codeflow); - String leftdesc = getLeftOperand().getExitDescriptor(); - if (!CodeFlow.isPrimitive(leftdesc)) { - CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc); - } - if (this.children.length>1) { - codeflow.enterCompilationScope(); - getRightOperand().generateCode(mv, codeflow); - String rightdesc = getRightOperand().getExitDescriptor(); - codeflow.exitCompilationScope(); - if (!CodeFlow.isPrimitive(rightdesc)) { - CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + getLeftOperand().generateCode(mv, cf); + String leftDesc = getLeftOperand().exitTypeDescriptor; + if (!CodeFlow.isPrimitive(leftDesc)) { + CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc); + } + if (this.children.length > 1) { + cf.enterCompilationScope(); + getRightOperand().generateCode(mv, cf); + String rightDesc = getRightOperand().exitTypeDescriptor; + cf.exitCompilationScope(); + if (!CodeFlow.isPrimitive(rightDesc)) { + CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc); } switch (this.exitTypeDescriptor.charAt(0)) { case 'I': @@ -186,14 +203,15 @@ public class OpMinus extends Operator { case 'J': mv.visitInsn(LSUB); break; - case 'F': + case 'F': mv.visitInsn(FSUB); break; case 'D': mv.visitInsn(DSUB); - break; + break; default: - throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'"); + throw new IllegalStateException( + "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'"); } } else { @@ -204,17 +222,18 @@ public class OpMinus extends Operator { case 'J': mv.visitInsn(LNEG); break; - case 'F': + case 'F': mv.visitInsn(FNEG); break; case 'D': mv.visitInsn(DNEG); - break; + break; default: - throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'"); - } + throw new IllegalStateException( + "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'"); + } } - codeflow.pushDescriptor(this.exitTypeDescriptor); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java index 2cc81ced75b..c76f538c995 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.asm.MethodVisitor; import org.springframework.expression.EvaluationException; @@ -30,12 +31,12 @@ import org.springframework.util.NumberUtils; * Implements the modulus operator. * * @author Andy Clement + * @author Juergen Hoeller * @author Giovanni Dall'Oglio Risso * @since 3.0 */ public class OpModulus extends Operator { - public OpModulus(int pos, SpelNodeImpl... operands) { super("%", pos, operands); } @@ -45,6 +46,7 @@ public class OpModulus extends Operator { public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { Object leftOperand = getLeftOperand().getValueInternal(state).getValue(); Object rightOperand = getRightOperand().getValueInternal(state).getValue(); + if (leftOperand instanceof Number && rightOperand instanceof Number) { Number leftNumber = (Number) leftOperand; Number rightNumber = (Number) rightOperand; @@ -54,37 +56,44 @@ public class OpModulus extends Operator { BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return new TypedValue(leftBigDecimal.remainder(rightBigDecimal)); } - - if (leftNumber instanceof Double || rightNumber instanceof Double) { - if (leftNumber instanceof Double && rightNumber instanceof Double) { + else if (leftNumber instanceof Double || rightNumber instanceof Double) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "D"; } return new TypedValue(leftNumber.doubleValue() % rightNumber.doubleValue()); } - - if (leftNumber instanceof Float || rightNumber instanceof Float) { - if (leftNumber instanceof Float && rightNumber instanceof Float) { + else if (leftNumber instanceof Float || rightNumber instanceof Float) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "F"; } return new TypedValue(leftNumber.floatValue() % rightNumber.floatValue()); } - - if (leftNumber instanceof Long || rightNumber instanceof Long) { - if (leftNumber instanceof Long && rightNumber instanceof Long) { + else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); + return new TypedValue(leftBigInteger.remainder(rightBigInteger)); + } + else if (leftNumber instanceof Long || rightNumber instanceof Long) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "J"; } return new TypedValue(leftNumber.longValue() % rightNumber.longValue()); } - - if (leftNumber instanceof Integer && rightNumber instanceof Integer) { - this.exitTypeDescriptor = "I"; + else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) { + if (leftNumber instanceof Integer && rightNumber instanceof Integer) { + this.exitTypeDescriptor = "I"; + } + return new TypedValue(leftNumber.intValue() % rightNumber.intValue()); + } + else { + // Unknown Number subtypes -> best guess is double division + return new TypedValue(leftNumber.doubleValue() % rightNumber.doubleValue()); } - return new TypedValue(leftNumber.intValue() % rightNumber.intValue()); } return state.operate(Operation.MODULUS, leftOperand, rightOperand); } - + @Override public boolean isCompilable() { if (!getLeftOperand().isCompilable()) { @@ -97,21 +106,21 @@ public class OpModulus extends Operator { } return this.exitTypeDescriptor!=null; } - + @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - getLeftOperand().generateCode(mv, codeflow); - String leftdesc = getLeftOperand().getExitDescriptor(); - if (!CodeFlow.isPrimitive(leftdesc)) { - CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + getLeftOperand().generateCode(mv, cf); + String leftDesc = getLeftOperand().exitTypeDescriptor; + if (!CodeFlow.isPrimitive(leftDesc)) { + CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc); } if (this.children.length > 1) { - codeflow.enterCompilationScope(); - getRightOperand().generateCode(mv, codeflow); - String rightdesc = getRightOperand().getExitDescriptor(); - codeflow.exitCompilationScope(); - if (!CodeFlow.isPrimitive(rightdesc)) { - CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc); + cf.enterCompilationScope(); + getRightOperand().generateCode(mv, cf); + String rightDesc = getRightOperand().exitTypeDescriptor; + cf.exitCompilationScope(); + if (!CodeFlow.isPrimitive(rightDesc)) { + CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc); } switch (this.exitTypeDescriptor.charAt(0)) { case 'I': @@ -127,10 +136,11 @@ public class OpModulus extends Operator { mv.visitInsn(DREM); break; default: - throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'"); + throw new IllegalStateException( + "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'"); } } - codeflow.pushDescriptor(this.exitTypeDescriptor); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java index 4d1fc60b630..72efbfc2043 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.asm.MethodVisitor; import org.springframework.expression.EvaluationException; @@ -30,25 +31,26 @@ import org.springframework.util.NumberUtils; * Implements the {@code multiply} operator. * *

Conversions and promotions are handled as defined in - * Section - * 5.6.2 of the Java Language Specification, with the addiction of {@code BigDecimal} management: + * Section 5.6.2 of the + * Java Language Specification, with the addiction of {@code BigDecimal}/{@code BigInteger} management: * *

If any of the operands is of a reference type, unboxing conversion (Section 5.1.8) * is performed. Then:
* If either operand is of type {@code BigDecimal}, the other is converted to {@code BigDecimal}.
* If either operand is of type double, the other is converted to double.
* Otherwise, if either operand is of type float, the other is converted to float.
+ * If either operand is of type {@code BigInteger}, the other is converted to {@code BigInteger}.
* Otherwise, if either operand is of type long, the other is converted to long.
* Otherwise, both operands are converted to type int. * * @author Andy Clement + * @author Juergen Hoeller * @author Sam Brannen * @author Giovanni Dall'Oglio Risso * @since 3.0 */ public class OpMultiply extends Operator { - public OpMultiply(int pos, SpelNodeImpl... operands) { super("*", pos, operands); } @@ -60,55 +62,60 @@ public class OpMultiply extends Operator { * for types not supported here. *

Supported operand types: *

    - *
  • {@code BigDecimal} - *
  • doubles - *
  • longs - *
  • integers + *
  • numbers *
  • String and int ('abc' * 2 == 'abcabc') *
*/ @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { - Object leftOperand = getLeftOperand().getValueInternal(state).getValue(); Object rightOperand = getRightOperand().getValueInternal(state).getValue(); if (leftOperand instanceof Number && rightOperand instanceof Number) { Number leftNumber = (Number) leftOperand; Number rightNumber = (Number) rightOperand; + if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return new TypedValue(leftBigDecimal.multiply(rightBigDecimal)); } - - if (leftNumber instanceof Double || rightNumber instanceof Double) { - if (leftNumber instanceof Double && rightNumber instanceof Double) { + else if (leftNumber instanceof Double || rightNumber instanceof Double) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "D"; } - return new TypedValue(leftNumber.doubleValue() - * rightNumber.doubleValue()); + return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue()); } - - if (leftNumber instanceof Float || rightNumber instanceof Float) { - if (leftNumber instanceof Float && rightNumber instanceof Float) { + else if (leftNumber instanceof Float || rightNumber instanceof Float) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "F"; } return new TypedValue(leftNumber.floatValue() * rightNumber.floatValue()); } - - if (leftNumber instanceof Long || rightNumber instanceof Long) { - if (leftNumber instanceof Long && rightNumber instanceof Long) { + else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); + return new TypedValue(leftBigInteger.multiply(rightBigInteger)); + } + else if (leftNumber instanceof Long || rightNumber instanceof Long) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "J"; } return new TypedValue(leftNumber.longValue() * rightNumber.longValue()); } - if (leftNumber instanceof Integer && rightNumber instanceof Integer) { - this.exitTypeDescriptor = "I"; + else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) { + if (leftNumber instanceof Integer && rightNumber instanceof Integer) { + this.exitTypeDescriptor = "I"; + } + return new TypedValue(leftNumber.intValue() * rightNumber.intValue()); + } + else { + // Unknown Number subtypes -> best guess is double multiplication + return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue()); } - return new TypedValue(leftNumber.intValue() * rightNumber.intValue()); } - else if (leftOperand instanceof String && rightOperand instanceof Integer) { + + if (leftOperand instanceof String && rightOperand instanceof Integer) { int repeats = (Integer) rightOperand; StringBuilder result = new StringBuilder(); for (int i = 0; i < repeats; i++) { @@ -119,34 +126,34 @@ public class OpMultiply extends Operator { return state.operate(Operation.MULTIPLY, leftOperand, rightOperand); } - + @Override public boolean isCompilable() { if (!getLeftOperand().isCompilable()) { return false; } - if (this.children.length>1) { + if (this.children.length > 1) { if (!getRightOperand().isCompilable()) { return false; } } - return this.exitTypeDescriptor!=null; + return (this.exitTypeDescriptor != null); } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - getLeftOperand().generateCode(mv, codeflow); - String leftdesc = getLeftOperand().getExitDescriptor(); - if (!CodeFlow.isPrimitive(leftdesc)) { - CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + getLeftOperand().generateCode(mv, cf); + String leftDesc = getLeftOperand().exitTypeDescriptor; + if (!CodeFlow.isPrimitive(leftDesc)) { + CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc); } - if (this.children.length>1) { - codeflow.enterCompilationScope(); - getRightOperand().generateCode(mv, codeflow); - String rightdesc = getRightOperand().getExitDescriptor(); - codeflow.exitCompilationScope(); - if (!CodeFlow.isPrimitive(rightdesc)) { - CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc); + if (this.children.length > 1) { + cf.enterCompilationScope(); + getRightOperand().generateCode(mv, cf); + String rightDesc = getRightOperand().exitTypeDescriptor; + cf.exitCompilationScope(); + if (!CodeFlow.isPrimitive(rightDesc)) { + CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc); } switch (this.exitTypeDescriptor.charAt(0)) { case 'I': @@ -162,10 +169,11 @@ public class OpMultiply extends Operator { mv.visitInsn(DMUL); break; default: - throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'"); + throw new IllegalStateException( + "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'"); } } - codeflow.pushDescriptor(this.exitTypeDescriptor); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java index f675773851c..543e459ebeb 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java @@ -54,19 +54,17 @@ public class OpNE extends Operator { if (!left.isCompilable() || !right.isCompilable()) { return false; } - String leftdesc = left.getExitDescriptor(); - String rightdesc = right.getExitDescriptor(); - DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftdesc, rightdesc, leftActualDescriptor, rightActualDescriptor); - if (dc.areNumbers) { - return dc.areCompatible; - } - return true; + + String leftDesc = left.exitTypeDescriptor; + String rightDesc = right.exitTypeDescriptor; + DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor); + return (!dc.areNumbers || dc.areCompatible); } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - String leftDesc = getLeftOperand().getExitDescriptor(); - String rightDesc = getRightOperand().getExitDescriptor(); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + String leftDesc = getLeftOperand().exitTypeDescriptor; + String rightDesc = getRightOperand().exitTypeDescriptor; Label elseTarget = new Label(); Label endOfIf = new Label(); boolean leftPrim = CodeFlow.isPrimitive(leftDesc); @@ -77,14 +75,14 @@ public class OpNE extends Operator { if (dc.areNumbers && dc.areCompatible) { char targetType = dc.compatibleType; - getLeftOperand().generateCode(mv, codeflow); + getLeftOperand().generateCode(mv, cf); if (!leftPrim) { CodeFlow.insertUnboxInsns(mv, targetType, leftDesc); } - codeflow.enterCompilationScope(); - getRightOperand().generateCode(mv, codeflow); - codeflow.exitCompilationScope(); + cf.enterCompilationScope(); + getRightOperand().generateCode(mv, cf); + cf.exitCompilationScope(); if (!rightPrim) { CodeFlow.insertUnboxInsns(mv, targetType, rightDesc); } @@ -109,8 +107,8 @@ public class OpNE extends Operator { } } else { - getLeftOperand().generateCode(mv, codeflow); - getRightOperand().generateCode(mv, codeflow); + getLeftOperand().generateCode(mv, cf); + getRightOperand().generateCode(mv, cf); mv.visitJumpInsn(IF_ACMPEQ, elseTarget); } mv.visitInsn(ICONST_1); @@ -118,7 +116,7 @@ public class OpNE extends Operator { mv.visitLabel(elseTarget); mv.visitInsn(ICONST_0); mv.visitLabel(endOfIf); - codeflow.pushDescriptor("Z"); + cf.pushDescriptor("Z"); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpOr.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpOr.java index 45e6b03ceeb..e1c6ab53d4c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpOr.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpOr.java @@ -71,34 +71,31 @@ public class OpOr extends Operator { @Override public boolean isCompilable() { SpelNodeImpl left = getLeftOperand(); - SpelNodeImpl right= getRightOperand(); - if (!left.isCompilable() || !right.isCompilable()) { - return false; - } - return - CodeFlow.isBooleanCompatible(left.getExitDescriptor()) && - CodeFlow.isBooleanCompatible(right.getExitDescriptor()); + SpelNodeImpl right = getRightOperand(); + return (left.isCompilable() && right.isCompilable() && + CodeFlow.isBooleanCompatible(left.exitTypeDescriptor) && + CodeFlow.isBooleanCompatible(right.exitTypeDescriptor)); } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { // pseudo: if (leftOperandValue) { result=true; } else { result=rightOperandValue; } Label elseTarget = new Label(); Label endOfIf = new Label(); - codeflow.enterCompilationScope(); - getLeftOperand().generateCode(mv, codeflow); - codeflow.unboxBooleanIfNecessary(mv); - codeflow.exitCompilationScope(); + cf.enterCompilationScope(); + getLeftOperand().generateCode(mv, cf); + cf.unboxBooleanIfNecessary(mv); + cf.exitCompilationScope(); mv.visitJumpInsn(IFEQ, elseTarget); mv.visitLdcInsn(1); // TRUE mv.visitJumpInsn(GOTO,endOfIf); mv.visitLabel(elseTarget); - codeflow.enterCompilationScope(); - getRightOperand().generateCode(mv, codeflow); - codeflow.unboxBooleanIfNecessary(mv); - codeflow.exitCompilationScope(); + cf.enterCompilationScope(); + getRightOperand().generateCode(mv, cf); + cf.unboxBooleanIfNecessary(mv); + cf.exitCompilationScope(); mv.visitLabel(endOfIf); - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java index b7a5f921065..5e5cc65052c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.asm.MethodVisitor; import org.springframework.core.convert.TypeDescriptor; @@ -32,24 +33,22 @@ import org.springframework.util.NumberUtils; /** * The plus operator will: *
    - *
  • add {@code BigDecimal} - *
  • add doubles (floats are represented as doubles) - *
  • add longs - *
  • add integers + *
  • add numbers *
  • concatenate strings *
- * It can be used as a unary operator for numbers ({@code BigDecimal}/double/long/int). + * + *

It can be used as a unary operator for numbers. * The standard promotions are performed when the operand types vary (double+int=double). * For other options it defers to the registered overloader. * * @author Andy Clement + * @author Juergen Hoeller * @author Ivo Smid * @author Giovanni Dall'Oglio Risso * @since 3.0 */ public class OpPlus extends Operator { - public OpPlus(int pos, SpelNodeImpl... operands) { super("+", pos, operands); Assert.notEmpty(operands); @@ -61,30 +60,30 @@ public class OpPlus extends Operator { SpelNodeImpl leftOp = getLeftOperand(); SpelNodeImpl rightOp = getRightOperand(); - if (rightOp == null) { // If only one operand, then this is unary plus + if (rightOp == null) { // if only one operand, then this is unary plus Object operandOne = leftOp.getValueInternal(state).getValue(); if (operandOne instanceof Number) { - if (operandOne instanceof Double || operandOne instanceof Long || operandOne instanceof BigDecimal) { - if (operandOne instanceof Double || operandOne instanceof Long) { - this.exitTypeDescriptor = (operandOne instanceof Double)?"D":"J"; - } - return new TypedValue(operandOne); + if (operandOne instanceof Double) { + this.exitTypeDescriptor = "D"; } - if (operandOne instanceof Float) { + else if (operandOne instanceof Float) { this.exitTypeDescriptor = "F"; - return new TypedValue(((Number) operandOne).floatValue()); } - this.exitTypeDescriptor = "I"; - return new TypedValue(((Number) operandOne).intValue()); + else if (operandOne instanceof Long) { + this.exitTypeDescriptor = "J"; + } + else if (operandOne instanceof Integer) { + this.exitTypeDescriptor = "I"; + } + return new TypedValue(operandOne); } return state.operate(Operation.ADD, operandOne, null); } - final TypedValue operandOneValue = leftOp.getValueInternal(state); - final Object leftOperand = operandOneValue.getValue(); - - final TypedValue operandTwoValue = rightOp.getValueInternal(state); - final Object rightOperand = operandTwoValue.getValue(); + TypedValue operandOneValue = leftOp.getValueInternal(state); + Object leftOperand = operandOneValue.getValue(); + TypedValue operandTwoValue = rightOp.getValueInternal(state); + Object rightOperand = operandTwoValue.getValue(); if (leftOperand instanceof Number && rightOperand instanceof Number) { Number leftNumber = (Number) leftOperand; @@ -95,47 +94,53 @@ public class OpPlus extends Operator { BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return new TypedValue(leftBigDecimal.add(rightBigDecimal)); } - if (leftNumber instanceof Double || rightNumber instanceof Double) { - if (leftNumber instanceof Double && rightNumber instanceof Double) { + else if (leftNumber instanceof Double || rightNumber instanceof Double) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "D"; } return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue()); } - if (leftNumber instanceof Float || rightNumber instanceof Float) { - if (leftNumber instanceof Float && rightNumber instanceof Float) { + else if (leftNumber instanceof Float || rightNumber instanceof Float) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "F"; } return new TypedValue(leftNumber.floatValue() + rightNumber.floatValue()); } - if (leftNumber instanceof Long || rightNumber instanceof Long) { - if (leftNumber instanceof Long && rightNumber instanceof Long) { + else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); + return new TypedValue(leftBigInteger.add(rightBigInteger)); + } + else if (leftNumber instanceof Long || rightNumber instanceof Long) { + if (leftNumber.getClass() == rightNumber.getClass()) { this.exitTypeDescriptor = "J"; } return new TypedValue(leftNumber.longValue() + rightNumber.longValue()); } - - // TODO what about overflow? - this.exitTypeDescriptor = "I"; - return new TypedValue(leftNumber.intValue() + rightNumber.intValue()); + else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) { + if (leftNumber instanceof Integer && rightNumber instanceof Integer) { + this.exitTypeDescriptor = "I"; + } + return new TypedValue(leftNumber.intValue() + rightNumber.intValue()); + } + else { + // Unknown Number subtypes -> best guess is double addition + return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue()); + } } if (leftOperand instanceof String && rightOperand instanceof String) { - return new TypedValue(new StringBuilder((String) leftOperand).append( - (String) rightOperand).toString()); + return new TypedValue((String) leftOperand + rightOperand); } if (leftOperand instanceof String) { - StringBuilder result = new StringBuilder((String) leftOperand); - result.append((rightOperand == null ? "null" : convertTypedValueToString( - operandTwoValue, state))); - return new TypedValue(result.toString()); + return new TypedValue( + leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state))); } if (rightOperand instanceof String) { - StringBuilder result = new StringBuilder((leftOperand == null ? "null" - : convertTypedValueToString(operandOneValue, state))); - result.append((String) rightOperand); - return new TypedValue(result.toString()); + return new TypedValue( + (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand); } return state.operate(Operation.ADD, leftOperand, rightOperand); @@ -143,8 +148,8 @@ public class OpPlus extends Operator { @Override public String toStringAST() { - if (this.children.length<2) { // unary plus - return new StringBuilder().append("+").append(getLeftOperand().toStringAST()).toString(); + if (this.children.length < 2) { // unary plus + return "+" + getLeftOperand().toStringAST(); } return super.toStringAST(); } @@ -160,21 +165,17 @@ public class OpPlus extends Operator { /** * Convert operand value to string using registered converter or using * {@code toString} method. - * * @param value typed value to be converted * @param state expression state * @return {@code TypedValue} instance converted to {@code String} */ private static String convertTypedValueToString(TypedValue value, ExpressionState state) { - final TypeConverter typeConverter = state.getEvaluationContext().getTypeConverter(); - final TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(String.class); - + TypeConverter typeConverter = state.getEvaluationContext().getTypeConverter(); + TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(String.class); if (typeConverter.canConvert(value.getTypeDescriptor(), typeDescriptor)) { - final Object obj = typeConverter.convertValue(value.getValue(), - value.getTypeDescriptor(), typeDescriptor); - return String.valueOf(obj); + return String.valueOf(typeConverter.convertValue(value.getValue(), + value.getTypeDescriptor(), typeDescriptor)); } - return String.valueOf(value.getValue()); } @@ -183,28 +184,28 @@ public class OpPlus extends Operator { if (!getLeftOperand().isCompilable()) { return false; } - if (this.children.length>1) { + if (this.children.length > 1) { if (!getRightOperand().isCompilable()) { return false; } } - return this.exitTypeDescriptor!=null; + return (this.exitTypeDescriptor != null); } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - getLeftOperand().generateCode(mv, codeflow); - String leftdesc = getLeftOperand().getExitDescriptor(); - if (!CodeFlow.isPrimitive(leftdesc)) { - CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + getLeftOperand().generateCode(mv, cf); + String leftDesc = getLeftOperand().exitTypeDescriptor; + if (!CodeFlow.isPrimitive(leftDesc)) { + CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc); } - if (this.children.length>1) { - codeflow.enterCompilationScope(); - getRightOperand().generateCode(mv, codeflow); - String rightdesc = getRightOperand().getExitDescriptor(); - codeflow.exitCompilationScope(); - if (!CodeFlow.isPrimitive(rightdesc)) { - CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc); + if (this.children.length > 1) { + cf.enterCompilationScope(); + getRightOperand().generateCode(mv, cf); + String rightDesc = getRightOperand().exitTypeDescriptor; + cf.exitCompilationScope(); + if (!CodeFlow.isPrimitive(rightDesc)) { + CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc); } switch (this.exitTypeDescriptor.charAt(0)) { case 'I': @@ -220,10 +221,11 @@ public class OpPlus extends Operator { mv.visitInsn(DADD); break; default: - throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'"); + throw new IllegalStateException( + "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'"); } } - codeflow.pushDescriptor(this.exitTypeDescriptor); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java index fb1ef1aa7f0..7172dd966bf 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.asm.Label; import org.springframework.asm.MethodVisitor; @@ -44,7 +45,11 @@ public abstract class Operator extends SpelNodeImpl { // descriptors are not providing enough information (for example a generic type // whose accessors seem to only be returning 'Object' - the actual descriptors may // indicate 'int') - protected String leftActualDescriptor, rightActualDescriptor; + + protected String leftActualDescriptor; + + protected String rightActualDescriptor; + public Operator(String payload,int pos,SpelNodeImpl... operands) { super(pos, operands); @@ -69,8 +74,7 @@ public abstract class Operator extends SpelNodeImpl { */ @Override public String toStringAST() { - StringBuilder sb = new StringBuilder(); - sb.append("("); + StringBuilder sb = new StringBuilder("("); sb.append(getChild(0).toStringAST()); for (int i = 1; i < getChildCount(); i++) { sb.append(" ").append(getOperatorName()).append(" "); @@ -86,69 +90,70 @@ public abstract class Operator extends SpelNodeImpl { if (!left.isCompilable() || !right.isCompilable()) { return false; } + // Supported operand types for equals (at the moment) - String leftDesc = left.getExitDescriptor(); - String rightDesc= right.getExitDescriptor(); - DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor); - if (dc.areNumbers) { - return dc.areCompatible; - } - return false; + String leftDesc = left.exitTypeDescriptor; + String rightDesc = right.exitTypeDescriptor; + DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, + this.leftActualDescriptor, this.rightActualDescriptor); + return (dc.areNumbers && dc.areCompatible); } /** * Numeric comparison operators share very similar generated code, only differing in * two comparison instructions. */ - protected void generateComparisonCode(MethodVisitor mv, CodeFlow codeflow, int compareInstruction1, - int compareInstruction2) { - String leftDesc = getLeftOperand().getExitDescriptor(); - String rightDesc = getRightOperand().getExitDescriptor(); + protected void generateComparisonCode(MethodVisitor mv, CodeFlow cf, int compInstruction1, int compInstruction2) { + String leftDesc = getLeftOperand().exitTypeDescriptor; + String rightDesc = getRightOperand().exitTypeDescriptor; boolean unboxLeft = !CodeFlow.isPrimitive(leftDesc); boolean unboxRight = !CodeFlow.isPrimitive(rightDesc); - DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor); + DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, + this.leftActualDescriptor, this.rightActualDescriptor); char targetType = dc.compatibleType;//CodeFlow.toPrimitiveTargetDesc(leftDesc); - getLeftOperand().generateCode(mv, codeflow); + getLeftOperand().generateCode(mv, cf); if (unboxLeft) { CodeFlow.insertUnboxInsns(mv, targetType, leftDesc); } - codeflow.enterCompilationScope(); - getRightOperand().generateCode(mv, codeflow); - codeflow.exitCompilationScope(); + cf.enterCompilationScope(); + getRightOperand().generateCode(mv, cf); + cf.exitCompilationScope(); if (unboxRight) { CodeFlow.insertUnboxInsns(mv, targetType, rightDesc); } + // assert: SpelCompiler.boxingCompatible(leftDesc, rightDesc) Label elseTarget = new Label(); Label endOfIf = new Label(); if (targetType=='D') { mv.visitInsn(DCMPG); - mv.visitJumpInsn(compareInstruction1, elseTarget); + mv.visitJumpInsn(compInstruction1, elseTarget); } else if (targetType=='F') { mv.visitInsn(FCMPG); - mv.visitJumpInsn(compareInstruction1, elseTarget); + mv.visitJumpInsn(compInstruction1, elseTarget); } else if (targetType=='J') { mv.visitInsn(LCMP); - mv.visitJumpInsn(compareInstruction1, elseTarget); + mv.visitJumpInsn(compInstruction1, elseTarget); } else if (targetType=='I') { - mv.visitJumpInsn(compareInstruction2, elseTarget); + mv.visitJumpInsn(compInstruction2, elseTarget); } else { throw new IllegalStateException("Unexpected descriptor "+leftDesc); } + // Other numbers are not yet supported (isCompilable will not have returned true) mv.visitInsn(ICONST_1); mv.visitJumpInsn(GOTO,endOfIf); mv.visitLabel(elseTarget); mv.visitInsn(ICONST_0); mv.visitLabel(endOfIf); - codeflow.pushDescriptor("Z"); + cf.pushDescriptor("Z"); } protected boolean equalityCheck(ExpressionState state, Object left, Object right) { @@ -161,20 +166,33 @@ public abstract class Operator extends SpelNodeImpl { BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return (leftBigDecimal == null ? rightBigDecimal == null : leftBigDecimal.compareTo(rightBigDecimal) == 0); } - - if (leftNumber instanceof Double || rightNumber instanceof Double) { + else if (leftNumber instanceof Double || rightNumber instanceof Double) { return (leftNumber.doubleValue() == rightNumber.doubleValue()); } - - if (leftNumber instanceof Float || rightNumber instanceof Float) { + else if (leftNumber instanceof Float || rightNumber instanceof Float) { return (leftNumber.floatValue() == rightNumber.floatValue()); } - - if (leftNumber instanceof Long || rightNumber instanceof Long) { + else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); + return (leftBigInteger == null ? rightBigInteger == null : leftBigInteger.compareTo(rightBigInteger) == 0); + } + else if (leftNumber instanceof Long || rightNumber instanceof Long) { return (leftNumber.longValue() == rightNumber.longValue()); } - - return (leftNumber.intValue() == rightNumber.intValue()); + else if (leftNumber instanceof Integer || rightNumber instanceof Integer) { + return (leftNumber.intValue() == rightNumber.intValue()); + } + else if (leftNumber instanceof Short || rightNumber instanceof Short) { + return (leftNumber.shortValue() == rightNumber.shortValue()); + } + else if (leftNumber instanceof Byte || rightNumber instanceof Byte) { + return (leftNumber.byteValue() == rightNumber.byteValue()); + } + else { + // Unknown Number subtypes -> best guess is double comparison + return (leftNumber.doubleValue() == rightNumber.doubleValue()); + } } if (left instanceof CharSequence && right instanceof CharSequence) { @@ -195,16 +213,21 @@ public abstract class Operator extends SpelNodeImpl { return false; } + /** * A descriptor comparison encapsulates the result of comparing descriptor for two operands and * describes at what level they are compatible. */ protected static class DescriptorComparison { - static DescriptorComparison NOT_NUMBERS = new DescriptorComparison(false,false,' '); - static DescriptorComparison INCOMPATIBLE_NUMBERS = new DescriptorComparison(true,false,' '); + + static DescriptorComparison NOT_NUMBERS = new DescriptorComparison(false, false, ' '); + + static DescriptorComparison INCOMPATIBLE_NUMBERS = new DescriptorComparison(true, false, ' '); final boolean areNumbers; // Were the two compared descriptor both for numbers? + final boolean areCompatible; // If they were numbers, were they compatible? + final char compatibleType; // When compatible, what is the descriptor of the common type private DescriptorComparison(boolean areNumbers, boolean areCompatible, char compatibleType) { @@ -220,18 +243,19 @@ public abstract class Operator extends SpelNodeImpl { * For generic types with unbound type variables the declared descriptor discovered may be 'Object' but * from the actual descriptor it is possible to observe that the objects are really numeric values (e.g. * ints). - * * @param leftDeclaredDescriptor the statically determinable left descriptor * @param rightDeclaredDescriptor the statically determinable right descriptor * @param leftActualDescriptor the dynamic/runtime left object descriptor * @param rightActualDescriptor the dynamic/runtime right object descriptor * @return a DescriptorComparison object indicating the type of compatibility, if any */ - public static DescriptorComparison checkNumericCompatibility(String leftDeclaredDescriptor, String rightDeclaredDescriptor, String leftActualDescriptor, String rightActualDescriptor) { + public static DescriptorComparison checkNumericCompatibility(String leftDeclaredDescriptor, + String rightDeclaredDescriptor, String leftActualDescriptor, String rightActualDescriptor) { + String ld = leftDeclaredDescriptor; String rd = rightDeclaredDescriptor; - boolean leftNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(ld) ; + boolean leftNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(ld); boolean rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd); // If the declared descriptors aren't providing the information, try the actual descriptors @@ -256,7 +280,6 @@ public abstract class Operator extends SpelNodeImpl { return DescriptorComparison.NOT_NUMBERS; } } - } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java index 7f38830d7a5..7ca75e5ce7a 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +40,7 @@ public class OperatorBetween extends Operator { super("between", pos, operands); } + /** * Returns a boolean based on whether a value is in the range expressed. The first * operand is any value whilst the second is a list of two values - those two values @@ -56,13 +57,13 @@ public class OperatorBetween extends Operator { throw new SpelEvaluationException(getRightOperand().getStartPosition(), SpelMessage.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST); } - List l = (List) right; - Object low = l.get(0); - Object high = l.get(1); - TypeComparator comparator = state.getTypeComparator(); + + List list = (List) right; + Object low = list.get(0); + Object high = list.get(1); + TypeComparator comp = state.getTypeComparator(); try { - return BooleanTypedValue.forValue((comparator.compare(left, low) >= 0 && - comparator.compare(left, high) <= 0)); + return BooleanTypedValue.forValue(comp.compare(left, low) >= 0 && comp.compare(left, high) <= 0); } catch (SpelEvaluationException ex) { ex.setPosition(getStartPosition()); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java index 231615e5f4c..5a3063b6f48 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java @@ -81,10 +81,10 @@ public class OperatorInstanceof extends Operator { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - getLeftOperand().generateCode(mv, codeflow); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + getLeftOperand().generateCode(mv, cf); mv.visitTypeInsn(INSTANCEOF,Type.getInternalName(this.type)); - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java index 573c80de42a..43071c72469 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,10 +44,10 @@ public class OperatorMatches extends Operator { /** * Check the first operand matches the regex specified as the second operand. * @param state the expression state - * @return true if the first operand matches the regex specified as the second - * operand, otherwise false - * @throws EvaluationException if there is a problem evaluating the expression (e.g. - * the regex is invalid) + * @return {@code true} if the first operand matches the regex specified as the + * second operand, otherwise {@code false} + * @throws EvaluationException if there is a problem evaluating the expression + * (e.g. the regex is invalid) */ @Override public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException { @@ -55,21 +55,23 @@ public class OperatorMatches extends Operator { SpelNodeImpl rightOp = getRightOperand(); Object left = leftOp.getValue(state, String.class); Object right = getRightOperand().getValueInternal(state).getValue(); + + if (!(left instanceof String)) { + throw new SpelEvaluationException(leftOp.getStartPosition(), + SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, left); + } + if (!(right instanceof String)) { + throw new SpelEvaluationException(rightOp.getStartPosition(), + SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, right); + } + try { - if (!(left instanceof String)) { - throw new SpelEvaluationException(leftOp.getStartPosition(), - SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, left); - } - if (!(right instanceof String)) { - throw new SpelEvaluationException(rightOp.getStartPosition(), - SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, right); - } Pattern pattern = Pattern.compile((String) right); Matcher matcher = pattern.matcher((String) left); return BooleanTypedValue.forValue(matcher.matches()); } - catch (PatternSyntaxException pse) { - throw new SpelEvaluationException(rightOp.getStartPosition(), pse, SpelMessage.INVALID_PATTERN, right); + catch (PatternSyntaxException ex) { + throw new SpelEvaluationException(rightOp.getStartPosition(), ex, SpelMessage.INVALID_PATTERN, right); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java index a24ff618cf2..73310399da1 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java @@ -33,7 +33,7 @@ import org.springframework.expression.spel.support.BooleanTypedValue; * @author Oliver Becker * @since 3.0 */ -public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do not extend BinaryOperator +public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so does not extend BinaryOperator public OperatorNot(int pos, SpelNodeImpl operand) { super(pos, operand); @@ -58,21 +58,19 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do @Override public String toStringAST() { - StringBuilder sb = new StringBuilder(); - sb.append("!").append(getChild(0).toStringAST()); - return sb.toString(); + return "!" + getChild(0).toStringAST(); } @Override public boolean isCompilable() { SpelNodeImpl child = this.children[0]; - return child.isCompilable() && CodeFlow.isBooleanCompatible(child.getExitDescriptor()); + return (child.isCompilable() && CodeFlow.isBooleanCompatible(child.exitTypeDescriptor)); } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - this.children[0].generateCode(mv, codeflow); - codeflow.unboxBooleanIfNecessary(mv); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + this.children[0].generateCode(mv, cf); + cf.unboxBooleanIfNecessary(mv); Label elseTarget = new Label(); Label endOfIf = new Label(); mv.visitJumpInsn(IFNE,elseTarget); @@ -81,7 +79,7 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do mv.visitLabel(elseTarget); mv.visitInsn(ICONST_0); // FALSE mv.visitLabel(endOfIf); - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorPower.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorPower.java index 1ad724c1b20..e7c36618fac 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorPower.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorPower.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.math.BigDecimal; +import java.math.BigInteger; import org.springframework.expression.EvaluationException; import org.springframework.expression.Operation; @@ -54,22 +55,19 @@ public class OperatorPower extends Operator { BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); return new TypedValue(leftBigDecimal.pow(rightNumber.intValue())); } - - if (leftNumber instanceof Double || rightNumber instanceof Double) { + else if (leftNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + return new TypedValue(leftBigInteger.pow(rightNumber.intValue())); + } + else if (leftNumber instanceof Double || rightNumber instanceof Double) { return new TypedValue(Math.pow(leftNumber.doubleValue(), rightNumber.doubleValue())); } - - if (leftNumber instanceof Float || rightNumber instanceof Float) { + else if (leftNumber instanceof Float || rightNumber instanceof Float) { return new TypedValue(Math.pow(leftNumber.floatValue(), rightNumber.floatValue())); } - if (leftNumber instanceof Long || rightNumber instanceof Long) { - double d = Math.pow(leftNumber.longValue(), rightNumber.longValue()); - return new TypedValue((long) d); - } - - double d = Math.pow(leftNumber.longValue(), rightNumber.longValue()); - if (d > Integer.MAX_VALUE) { + double d = Math.pow(leftNumber.doubleValue(), rightNumber.doubleValue()); + if (d > Integer.MAX_VALUE || leftNumber instanceof Long || rightNumber instanceof Long) { return new TypedValue((long) d); } else { diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Projection.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Projection.java index d7fcb505d16..2e91f8b32f2 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Projection.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Projection.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,7 @@ public class Projection extends SpelNodeImpl { private final boolean nullSafe; + public Projection(boolean nullSafe, int pos, SpelNodeImpl expression) { super(pos, expression); this.nullSafe = nullSafe; @@ -80,7 +81,7 @@ public class Projection extends SpelNodeImpl { state.popActiveContextObject(); } } - return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this); // TODO unable to build correct type descriptor + return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this); // TODO unable to build correct type descriptor } if (operand instanceof Collection || operandIsArray) { @@ -118,20 +119,18 @@ public class Projection extends SpelNodeImpl { if (operand==null) { if (this.nullSafe) { - return ValueRef.NullValueRef.instance; + return ValueRef.NullValueRef.INSTANCE; } - throw new SpelEvaluationException(getStartPosition(), - SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, "null"); + throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, "null"); } - throw new SpelEvaluationException(getStartPosition(), - SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, operand.getClass().getName()); + throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, + operand.getClass().getName()); } @Override public String toStringAST() { - StringBuilder sb = new StringBuilder(); - return sb.append("![").append(getChild(0).toStringAST()).append("]").toString(); + return "![" + getChild(0).toStringAST() + "]"; } private Class determineCommonType(Class oldType, Class newType) { diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java index cca9683785e..bfb3ccf2d52 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java @@ -87,10 +87,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl { return tv; } - private TypedValue getValueInternal(TypedValue contextObject, EvaluationContext eContext, + private TypedValue getValueInternal(TypedValue contextObject, EvaluationContext evalContext, boolean isAutoGrowNullReferences) throws EvaluationException { - TypedValue result = readProperty(contextObject, eContext, this.name); + TypedValue result = readProperty(contextObject, evalContext, this.name); // Dynamically create the objects if the user has requested that optional behavior if (result.getValue() == null && isAutoGrowNullReferences && @@ -101,10 +101,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl { // Create a new collection or map ready for the indexer if (resultDescriptor.getType().equals(List.class)) { try { - if (isWritableProperty(this.name, contextObject,eContext)) { + if (isWritableProperty(this.name, contextObject, evalContext)) { List newList = ArrayList.class.newInstance(); - writeProperty(contextObject, eContext, this.name, newList); - result = readProperty(contextObject, eContext, this.name); + writeProperty(contextObject, evalContext, this.name, newList); + result = readProperty(contextObject, evalContext, this.name); } } catch (InstantiationException ex) { @@ -118,10 +118,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } else { try { - if (isWritableProperty(this.name,contextObject,eContext)) { + if (isWritableProperty(this.name,contextObject, evalContext)) { Map newMap = HashMap.class.newInstance(); - writeProperty(contextObject, eContext, this.name, newMap); - result = readProperty(contextObject, eContext, this.name); + writeProperty(contextObject, evalContext, this.name, newMap); + result = readProperty(contextObject, evalContext, this.name); } } catch (InstantiationException ex) { @@ -137,10 +137,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl { else { // 'simple' object try { - if (isWritableProperty(this.name,contextObject,eContext)) { + if (isWritableProperty(this.name,contextObject, evalContext)) { Object newObject = result.getTypeDescriptor().getType().newInstance(); - writeProperty(contextObject, eContext, this.name, newObject); - result = readProperty(contextObject, eContext, this.name); + writeProperty(contextObject, evalContext, this.name, newObject); + result = readProperty(contextObject, evalContext, this.name); } } catch (InstantiationException ex) { @@ -176,7 +176,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl { * @return the value of the property * @throws EvaluationException if any problem accessing the property or it cannot be found */ - private TypedValue readProperty(TypedValue contextObject, EvaluationContext eContext, String name) + private TypedValue readProperty(TypedValue contextObject, EvaluationContext evalContext, String name) throws EvaluationException { Object targetObject = contextObject.getValue(); @@ -187,9 +187,9 @@ public class PropertyOrFieldReference extends SpelNodeImpl { PropertyAccessor accessorToUse = this.cachedReadAccessor; if (accessorToUse != null) { try { - return accessorToUse.read(eContext, contextObject.getValue(), name); + return accessorToUse.read(evalContext, contextObject.getValue(), name); } - catch (AccessException ae) { + catch (AccessException ex) { // this is OK - it may have gone stale due to a class change, // let's try to get a new one and call it before giving up this.cachedReadAccessor = null; @@ -197,25 +197,25 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } List accessorsToTry = - getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors()); + getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors()); // Go through the accessors that may be able to resolve it. If they are a cacheable accessor then // get the accessor and use it. If they are not cacheable but report they can read the property // then ask them to read it if (accessorsToTry != null) { try { for (PropertyAccessor accessor : accessorsToTry) { - if (accessor.canRead(eContext, contextObject.getValue(), name)) { + if (accessor.canRead(evalContext, contextObject.getValue(), name)) { if (accessor instanceof ReflectivePropertyAccessor) { accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor( - eContext, contextObject.getValue(), name); + evalContext, contextObject.getValue(), name); } this.cachedReadAccessor = accessor; - return accessor.read(eContext, contextObject.getValue(), name); + return accessor.read(evalContext, contextObject.getValue(), name); } } } - catch (AccessException ae) { - throw new SpelEvaluationException(ae, SpelMessage.EXCEPTION_DURING_PROPERTY_READ, name, ae.getMessage()); + catch (AccessException ex) { + throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_DURING_PROPERTY_READ, name, ex.getMessage()); } } if (contextObject.getValue() == null) { @@ -227,7 +227,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } } - private void writeProperty(TypedValue contextObject, EvaluationContext eContext, String name, Object newValue) + private void writeProperty(TypedValue contextObject, EvaluationContext evalContext, String name, Object newValue) throws EvaluationException { if (contextObject.getValue() == null && this.nullSafe) { @@ -237,10 +237,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl { PropertyAccessor accessorToUse = this.cachedWriteAccessor; if (accessorToUse != null) { try { - accessorToUse.write(eContext, contextObject.getValue(), name, newValue); + accessorToUse.write(evalContext, contextObject.getValue(), name, newValue); return; } - catch (AccessException ae) { + catch (AccessException ex) { // this is OK - it may have gone stale due to a class change, // let's try to get a new one and call it before giving up this.cachedWriteAccessor = null; @@ -248,23 +248,23 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } List accessorsToTry = - getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors()); + getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors()); if (accessorsToTry != null) { try { for (PropertyAccessor accessor : accessorsToTry) { - if (accessor.canWrite(eContext, contextObject.getValue(), name)) { + if (accessor.canWrite(evalContext, contextObject.getValue(), name)) { this.cachedWriteAccessor = accessor; - accessor.write(eContext, contextObject.getValue(), name, newValue); + accessor.write(evalContext, contextObject.getValue(), name, newValue); return; } } } - catch (AccessException ae) { - throw new SpelEvaluationException(getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE, - name, ae.getMessage()); + catch (AccessException ex) { + throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE, + name, ex.getMessage()); } } - if (contextObject.getValue()==null) { + if (contextObject.getValue() == null) { throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL, name); } else { @@ -273,19 +273,19 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } } - public boolean isWritableProperty(String name, TypedValue contextObject, EvaluationContext eContext) + public boolean isWritableProperty(String name, TypedValue contextObject, EvaluationContext evalContext) throws EvaluationException { List accessorsToTry = - getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors()); + getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors()); if (accessorsToTry != null) { for (PropertyAccessor accessor : accessorsToTry) { try { - if (accessor.canWrite(eContext, contextObject.getValue(), name)) { + if (accessor.canWrite(evalContext, contextObject.getValue(), name)) { return true; } } - catch (AccessException ae) { + catch (AccessException ex) { // let others try } } @@ -343,9 +343,9 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - ((CompilablePropertyAccessor) this.cachedReadAccessor).generateCode(this.name, mv, codeflow); - codeflow.pushDescriptor(this.exitTypeDescriptor); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + ((CompilablePropertyAccessor) this.cachedReadAccessor).generateCode(this.name, mv, cf); + cf.pushDescriptor(this.exitTypeDescriptor); } @@ -355,21 +355,21 @@ public class PropertyOrFieldReference extends SpelNodeImpl { private final TypedValue contextObject; - private final EvaluationContext eContext; + private final EvaluationContext evalContext; private final boolean autoGrowNullReferences; public AccessorLValue(PropertyOrFieldReference propertyOrFieldReference, TypedValue activeContextObject, - EvaluationContext evaluationContext, boolean autoGrowNullReferences) { + EvaluationContext evalContext, boolean autoGrowNullReferences) { this.ref = propertyOrFieldReference; this.contextObject = activeContextObject; - this.eContext = evaluationContext; + this.evalContext = evalContext; this.autoGrowNullReferences = autoGrowNullReferences; } @Override public TypedValue getValue() { - TypedValue value = this.ref.getValueInternal(this.contextObject, this.eContext, this.autoGrowNullReferences); + TypedValue value = this.ref.getValueInternal(this.contextObject, this.evalContext, this.autoGrowNullReferences); if (this.ref.cachedReadAccessor instanceof CompilablePropertyAccessor) { CompilablePropertyAccessor accessor = (CompilablePropertyAccessor) this.ref.cachedReadAccessor; this.ref.exitTypeDescriptor = CodeFlow.toDescriptor(accessor.getPropertyType()); @@ -379,7 +379,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl { @Override public void setValue(Object newValue) { - this.ref.writeProperty(this.contextObject, this.eContext, this.ref.name, newValue); + this.ref.writeProperty(this.contextObject, this.evalContext, this.ref.name, newValue); } @Override diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java index 99b4b13f53d..be872e7fbd9 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,8 +35,8 @@ public class QualifiedIdentifier extends SpelNodeImpl { private TypedValue value; - public QualifiedIdentifier(int pos,SpelNodeImpl... operands) { - super(pos,operands); + public QualifiedIdentifier(int pos, SpelNodeImpl... operands) { + super(pos, operands); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java index 273d47697d7..4e25289c36e 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java @@ -49,9 +49,9 @@ public class RealLiteral extends Literal { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { mv.visitLdcInsn(this.value.getValue()); - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java index dfb838042f0..944b9486b89 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,13 +59,13 @@ public class Selection extends SpelNodeImpl { public Selection(boolean nullSafe, int variant, int pos, SpelNodeImpl expression) { - super(pos, expression != null ? new SpelNodeImpl[] { expression } - : new SpelNodeImpl[] {}); + super(pos, expression); Assert.notNull(expression, "Expression must not be null"); this.nullSafe = nullSafe; this.variant = variant; } + @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { return getValueRef(state).getValue(); @@ -84,30 +84,30 @@ public class Selection extends SpelNodeImpl { Object lastKey = null; for (Map.Entry entry : mapdata.entrySet()) { try { - TypedValue kvpair = new TypedValue(entry); - state.pushActiveContextObject(kvpair); - Object o = selectionCriteria.getValueInternal(state).getValue(); - if (o instanceof Boolean) { - if (((Boolean) o).booleanValue() == true) { + TypedValue kvPair = new TypedValue(entry); + state.pushActiveContextObject(kvPair); + Object val = selectionCriteria.getValueInternal(state).getValue(); + if (val instanceof Boolean) { + if ((Boolean) val) { if (this.variant == FIRST) { - result.put(entry.getKey(),entry.getValue()); - return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this); + result.put(entry.getKey(), entry.getValue()); + return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this); } - result.put(entry.getKey(),entry.getValue()); + result.put(entry.getKey(), entry.getValue()); lastKey = entry.getKey(); } } else { throw new SpelEvaluationException(selectionCriteria.getStartPosition(), - SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST()); + SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); } } finally { state.popActiveContextObject(); } } - if ((this.variant == FIRST || this.variant == LAST) && result.size() == 0) { - return new ValueRef.TypedValueHolderValueRef(new TypedValue(null),this); + if ((this.variant == FIRST || this.variant == LAST) && result.isEmpty()) { + return new ValueRef.TypedValueHolderValueRef(new TypedValue(null), this); } if (this.variant == LAST) { @@ -122,29 +122,29 @@ public class Selection extends SpelNodeImpl { if ((operand instanceof Collection) || ObjectUtils.isArray(operand)) { List data = new ArrayList(); - Collection c = (operand instanceof Collection) ? - (Collection) operand : Arrays.asList(ObjectUtils.toObjectArray(operand)); - data.addAll(c); + Collection coll = (operand instanceof Collection ? + (Collection) operand : Arrays.asList(ObjectUtils.toObjectArray(operand))); + data.addAll(coll); List result = new ArrayList(); - int idx = 0; + int index = 0; for (Object element : data) { try { state.pushActiveContextObject(new TypedValue(element)); - state.enterScope("index", idx); - Object o = selectionCriteria.getValueInternal(state).getValue(); - if (o instanceof Boolean) { - if (((Boolean) o).booleanValue() == true) { + state.enterScope("index", index); + Object val = selectionCriteria.getValueInternal(state).getValue(); + if (val instanceof Boolean) { + if ((Boolean) val) { if (this.variant == FIRST) { - return new ValueRef.TypedValueHolderValueRef(new TypedValue(element),this); + return new ValueRef.TypedValueHolderValueRef(new TypedValue(element), this); } result.add(element); } } else { throw new SpelEvaluationException(selectionCriteria.getStartPosition(), - SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST()); + SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); } - idx++; + index++; } finally { state.exitScope(); @@ -153,7 +153,7 @@ public class Selection extends SpelNodeImpl { } if ((this.variant == FIRST || this.variant == LAST) && result.size() == 0) { - return ValueRef.NullValueRef.instance; + return ValueRef.NullValueRef.INSTANCE; } if (this.variant == LAST) { @@ -163,20 +163,20 @@ public class Selection extends SpelNodeImpl { if (operand instanceof Collection) { return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this); } - Class elementType = ClassUtils.resolvePrimitiveIfNecessary(op.getTypeDescriptor().getElementTypeDescriptor().getType()); + Class elementType = ClassUtils.resolvePrimitiveIfNecessary( + op.getTypeDescriptor().getElementTypeDescriptor().getType()); Object resultArray = Array.newInstance(elementType, result.size()); System.arraycopy(result.toArray(), 0, resultArray, 0, result.size()); return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultArray),this); } - if (operand==null) { + if (operand == null) { if (this.nullSafe) { - return ValueRef.NullValueRef.instance; + return ValueRef.NullValueRef.INSTANCE; } - throw new SpelEvaluationException(getStartPosition(), - SpelMessage.INVALID_TYPE_FOR_SELECTION, "null"); + throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION, "null"); } - throw new SpelEvaluationException(getStartPosition(), - SpelMessage.INVALID_TYPE_FOR_SELECTION, operand.getClass().getName()); + throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION, + operand.getClass().getName()); } @Override diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java index 9c46c8f6068..d584a50fa67 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java @@ -28,10 +28,11 @@ import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelNode; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** - * The common supertype of all AST nodes in a parsed Spring Expression Language format - * expression. + * The common supertype of all AST nodes in a parsed Spring Expression Language + * format expression. * * @author Andy Clement * @since 3.0 @@ -62,11 +63,11 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes { public SpelNodeImpl(int pos, SpelNodeImpl... operands) { this.pos = pos; // pos combines start and end so can never be zero because tokens cannot be zero length - Assert.isTrue(pos != 0); - if (operands != null && operands.length > 0) { + Assert.isTrue(pos != 0, "Pos must not be 0"); + if (!ObjectUtils.isEmpty(operands)) { this.children = operands; - for (SpelNodeImpl childnode : operands) { - childnode.parent = this; + for (SpelNodeImpl childNode : operands) { + childNode.parent = this; } } } @@ -195,10 +196,10 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes { * example it will include information about the type of the object currently * on the stack. * @param mv the ASM MethodVisitor into which code should be generated - * @param codeflow a context object with info about what is on the stack + * @param cf a context object with info about what is on the stack */ - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - throw new IllegalStateException(this.getClass().getName()+" has no generateCode(..) method"); + public void generateCode(MethodVisitor mv, CodeFlow cf) { + throw new IllegalStateException(getClass().getName() +" has no generateCode(..) method"); } public String getExitDescriptor() { diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java index 6a1aabd9289..930bc4738de 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,9 +56,9 @@ public class StringLiteral extends Literal { } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { mv.visitLdcInsn(this.value.getValue()); - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java index ae0171a3255..6855d59c4a4 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java @@ -34,17 +34,17 @@ import org.springframework.expression.spel.SpelMessage; */ public class Ternary extends SpelNodeImpl { - public Ternary(int pos, SpelNodeImpl... args) { super(pos,args); } + /** * Evaluate the condition and if true evaluate the first alternative, otherwise * evaluate the second alternative. * @param state the expression state - * @throws EvaluationException if the condition does not evaluate correctly to a - * boolean or there is a problem executing the chosen alternative + * @throws EvaluationException if the condition does not evaluate correctly to + * a boolean or there is a problem executing the chosen alternative */ @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { @@ -53,25 +53,19 @@ public class Ternary extends SpelNodeImpl { throw new SpelEvaluationException(getChild(0).getStartPosition(), SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean"); } - TypedValue result = null; - if (value.booleanValue()) { - result = this.children[1].getValueInternal(state); - } - else { - result = this.children[2].getValueInternal(state); - } + TypedValue result = this.children[value ? 1 : 2].getValueInternal(state); computeExitTypeDescriptor(); return result; } @Override public String toStringAST() { - return new StringBuilder().append(getChild(0).toStringAST()).append(" ? ").append(getChild(1).toStringAST()) - .append(" : ").append(getChild(2).toStringAST()).toString(); + return getChild(0).toStringAST() + " ? " + getChild(1).toStringAST() + " : " + getChild(2).toStringAST(); } private void computeExitTypeDescriptor() { - if (exitTypeDescriptor == null && this.children[1].getExitDescriptor()!=null && this.children[2].getExitDescriptor()!=null) { + if (this.exitTypeDescriptor == null && this.children[1].exitTypeDescriptor != null && + this.children[2].exitTypeDescriptor != null) { String leftDescriptor = this.children[1].exitTypeDescriptor; String rightDescriptor = this.children[2].exitTypeDescriptor; if (leftDescriptor.equals(rightDescriptor)) { @@ -95,43 +89,40 @@ public class Ternary extends SpelNodeImpl { SpelNodeImpl condition = this.children[0]; SpelNodeImpl left = this.children[1]; SpelNodeImpl right = this.children[2]; - if (!(condition.isCompilable() && left.isCompilable() && right.isCompilable())) { - return false; - } - return CodeFlow.isBooleanCompatible(condition.exitTypeDescriptor) && - left.getExitDescriptor()!=null && - right.getExitDescriptor()!=null; + return (condition.isCompilable() && left.isCompilable() && right.isCompilable() && + CodeFlow.isBooleanCompatible(condition.exitTypeDescriptor) && + left.exitTypeDescriptor != null && right.exitTypeDescriptor != null); } @Override - public void generateCode(MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(MethodVisitor mv, CodeFlow cf) { // May reach here without it computed if all elements are literals computeExitTypeDescriptor(); - codeflow.enterCompilationScope(); - this.children[0].generateCode(mv, codeflow); - if (!CodeFlow.isPrimitive(codeflow.lastDescriptor())) { - CodeFlow.insertUnboxInsns(mv, 'Z', codeflow.lastDescriptor()); + cf.enterCompilationScope(); + this.children[0].generateCode(mv, cf); + if (!CodeFlow.isPrimitive(cf.lastDescriptor())) { + CodeFlow.insertUnboxInsns(mv, 'Z', cf.lastDescriptor()); } - codeflow.exitCompilationScope(); + cf.exitCompilationScope(); Label elseTarget = new Label(); Label endOfIf = new Label(); mv.visitJumpInsn(IFEQ, elseTarget); - codeflow.enterCompilationScope(); - this.children[1].generateCode(mv, codeflow); - if (!CodeFlow.isPrimitive(getExitDescriptor())) { - CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0)); + cf.enterCompilationScope(); + this.children[1].generateCode(mv, cf); + if (!CodeFlow.isPrimitive(this.exitTypeDescriptor)) { + CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0)); } - codeflow.exitCompilationScope(); + cf.exitCompilationScope(); mv.visitJumpInsn(GOTO, endOfIf); mv.visitLabel(elseTarget); - codeflow.enterCompilationScope(); - this.children[2].generateCode(mv, codeflow); - if (!CodeFlow.isPrimitive(getExitDescriptor())) { - CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0)); + cf.enterCompilationScope(); + this.children[2].generateCode(mv, cf); + if (!CodeFlow.isPrimitive(this.exitTypeDescriptor)) { + CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0)); } - codeflow.exitCompilationScope(); + cf.exitCompilationScope(); mv.visitLabel(endOfIf); - codeflow.pushDescriptor(getExitDescriptor()); + cf.pushDescriptor(this.exitTypeDescriptor); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeCode.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeCode.java index 6a477af880c..fe785b1a39b 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeCode.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeCode.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; /** @@ -66,11 +67,10 @@ public enum TypeCode { return TypeCode.OBJECT; } - public static TypeCode forClass(Class c) { + public static TypeCode forClass(Class clazz) { TypeCode[] allValues = TypeCode.values(); - for (int i = 0; i < allValues.length; i++) { - TypeCode typeCode = allValues[i]; - if (c == typeCode.getType()) { + for (TypeCode typeCode : allValues) { + if (clazz == typeCode.getType()) { return typeCode; } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java index 11c475b460d..11bf870bad5 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java @@ -50,19 +50,18 @@ public class TypeReference extends SpelNodeImpl { @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { // TODO possible optimization here if we cache the discovered type reference, but can we do that? - String typename = (String) this.children[0].getValueInternal(state).getValue(); - if (typename.indexOf('.') == -1 && Character.isLowerCase(typename.charAt(0))) { - TypeCode tc = TypeCode.valueOf(typename.toUpperCase()); + String typeName = (String) this.children[0].getValueInternal(state).getValue(); + if (!typeName.contains(".") && Character.isLowerCase(typeName.charAt(0))) { + TypeCode tc = TypeCode.valueOf(typeName.toUpperCase()); if (tc != TypeCode.OBJECT) { - // it is a primitive type - Class clazz = tc.getType(); - clazz = makeArrayIfNecessary(clazz); + // It is a primitive type + Class clazz = makeArrayIfNecessary(tc.getType()); this.exitTypeDescriptor = "Ljava/lang/Class"; this.type = clazz; return new TypedValue(clazz); } } - Class clazz = state.findType(typename); + Class clazz = state.findType(typeName); clazz = makeArrayIfNecessary(clazz); this.exitTypeDescriptor = "Ljava/lang/Class"; this.type = clazz; @@ -81,10 +80,9 @@ public class TypeReference extends SpelNodeImpl { @Override public String toStringAST() { - StringBuilder sb = new StringBuilder(); - sb.append("T("); + StringBuilder sb = new StringBuilder("T("); sb.append(getChild(0).toStringAST()); - for (int d=0;d constructedNodes = new Stack(); + // The expression being parsed private String expressionString; @@ -99,11 +104,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { // Current location in the token stream when processing tokens private int tokenStreamPointer; - // For rules that build nodes, they are stacked here for return - private final Stack constructedNodes = new Stack(); - - private final SpelParserConfiguration configuration; - /** * Create a parser with some configured behavior. @@ -126,7 +126,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { this.constructedNodes.clear(); SpelNodeImpl ast = eatExpression(); if (moreTokens()) { - throw new SpelParseException(peekToken().startpos, SpelMessage.MORE_INPUT, toString(nextToken())); + throw new SpelParseException(peekToken().startPos, SpelMessage.MORE_INPUT, toString(nextToken())); } Assert.isTrue(this.constructedNodes.isEmpty()); return new SpelExpression(expressionString, ast, this.configuration); @@ -146,36 +146,36 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { SpelNodeImpl expr = eatLogicalOrExpression(); if (moreTokens()) { Token t = peekToken(); - if (t.kind==TokenKind.ASSIGN) { // a=b - if (expr==null) { - expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1)); + if (t.kind == TokenKind.ASSIGN) { // a=b + if (expr == null) { + expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 1)); } nextToken(); SpelNodeImpl assignedValue = eatLogicalOrExpression(); - return new Assign(toPos(t),expr,assignedValue); + return new Assign(toPos(t), expr, assignedValue); } - if (t.kind==TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b) - if (expr==null) { - expr = new NullLiteral(toPos(t.startpos-1,t.endpos-2)); + if (t.kind == TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b) + if (expr == null) { + expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 2)); } - nextToken(); // elvis has left the building + nextToken(); // elvis has left the building SpelNodeImpl valueIfNull = eatExpression(); if (valueIfNull==null) { - valueIfNull = new NullLiteral(toPos(t.startpos+1,t.endpos+1)); + valueIfNull = new NullLiteral(toPos(t.startPos + 1, t.endPos + 1)); } - return new Elvis(toPos(t),expr,valueIfNull); + return new Elvis(toPos(t), expr, valueIfNull); } - if (t.kind==TokenKind.QMARK) { // a?b:c - if (expr==null) { - expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1)); + if (t.kind == TokenKind.QMARK) { // a?b:c + if (expr == null) { + expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 1)); } nextToken(); SpelNodeImpl ifTrueExprValue = eatExpression(); eatToken(TokenKind.COLON); SpelNodeImpl ifFalseExprValue = eatExpression(); - return new Ternary(toPos(t),expr,ifTrueExprValue,ifFalseExprValue); + return new Ternary(toPos(t), expr, ifTrueExprValue, ifFalseExprValue); } } return expr; @@ -185,10 +185,10 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { private SpelNodeImpl eatLogicalOrExpression() { SpelNodeImpl expr = eatLogicalAndExpression(); while (peekIdentifierToken("or") || peekToken(TokenKind.SYMBOLIC_OR)) { - Token t = nextToken(); //consume OR + Token t = nextToken(); //consume OR SpelNodeImpl rhExpr = eatLogicalAndExpression(); - checkOperands(t,expr,rhExpr); - expr = new OpOr(toPos(t),expr,rhExpr); + checkOperands(t, expr, rhExpr); + expr = new OpOr(toPos(t), expr, rhExpr); } return expr; } @@ -197,10 +197,10 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { private SpelNodeImpl eatLogicalAndExpression() { SpelNodeImpl expr = eatRelationalExpression(); while (peekIdentifierToken("and") || peekToken(TokenKind.SYMBOLIC_AND)) { - Token t = nextToken();// consume 'AND' + Token t = nextToken(); // consume 'AND' SpelNodeImpl rhExpr = eatRelationalExpression(); - checkOperands(t,expr,rhExpr); - expr = new OpAnd(toPos(t),expr,rhExpr); + checkOperands(t, expr, rhExpr); + expr = new OpAnd(toPos(t), expr, rhExpr); } return expr; } @@ -210,9 +210,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { SpelNodeImpl expr = eatSumExpression(); Token relationalOperatorToken = maybeEatRelationalOperator(); if (relationalOperatorToken != null) { - Token t = nextToken(); //consume relational operator token + Token t = nextToken(); // consume relational operator token SpelNodeImpl rhExpr = eatSumExpression(); - checkOperands(t,expr,rhExpr); + checkOperands(t, expr, rhExpr); TokenKind tk = relationalOperatorToken.kind; if (relationalOperatorToken.isNumericRelationalOperator()) { @@ -271,9 +271,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { private SpelNodeImpl eatProductExpression() { SpelNodeImpl expr = eatPowerIncDecExpression(); while (peekToken(TokenKind.STAR, TokenKind.DIV, TokenKind.MOD)) { - Token t = nextToken(); // consume STAR/DIV/MOD + Token t = nextToken(); // consume STAR/DIV/MOD SpelNodeImpl rhExpr = eatPowerIncDecExpression(); - checkOperands(t,expr,rhExpr); + checkOperands(t, expr, rhExpr); if (t.kind == TokenKind.STAR) { expr = new OpMultiply(toPos(t), expr, rhExpr); } @@ -292,18 +292,18 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { private SpelNodeImpl eatPowerIncDecExpression() { SpelNodeImpl expr = eatUnaryExpression(); if (peekToken(TokenKind.POWER)) { - Token t = nextToken();//consume POWER + Token t = nextToken(); //consume POWER SpelNodeImpl rhExpr = eatUnaryExpression(); checkRightOperand(t,rhExpr); - return new OperatorPower(toPos(t),expr, rhExpr); + return new OperatorPower(toPos(t), expr, rhExpr); } - if (expr!=null && peekToken(TokenKind.INC,TokenKind.DEC)) { - Token t = nextToken();//consume INC/DEC - if (t.getKind()==TokenKind.INC) { - return new OpInc(toPos(t),true,expr); + if (expr != null && peekToken(TokenKind.INC,TokenKind.DEC)) { + Token t = nextToken(); //consume INC/DEC + if (t.getKind() == TokenKind.INC) { + return new OpInc(toPos(t), true, expr); } - return new OpDec(toPos(t),true,expr); + return new OpDec(toPos(t), true, expr); } return expr; @@ -340,7 +340,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { // primaryExpression : startNode (node)? -> ^(EXPRESSION startNode (node)?); private SpelNodeImpl eatPrimaryExpression() { List nodes = new ArrayList(); - SpelNodeImpl start = eatStartNode(); // always a start node + SpelNodeImpl start = eatStartNode(); // always a start node nodes.add(start); while (maybeEatNode()) { nodes.add(pop()); @@ -401,10 +401,10 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { } if (peekToken() == null) { // unexpectedly ran out of data - raiseInternalException(t.startpos, SpelMessage.OOD); + raiseInternalException(t.startPos, SpelMessage.OOD); } else { - raiseInternalException(t.startpos, SpelMessage.UNEXPECTED_DATA_AFTER_DOT, + raiseInternalException(t.startPos, SpelMessage.UNEXPECTED_DATA_AFTER_DOT, toString(peekToken())); } return null; @@ -424,13 +424,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { Token functionOrVariableName = eatToken(TokenKind.IDENTIFIER); SpelNodeImpl[] args = maybeEatMethodArgs(); if (args == null) { - push(new VariableReference(functionOrVariableName.data, toPos(t.startpos, - functionOrVariableName.endpos))); + push(new VariableReference(functionOrVariableName.data, toPos(t.startPos, + functionOrVariableName.endPos))); return true; } - push(new FunctionReference(functionOrVariableName.data, toPos(t.startpos, - functionOrVariableName.endpos), args)); + push(new FunctionReference(functionOrVariableName.data, toPos(t.startPos, + functionOrVariableName.endPos), args)); return true; } @@ -457,10 +457,10 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { * Used for consuming arguments for either a method or a constructor call */ private void consumeArguments(List accumulatedArguments) { - int pos = peekToken().startpos; - Token next = null; + int pos = peekToken().startPos; + Token next; do { - nextToken();// consume ( (first time through) or comma (subsequent times) + nextToken(); // consume ( (first time through) or comma (subsequent times) Token t = peekToken(); if (t == null) { raiseInternalException(pos, SpelMessage.RUN_OUT_OF_ARGUMENTS); @@ -483,7 +483,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { // not found at the end of the expression return this.expressionString.length(); } - return t.startpos; + return t.startPos; } //startNode @@ -504,16 +504,14 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { else if (maybeEatParenExpression()) { return pop(); } - else if (maybeEatTypeReference() || maybeEatNullReference() - || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) - || maybeEatFunctionOrVar()) { + else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || + maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) { return pop(); } else if (maybeEatBeanReference()) { return pop(); } - else if (maybeEatProjection(false) || maybeEatSelection(false) - || maybeEatIndexer()) { + else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) { return pop(); } else if (maybeEatInlineListOrMap()) { @@ -530,22 +528,22 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { if (peekToken(TokenKind.BEAN_REF)) { Token beanRefToken = nextToken(); Token beanNameToken = null; - String beanname = null; + String beanName = null; if (peekToken(TokenKind.IDENTIFIER)) { beanNameToken = eatToken(TokenKind.IDENTIFIER); - beanname = beanNameToken.data; + beanName = beanNameToken.data; } else if (peekToken(TokenKind.LITERAL_STRING)) { beanNameToken = eatToken(TokenKind.LITERAL_STRING); - beanname = beanNameToken.stringValue(); - beanname = beanname.substring(1, beanname.length() - 1); + beanName = beanNameToken.stringValue(); + beanName = beanName.substring(1, beanName.length() - 1); } else { - raiseInternalException(beanRefToken.startpos, + raiseInternalException(beanRefToken.startPos, SpelMessage.INVALID_BEAN_REFERENCE); } - BeanReference beanReference = new BeanReference(toPos(beanNameToken),beanname); + BeanReference beanReference = new BeanReference(toPos(beanNameToken) ,beanName); this.constructedNodes.push(beanReference); return true; } @@ -564,7 +562,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { // dotted qualified id // Are there array dimensions? int dims = 0; - while (peekToken(TokenKind.LSQUARE,true)) { + while (peekToken(TokenKind.LSQUARE, true)) { eatToken(TokenKind.RSQUARE); dims++; } @@ -611,12 +609,12 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { Token closingCurly = peekToken(); if (peekToken(TokenKind.RCURLY, true)) { // empty list '{}' - expr = new InlineList(toPos(t.startpos,closingCurly.endpos)); + expr = new InlineList(toPos(t.startPos,closingCurly.endPos)); } else if (peekToken(TokenKind.COLON,true)) { closingCurly = eatToken(TokenKind.RCURLY); // empty map '{:}' - expr = new InlineMap(toPos(t.startpos,closingCurly.endpos)); + expr = new InlineMap(toPos(t.startPos,closingCurly.endPos)); } else { SpelNodeImpl firstExpression = eatExpression(); @@ -629,7 +627,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { List listElements = new ArrayList(); listElements.add(firstExpression); closingCurly = eatToken(TokenKind.RCURLY); - expr = new InlineList(toPos(t.startpos,closingCurly.endpos),listElements.toArray(new SpelNodeImpl[listElements.size()])); + expr = new InlineList(toPos(t.startPos,closingCurly.endPos),listElements.toArray(new SpelNodeImpl[listElements.size()])); } else if (peekToken(TokenKind.COMMA, true)) { // multi item list List listElements = new ArrayList(); @@ -639,7 +637,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { } while (peekToken(TokenKind.COMMA,true)); closingCurly = eatToken(TokenKind.RCURLY); - expr = new InlineList(toPos(t.startpos,closingCurly.endpos),listElements.toArray(new SpelNodeImpl[listElements.size()])); + expr = new InlineList(toPos(t.startPos,closingCurly.endPos),listElements.toArray(new SpelNodeImpl[listElements.size()])); } else if (peekToken(TokenKind.COLON, true)) { // map! @@ -652,10 +650,10 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { mapElements.add(eatExpression()); } closingCurly = eatToken(TokenKind.RCURLY); - expr = new InlineMap(toPos(t.startpos,closingCurly.endpos),mapElements.toArray(new SpelNodeImpl[mapElements.size()])); + expr = new InlineMap(toPos(t.startPos,closingCurly.endPos),mapElements.toArray(new SpelNodeImpl[mapElements.size()])); } else { - raiseInternalException(t.startpos, SpelMessage.OOD); + raiseInternalException(t.startPos, SpelMessage.OOD); } } this.constructedNodes.push(expr); @@ -669,7 +667,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { } SpelNodeImpl expr = eatExpression(); eatToken(TokenKind.RSQUARE); - this.constructedNodes.push(new Indexer(toPos(t),expr)); + this.constructedNodes.push(new Indexer(toPos(t), expr)); return true; } @@ -714,7 +712,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { if (node == null) { raiseInternalException( this.expressionString.length(), SpelMessage.OOD); } - raiseInternalException(node.startpos, SpelMessage.NOT_EXPECTED_TOKEN, + raiseInternalException(node.startPos, SpelMessage.NOT_EXPECTED_TOKEN, "qualified ID", node.getKind().toString().toLowerCase()); } int pos = toPos(qualifiedIdPieces.getFirst().getStartPosition(), qualifiedIdPieces.getLast().getEndPosition()); @@ -729,7 +727,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { return true; } String value = node.stringValue(); - return StringUtils.hasLength(value) && VALID_QUALIFIED_ID_PATTERN.matcher(value).matches(); + return (StringUtils.hasLength(value) && VALID_QUALIFIED_ID_PATTERN.matcher(value).matches()); } // This is complicated due to the support for dollars in identifiers. Dollars are normally separate tokens but @@ -744,7 +742,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { return true; } // methodreference - push(new MethodReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName),args)); + push(new MethodReference(nullSafeNavigation, methodOrPropertyName.data, toPos(methodOrPropertyName), args)); // TODO what is the end position for a method reference? the name or the last arg? return true; } @@ -807,7 +805,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { // | NULL_LITERAL private boolean maybeEatLiteral() { Token t = peekToken(); - if (t==null) { + if (t == null) { return false; } if (t.kind == TokenKind.LITERAL_INT) { @@ -890,7 +888,8 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { raiseInternalException( this.expressionString.length(), SpelMessage.OOD); } if (t.kind != expectedKind) { - raiseInternalException(t.startpos,SpelMessage.NOT_EXPECTED_TOKEN, expectedKind.toString().toLowerCase(),t.getKind().toString().toLowerCase()); + raiseInternalException(t.startPos, SpelMessage.NOT_EXPECTED_TOKEN, + expectedKind.toString().toLowerCase(), t.getKind().toString().toLowerCase()); } return t; } @@ -943,7 +942,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { return false; } Token t = peekToken(); - return t.kind==TokenKind.IDENTIFIER && t.stringValue().equalsIgnoreCase(identifierString); + return t.kind == TokenKind.IDENTIFIER && t.stringValue().equalsIgnoreCase(identifierString); } private boolean peekSelectToken() { @@ -974,8 +973,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { } private void raiseInternalException(int pos, SpelMessage message, Object... inserts) { - throw new InternalParseException(new SpelParseException(this.expressionString, - pos, message, inserts)); + throw new InternalParseException(new SpelParseException(this.expressionString, pos, message, inserts)); } public String toString(Token t) { @@ -992,13 +990,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { private void checkLeftOperand(Token token, SpelNodeImpl operandExpression) { if (operandExpression==null) { - raiseInternalException(token.startpos,SpelMessage.LEFT_OPERAND_PROBLEM); + raiseInternalException(token.startPos, SpelMessage.LEFT_OPERAND_PROBLEM); } } private void checkRightOperand(Token token, SpelNodeImpl operandExpression) { - if (operandExpression==null) { - raiseInternalException(token.startpos,SpelMessage.RIGHT_OPERAND_PROBLEM); + if (operandExpression == null) { + raiseInternalException(token.startPos, SpelMessage.RIGHT_OPERAND_PROBLEM); } } @@ -1006,7 +1004,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { * Compress the start and end of a token into a single int. */ private int toPos(Token t) { - return (t.startpos<<16) + t.endpos; + return (t.startPos<<16) + t.endPos; } private int toPos(int start,int end) { diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelCompiler.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelCompiler.java index 9b6630fd54b..ac400689fbc 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelCompiler.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelCompiler.java @@ -141,21 +141,23 @@ public class SpelCompiler implements Opcodes { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "org/springframework/expression/spel/CompiledExpression", "", "()V",false); + mv.visitMethodInsn(INVOKESPECIAL, "org/springframework/expression/spel/CompiledExpression", + "", "()V", false); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); // Create getValue() method - mv = cw.visitMethod(ACC_PUBLIC, "getValue", "(Ljava/lang/Object;Lorg/springframework/expression/EvaluationContext;)Ljava/lang/Object;", null, - new String[]{"org/springframework/expression/EvaluationException"}); + mv = cw.visitMethod(ACC_PUBLIC, "getValue", + "(Ljava/lang/Object;Lorg/springframework/expression/EvaluationContext;)Ljava/lang/Object;", null, + new String[ ]{"org/springframework/expression/EvaluationException"}); mv.visitCode(); - CodeFlow codeflow = new CodeFlow(); + CodeFlow cf = new CodeFlow(); // Ask the expression AST to generate the body of the method try { - expressionToCompile.generateCode(mv, codeflow); + expressionToCompile.generateCode(mv, cf); } catch (IllegalStateException ex) { if (logger.isDebugEnabled()) { @@ -165,19 +167,19 @@ public class SpelCompiler implements Opcodes { return null; } - CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor()); - if ("V".equals(codeflow.lastDescriptor())) { + CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor()); + if ("V".equals(cf.lastDescriptor())) { mv.visitInsn(ACONST_NULL); } mv.visitInsn(ARETURN); - mv.visitMaxs(0,0); // not supplied due to COMPUTE_MAXS + mv.visitMaxs(0, 0); // not supplied due to COMPUTE_MAXS mv.visitEnd(); cw.visitEnd(); byte[] data = cw.toByteArray(); // TODO need to make this conditionally occur based on a debug flag // dump(expressionToCompile.toStringAST(), clazzName, data); - return (Class) ccl.defineClass(clazzName.replaceAll("/","."),data); + return (Class) this.ccl.defineClass(clazzName.replaceAll("/", "."), data); } @@ -249,7 +251,8 @@ public class SpelCompiler implements Opcodes { fos.close(); } catch (IOException ex) { - throw new IllegalStateException("Unexpected problem dumping class " + nameToUse + " into " + dumpLocation, ex); + throw new IllegalStateException( + "Unexpected problem dumping class " + nameToUse + " into " + dumpLocation, ex); } } @@ -259,7 +262,7 @@ public class SpelCompiler implements Opcodes { */ public static class ChildClassLoader extends URLClassLoader { - private static URL[] NO_URLS = new URL[0]; + private static final URL[] NO_URLS = new URL[0]; public ChildClassLoader(ClassLoader classloader) { super(NO_URLS, classloader); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Token.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Token.java index 69e4d3a6254..dd1373fa8af 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Token.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Token.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,25 +29,25 @@ class Token { String data; - int startpos; // index of first character + int startPos; // index of first character - int endpos; // index of char after the last character + int endPos; // index of char after the last character /** - * Constructor for use when there is no particular data for the token (eg. TRUE or - * '+') - * @param startpos the exact start - * @param endpos the index to the last character + * Constructor for use when there is no particular data for the token + * (e.g. TRUE or '+') + * @param startPos the exact start + * @param endPos the index to the last character */ - Token(TokenKind tokenKind, int startpos, int endpos) { + Token(TokenKind tokenKind, int startPos, int endPos) { this.kind = tokenKind; - this.startpos = startpos; - this.endpos = endpos; + this.startPos = startPos; + this.endPos = endPos; } - Token(TokenKind tokenKind, char[] tokenData, int pos, int endpos) { - this(tokenKind,pos,endpos); + Token(TokenKind tokenKind, char[] tokenData, int startPos, int endPos) { + this(tokenKind, startPos, endPos); this.data = new String(tokenData); } @@ -64,16 +64,17 @@ class Token { s.append(":").append(this.data); } s.append("]"); - s.append("(").append(this.startpos).append(",").append(this.endpos).append(")"); + s.append("(").append(this.startPos).append(",").append(this.endPos).append(")"); return s.toString(); } public boolean isIdentifier() { - return this.kind==TokenKind.IDENTIFIER; + return (this.kind == TokenKind.IDENTIFIER); } public boolean isNumericRelationalOperator() { - return this.kind==TokenKind.GT || this.kind==TokenKind.GE || this.kind==TokenKind.LT || this.kind==TokenKind.LE || this.kind==TokenKind.EQ || this.kind==TokenKind.NE; + return (this.kind == TokenKind.GT || this.kind == TokenKind.GE || this.kind == TokenKind.LT || + this.kind == TokenKind.LE || this.kind==TokenKind.EQ || this.kind==TokenKind.NE); } public String stringValue() { @@ -81,14 +82,15 @@ class Token { } public Token asInstanceOfToken() { - return new Token(TokenKind.INSTANCEOF,this.startpos,this.endpos); + return new Token(TokenKind.INSTANCEOF, this.startPos, this.endPos); } public Token asMatchesToken() { - return new Token(TokenKind.MATCHES,this.startpos,this.endpos); + return new Token(TokenKind.MATCHES, this.startPos, this.endPos); } public Token asBetweenToken() { - return new Token(TokenKind.BETWEEN,this.startpos,this.endpos); + return new Token(TokenKind.BETWEEN, this.startPos, this.endPos); } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java index c50ac090ba3..4a31941ee80 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -121,14 +121,14 @@ enum TokenKind { DEC("--"); - char[] tokenChars; + final char[] tokenChars; - private boolean hasPayload; // is there more to this token than simply the kind + final private boolean hasPayload; // is there more to this token than simply the kind private TokenKind(String tokenString) { this.tokenChars = tokenString.toCharArray(); - this.hasPayload = this.tokenChars.length==0; + this.hasPayload = (this.tokenChars.length == 0); } private TokenKind() { @@ -138,7 +138,7 @@ enum TokenKind { @Override public String toString() { - return this.name()+(this.tokenChars.length!=0?"("+new String(this.tokenChars)+")":""); + return (name() + (this.tokenChars.length !=0 ? "(" + new String(this.tokenChars) +")" : "")); } public boolean hasPayload() { @@ -148,4 +148,5 @@ enum TokenKind { public int getLength() { return this.tokenChars.length; } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java index cd2c6128b05..fb912228044 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java @@ -76,9 +76,9 @@ class Tokenizer { List tokens = new ArrayList(); - public Tokenizer(String inputdata) { - this.expressionString = inputdata; - this.toProcess = (inputdata + "\0").toCharArray(); + public Tokenizer(String inputData) { + this.expressionString = inputData; + this.toProcess = (inputData + "\0").toCharArray(); this.max = this.toProcess.length; this.pos = 0; process(); @@ -266,11 +266,9 @@ class Tokenizer { break; case '\\': throw new InternalParseException( - new SpelParseException(this.expressionString, this.pos, - SpelMessage.UNEXPECTED_ESCAPE_CHAR)); + new SpelParseException(this.expressionString, this.pos, SpelMessage.UNEXPECTED_ESCAPE_CHAR)); default: - throw new IllegalStateException("Cannot handle (" - + Integer.valueOf(ch) + ") '" + ch + "'"); + throw new IllegalStateException("Cannot handle (" + Integer.valueOf(ch) + ") '" + ch + "'"); } } } @@ -297,8 +295,8 @@ class Tokenizer { } } if (ch == 0) { - throw new InternalParseException(new SpelParseException(this.expressionString, - start, SpelMessage.NON_TERMINATING_QUOTED_STRING)); + throw new InternalParseException(new SpelParseException(this.expressionString, start, + SpelMessage.NON_TERMINATING_QUOTED_STRING)); } } this.pos++; @@ -595,4 +593,5 @@ class Tokenizer { } return (FLAGS[ch] & IS_HEXDIGIT) != 0; } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index dcfbb0491c4..ea805a331d4 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -455,7 +455,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { * This method will just return the ReflectivePropertyAccessor instance if it is unable to build * something more optimal. */ - public PropertyAccessor createOptimalAccessor(EvaluationContext eContext, Object target, String name) { + public PropertyAccessor createOptimalAccessor(EvaluationContext evalContext, Object target, String name) { // Don't be clever for arrays or null target if (target == null) { return this; @@ -484,7 +484,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { } if (invocationTarget == null || invocationTarget.member instanceof Field) { - Field field = (Field) (invocationTarget==null?null:invocationTarget.member); + Field field = (invocationTarget != null ? (Field) invocationTarget.member : null); if (field == null) { field = findField(name, type, target instanceof Class); if (field != null) { @@ -541,16 +541,13 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { return false; } CacheKey otherKey = (CacheKey) other; - boolean rtn = true; - rtn &= this.clazz.equals(otherKey.clazz); - rtn &= this.name.equals(otherKey.name); - rtn &= this.targetIsClass == otherKey.targetIsClass; - return rtn; + return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name) && + this.targetIsClass == otherKey.targetIsClass); } @Override public int hashCode() { - return this.clazz.hashCode() * 29 + this.name.hashCode(); + return (this.clazz.hashCode() * 29 + this.name.hashCode()); } @Override @@ -662,12 +659,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { @Override public boolean isCompilable() { - // If non public must continue to use reflection - if (!Modifier.isPublic(this.member.getModifiers()) || - !Modifier.isPublic(this.member.getDeclaringClass().getModifiers())) { - return false; - } - return true; + return (Modifier.isPublic(this.member.getModifiers()) && + Modifier.isPublic(this.member.getDeclaringClass().getModifiers())); } @Override @@ -681,13 +674,13 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { } @Override - public void generateCode(String propertyName, MethodVisitor mv, CodeFlow codeflow) { + public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) { boolean isStatic = Modifier.isStatic(this.member.getModifiers()); - String descriptor = codeflow.lastDescriptor(); + String descriptor = cf.lastDescriptor(); String memberDeclaringClassSlashedDescriptor = this.member.getDeclaringClass().getName().replace('.', '/'); if (!isStatic) { if (descriptor == null) { - codeflow.loadTarget(mv); + cf.loadTarget(mv); } if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) { mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor); @@ -695,7 +688,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { } if (this.member instanceof Field) { mv.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, memberDeclaringClassSlashedDescriptor, - this.member.getName(), CodeFlow.toJVMDescriptor(((Field) this.member).getType())); + this.member.getName(), CodeFlow.toJvmDescriptor(((Field) this.member).getType())); } else { mv.visitMethodInsn(isStatic ? INVOKESTATIC : INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java index 8fa89c4cafb..aa5d3119271 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java @@ -17,6 +17,7 @@ package org.springframework.expression.spel; import java.math.BigDecimal; +import java.math.BigInteger; import org.junit.Test; @@ -414,7 +415,6 @@ public class OperatorTests extends AbstractExpressionTests { // string concatenation evaluate("'abc'+'def'","abcdef",String.class); - // evaluate("5 + new Integer('37')",42,Integer.class); } @@ -423,17 +423,17 @@ public class OperatorTests extends AbstractExpressionTests { evaluate("'c' - 2", "a", String.class); evaluate("3.0f - 5.0f", -2.0f, Float.class); evaluateAndCheckError("'ab' - 2", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); - evaluateAndCheckError("2-'ab'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); + evaluateAndCheckError("2-'ab'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); SpelExpression expr = (SpelExpression)parser.parseExpression("-3"); - assertEquals("-3",expr.toStringAST()); + assertEquals("-3", expr.toStringAST()); expr = (SpelExpression)parser.parseExpression("2-3"); - assertEquals("(2 - 3)",expr.toStringAST()); + assertEquals("(2 - 3)", expr.toStringAST()); evaluate("-5d",-5d,Double.class); evaluate("-5L",-5L,Long.class); - evaluate("-5",-5,Integer.class); + evaluate("-5", -5, Integer.class); evaluate("-new java.math.BigDecimal('5')", new BigDecimal("-5"),BigDecimal.class); - evaluateAndCheckError("-'abc'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); + evaluateAndCheckError("-'abc'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); } @Test @@ -483,7 +483,6 @@ public class OperatorTests extends AbstractExpressionTests { evaluate("6.0d % 3.5d", 2.5d, Double.class); } - @Test public void testBigDecimals() { evaluate("3 + new java.math.BigDecimal('5')", new BigDecimal("8"), BigDecimal.class); @@ -545,7 +544,7 @@ public class OperatorTests extends AbstractExpressionTests { evaluate("3^2",9,Integer.class); evaluate("3.0d^2.0d",9.0d,Double.class); evaluate("3L^2L",9L,Long.class); - evaluate("(2^32)^2",9223372036854775807L,Long.class); + evaluate("(2^32)^2", 9223372036854775807L, Long.class); evaluate("new java.math.BigDecimal('5') ^ 3", new BigDecimal("125"), BigDecimal.class); } @@ -588,6 +587,17 @@ public class OperatorTests extends AbstractExpressionTests { evaluate("3L - 50L", -47L, Long.class); } + @Test + public void testBigIntegers() { + evaluate("3 + new java.math.BigInteger('5')", new BigInteger("8"), BigInteger.class); + evaluate("3 - new java.math.BigInteger('5')", new BigInteger("-2"), BigInteger.class); + evaluate("3 * new java.math.BigInteger('5')", new BigInteger("15"), BigInteger.class); + evaluate("3 / new java.math.BigInteger('5')", new BigInteger("0"), BigInteger.class); + evaluate("5 % new java.math.BigInteger('3')", new BigInteger("2"), BigInteger.class); + evaluate("new java.math.BigInteger('5') % 3", new BigInteger("2"), BigInteger.class); + evaluate("new java.math.BigInteger('5') ^ 3", new BigInteger("125"), BigInteger.class); + } + private Operator getOperatorNode(SpelExpression expr) { SpelNode node = expr.getAST(); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java index e03768143c1..c214e999ae6 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java @@ -256,6 +256,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { Object resultC = expression.getValue(new TestClass1(),Object.class); assertEquals(null,resultI); assertEquals(null,resultC); + assertEquals(null,resultC); } @Test @@ -2616,7 +2617,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } @Override - public void generateCode(String propertyName, MethodVisitor mv,CodeFlow codeflow) { + public void generateCode(String propertyName, MethodVisitor mv,CodeFlow cf) { if (method == null) { try { method = Payload2.class.getDeclaredMethod("getField", String.class); @@ -2624,10 +2625,10 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { catch (Exception e) { } } - String descriptor = codeflow.lastDescriptor(); + String descriptor = cf.lastDescriptor(); String memberDeclaringClassSlashedDescriptor = method.getDeclaringClass().getName().replace('.','/'); if (descriptor == null) { - codeflow.loadTarget(mv); + cf.loadTarget(mv); } if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) { mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor); @@ -2684,10 +2685,10 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } @Override - public void generateCode(String propertyName, MethodVisitor mv, CodeFlow codeflow) { - String descriptor = codeflow.lastDescriptor(); + public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) { + String descriptor = cf.lastDescriptor(); if (descriptor == null) { - codeflow.loadTarget(mv); + cf.loadTarget(mv); } mv.visitLdcInsn(propertyName); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get","(Ljava/lang/Object;)Ljava/lang/Object;",true); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java index f8d1dcd6acf..9c05f783b3c 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java @@ -328,14 +328,14 @@ public class SpelParserTests { public void token() { Token token = new Token(TokenKind.NOT, 0, 3); assertEquals(TokenKind.NOT, token.kind); - assertEquals(0, token.startpos); - assertEquals(3, token.endpos); + assertEquals(0, token.startPos); + assertEquals(3, token.endPos); assertEquals("[NOT(!)](0,3)", token.toString()); token = new Token(TokenKind.LITERAL_STRING, "abc".toCharArray(), 0, 3); assertEquals(TokenKind.LITERAL_STRING, token.kind); - assertEquals(0, token.startpos); - assertEquals(3, token.endpos); + assertEquals(0, token.startPos); + assertEquals(3, token.endPos); assertEquals("[LITERAL_STRING:abc](0,3)", token.toString()); }