/* * Copyright 2002-2016 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.expression.spel; import java.util.EmptyStackException; import java.util.HashMap; import java.util.Map; import org.junit.Test; import org.springframework.core.convert.TypeDescriptor; import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; import org.springframework.expression.Operation; import org.springframework.expression.TypedValue; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.testresources.Inventor; import static org.junit.Assert.*; /** * Tests for the expression state object - some features are not yet exploited in the language (eg nested scopes) * * @author Andy Clement */ public class ExpressionStateTests extends AbstractExpressionTests { @Test public void testConstruction() { EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); ExpressionState state = new ExpressionState(context); assertEquals(context,state.getEvaluationContext()); } // Local variables are in variable scopes which come and go during evaluation. Normal variables are // accessible through the evaluation context @Test public void testLocalVariables() { ExpressionState state = getState(); Object value = state.lookupLocalVariable("foo"); assertNull(value); state.setLocalVariable("foo",34); value = state.lookupLocalVariable("foo"); assertEquals(34,value); state.setLocalVariable("foo",null); value = state.lookupLocalVariable("foo"); assertEquals(null,value); } @Test public void testVariables() { ExpressionState state = getState(); TypedValue typedValue = state.lookupVariable("foo"); assertEquals(TypedValue.NULL,typedValue); state.setVariable("foo",34); typedValue = state.lookupVariable("foo"); assertEquals(34,typedValue.getValue()); assertEquals(Integer.class,typedValue.getTypeDescriptor().getType()); state.setVariable("foo","abc"); typedValue = state.lookupVariable("foo"); assertEquals("abc",typedValue.getValue()); assertEquals(String.class,typedValue.getTypeDescriptor().getType()); } @Test public void testNoVariableInteference() { ExpressionState state = getState(); TypedValue typedValue = state.lookupVariable("foo"); assertEquals(TypedValue.NULL,typedValue); state.setLocalVariable("foo",34); typedValue = state.lookupVariable("foo"); assertEquals(TypedValue.NULL,typedValue); state.setVariable("goo","hello"); assertNull(state.lookupLocalVariable("goo")); } @Test public void testLocalVariableNestedScopes() { ExpressionState state = getState(); assertEquals(null,state.lookupLocalVariable("foo")); state.setLocalVariable("foo",12); assertEquals(12,state.lookupLocalVariable("foo")); state.enterScope(null); assertEquals(12,state.lookupLocalVariable("foo")); // found in upper scope state.setLocalVariable("foo","abc"); assertEquals("abc",state.lookupLocalVariable("foo")); // found in nested scope state.exitScope(); assertEquals(12,state.lookupLocalVariable("foo")); // found in nested scope } @Test public void testRootContextObject() { ExpressionState state = getState(); assertEquals(Inventor.class,state.getRootContextObject().getValue().getClass()); // although the root object is being set on the evaluation context, the value in the 'state' remains what it was when constructed ((StandardEvaluationContext) state.getEvaluationContext()).setRootObject(null); assertEquals(Inventor.class,state.getRootContextObject().getValue().getClass()); // assertEquals(null, state.getRootContextObject().getValue()); state = new ExpressionState(new StandardEvaluationContext()); assertEquals(TypedValue.NULL,state.getRootContextObject()); ((StandardEvaluationContext)state.getEvaluationContext()).setRootObject(null); assertEquals(null,state.getRootContextObject().getValue()); } @Test public void testActiveContextObject() { ExpressionState state = getState(); assertEquals(state.getRootContextObject().getValue(),state.getActiveContextObject().getValue()); try { state.popActiveContextObject(); fail("stack should be empty..."); } catch (EmptyStackException ese) { // success } state.pushActiveContextObject(new TypedValue(34)); assertEquals(34,state.getActiveContextObject().getValue()); state.pushActiveContextObject(new TypedValue("hello")); assertEquals("hello",state.getActiveContextObject().getValue()); state.popActiveContextObject(); assertEquals(34,state.getActiveContextObject().getValue()); state.popActiveContextObject(); assertEquals(state.getRootContextObject().getValue(),state.getActiveContextObject().getValue()); state = new ExpressionState(new StandardEvaluationContext()); assertEquals(TypedValue.NULL,state.getActiveContextObject()); } @Test public void testPopulatedNestedScopes() { ExpressionState state = getState(); assertNull(state.lookupLocalVariable("foo")); state.enterScope("foo",34); assertEquals(34,state.lookupLocalVariable("foo")); state.enterScope(null); state.setLocalVariable("foo",12); assertEquals(12,state.lookupLocalVariable("foo")); state.exitScope(); assertEquals(34,state.lookupLocalVariable("foo")); state.exitScope(); assertNull(state.lookupLocalVariable("goo")); } @Test public void testRootObjectConstructor() { EvaluationContext ctx = getContext(); // TypedValue root = ctx.getRootObject(); // supplied should override root on context ExpressionState state = new ExpressionState(ctx,new TypedValue("i am a string")); TypedValue stateRoot = state.getRootContextObject(); assertEquals(String.class,stateRoot.getTypeDescriptor().getType()); assertEquals("i am a string",stateRoot.getValue()); } @Test public void testPopulatedNestedScopesMap() { ExpressionState state = getState(); assertNull(state.lookupLocalVariable("foo")); assertNull(state.lookupLocalVariable("goo")); Map<String,Object> m = new HashMap<>(); m.put("foo",34); m.put("goo","abc"); state.enterScope(m); assertEquals(34,state.lookupLocalVariable("foo")); assertEquals("abc",state.lookupLocalVariable("goo")); state.enterScope(null); state.setLocalVariable("foo",12); assertEquals(12,state.lookupLocalVariable("foo")); assertEquals("abc",state.lookupLocalVariable("goo")); state.exitScope(); state.exitScope(); assertNull(state.lookupLocalVariable("foo")); assertNull(state.lookupLocalVariable("goo")); } @Test public void testOperators() throws Exception { ExpressionState state = getState(); try { state.operate(Operation.ADD,1,2); fail("should have failed"); } catch (EvaluationException ee) { SpelEvaluationException sEx = (SpelEvaluationException)ee; assertEquals(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES,sEx.getMessageCode()); } try { state.operate(Operation.ADD,null,null); fail("should have failed"); } catch (EvaluationException ee) { SpelEvaluationException sEx = (SpelEvaluationException)ee; assertEquals(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES,sEx.getMessageCode()); } } @Test public void testComparator() { ExpressionState state = getState(); assertEquals(state.getEvaluationContext().getTypeComparator(),state.getTypeComparator()); } @Test public void testTypeLocator() throws EvaluationException { ExpressionState state = getState(); assertNotNull(state.getEvaluationContext().getTypeLocator()); assertEquals(Integer.class,state.findType("java.lang.Integer")); try { state.findType("someMadeUpName"); fail("Should have failed to find it"); } catch (EvaluationException ee) { SpelEvaluationException sEx = (SpelEvaluationException)ee; assertEquals(SpelMessage.TYPE_NOT_FOUND,sEx.getMessageCode()); } } @Test public void testTypeConversion() throws EvaluationException { ExpressionState state = getState(); String s = (String)state.convertValue(34, TypeDescriptor.valueOf(String.class)); assertEquals("34",s); s = (String)state.convertValue(new TypedValue(34), TypeDescriptor.valueOf(String.class)); assertEquals("34",s); } @Test public void testPropertyAccessors() { ExpressionState state = getState(); assertEquals(state.getEvaluationContext().getPropertyAccessors(),state.getPropertyAccessors()); } /** * @return a new ExpressionState */ private ExpressionState getState() { EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); ExpressionState state = new ExpressionState(context); return state; } private EvaluationContext getContext() { return TestScenarioCreator.getTestEvaluationContext(); } }