/* * 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.Collection; import java.util.Set; import org.junit.Test; import org.springframework.expression.EvaluationException; import org.springframework.expression.Expression; import org.springframework.expression.ParseException; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.testresources.PlaceOfBirth; import static org.junit.Assert.*; /** * Tests set value expressions. * * @author Keith Donald * @author Andy Clement */ public class SetValueTests extends AbstractExpressionTests { private final static boolean DEBUG = false; @Test public void testSetProperty() { setValue("wonNobelPrize", true); } @Test public void testSetNestedProperty() { setValue("placeOfBirth.city", "Wien"); } @Test public void testSetArrayElementValue() { setValue("inventions[0]", "Just the telephone"); } @Test public void testErrorCase() { setValueExpectError("3=4", null); } @Test public void testSetElementOfNull() { setValueExpectError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]",SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE); } @Test public void testSetArrayElementValueAllPrimitiveTypes() { setValue("arrayContainer.ints[1]", 3); setValue("arrayContainer.floats[1]", 3.0f); setValue("arrayContainer.booleans[1]", false); setValue("arrayContainer.doubles[1]", 3.4d); setValue("arrayContainer.shorts[1]", (short)3); setValue("arrayContainer.longs[1]", 3L); setValue("arrayContainer.bytes[1]", (byte) 3); setValue("arrayContainer.chars[1]", (char) 3); } @Test public void testIsWritableForInvalidExpressions_SPR10610() { Expression e = null; StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext(); // PROPERTYORFIELDREFERENCE // Non existent field (or property): e = parser.parseExpression("arrayContainer.wibble"); assertFalse("Should not be writable!",e.isWritable(lContext)); e = parser.parseExpression("arrayContainer.wibble.foo"); try { assertFalse("Should not be writable!",e.isWritable(lContext)); fail("Should have had an error because wibble does not really exist"); } catch (SpelEvaluationException see) { // org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 15): Property or field 'wibble' cannot be found on object of type 'org.springframework.expression.spel.testresources.ArrayContainer' - maybe not public? // at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:225) // success! } // VARIABLE // the variable does not exist (but that is OK, we should be writable) e = parser.parseExpression("#madeup1"); assertTrue("Should be writable!",e.isWritable(lContext)); e = parser.parseExpression("#madeup2.bar"); // compound expression assertFalse("Should not be writable!",e.isWritable(lContext)); // INDEXER // non existent indexer (wibble made up) e = parser.parseExpression("arrayContainer.wibble[99]"); try { assertFalse("Should not be writable!",e.isWritable(lContext)); fail("Should have had an error because wibble does not really exist"); } catch (SpelEvaluationException see) { // success! } // non existent indexer (index via a string) e = parser.parseExpression("arrayContainer.ints['abc']"); try { assertFalse("Should not be writable!",e.isWritable(lContext)); fail("Should have had an error because wibble does not really exist"); } catch (SpelEvaluationException see) { // success! } } @Test public void testSetArrayElementValueAllPrimitiveTypesErrors() { // none of these sets are possible due to (expected) conversion problems setValueExpectError("arrayContainer.ints[1]", "wibble"); setValueExpectError("arrayContainer.floats[1]", "dribble"); setValueExpectError("arrayContainer.booleans[1]", "nein"); // TODO -- this fails with NPE due to ArrayToObject converter - discuss with Andy //setValueExpectError("arrayContainer.doubles[1]", new ArrayList<String>()); //setValueExpectError("arrayContainer.shorts[1]", new ArrayList<String>()); //setValueExpectError("arrayContainer.longs[1]", new ArrayList<String>()); setValueExpectError("arrayContainer.bytes[1]", "NaB"); setValueExpectError("arrayContainer.chars[1]", "NaC"); } @Test public void testSetArrayElementNestedValue() { setValue("placesLived[0].city", "Wien"); } @Test public void testSetListElementValue() { setValue("placesLivedList[0]", new PlaceOfBirth("Wien")); } @Test public void testSetGenericListElementValueTypeCoersion() { // TODO currently failing since setValue does a getValue and "Wien" string != PlaceOfBirth - check with andy setValue("placesLivedList[0]", "Wien"); } @Test public void testSetGenericListElementValueTypeCoersionOK() { setValue("booleanList[0]", "true", Boolean.TRUE); } @Test public void testSetListElementNestedValue() { setValue("placesLived[0].city", "Wien"); } @Test public void testSetArrayElementInvalidIndex() { setValueExpectError("placesLived[23]", "Wien"); setValueExpectError("placesLivedList[23]", "Wien"); } @Test public void testSetMapElements() { setValue("testMap['montag']","lundi"); } @Test public void testIndexingIntoUnsupportedType() { setValueExpectError("'hello'[3]", 'p'); } @Test public void testSetPropertyTypeCoersion() { setValue("publicBoolean", "true", Boolean.TRUE); } @Test public void testSetPropertyTypeCoersionThroughSetter() { setValue("SomeProperty", "true", Boolean.TRUE); } @Test public void testAssign() throws Exception { StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext(); Expression e = parse("publicName='Andy'"); assertFalse(e.isWritable(eContext)); assertEquals("Andy",e.getValue(eContext)); } /* * Testing the coercion of both the keys and the values to the correct type */ @Test public void testSetGenericMapElementRequiresCoercion() throws Exception { StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext(); Expression e = parse("mapOfStringToBoolean[42]"); assertNull(e.getValue(eContext)); // Key should be coerced to string representation of 42 e.setValue(eContext, "true"); // All keys should be strings Set<?> ks = parse("mapOfStringToBoolean.keySet()").getValue(eContext, Set.class); for (Object o: ks) { assertEquals(String.class,o.getClass()); } // All values should be booleans Collection<?> vs = parse("mapOfStringToBoolean.values()").getValue(eContext, Collection.class); for (Object o: vs) { assertEquals(Boolean.class, o.getClass()); } // One final test check coercion on the key for a map lookup Object o = e.getValue(eContext); assertEquals(Boolean.TRUE,o); } private Expression parse(String expressionString) throws Exception { return parser.parseExpression(expressionString); } /** * Call setValue() but expect it to fail. */ protected void setValueExpectError(String expression, Object value) { try { Expression e = parser.parseExpression(expression); if (e == null) { fail("Parser returned null for expression"); } if (DEBUG) { SpelUtilities.printAbstractSyntaxTree(System.out, e); } StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext(); e.setValue(lContext, value); fail("expected an error"); } catch (ParseException pe) { pe.printStackTrace(); fail("Unexpected Exception: " + pe.getMessage()); } catch (EvaluationException ee) { // success! } } protected void setValue(String expression, Object value) { try { Expression e = parser.parseExpression(expression); if (e == null) { fail("Parser returned null for expression"); } if (DEBUG) { SpelUtilities.printAbstractSyntaxTree(System.out, e); } StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext(); assertTrue("Expression is not writeable but should be", e.isWritable(lContext)); e.setValue(lContext, value); assertEquals("Retrieved value was not equal to set value", value, e.getValue(lContext,value.getClass())); } catch (EvaluationException ee) { ee.printStackTrace(); fail("Unexpected Exception: " + ee.getMessage()); } catch (ParseException pe) { pe.printStackTrace(); fail("Unexpected Exception: " + pe.getMessage()); } } /** * For use when coercion is happening during a setValue(). The expectedValue should be * the coerced form of the value. */ protected void setValue(String expression, Object value, Object expectedValue) { try { Expression e = parser.parseExpression(expression); if (e == null) { fail("Parser returned null for expression"); } if (DEBUG) { SpelUtilities.printAbstractSyntaxTree(System.out, e); } StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext(); assertTrue("Expression is not writeable but should be", e.isWritable(lContext)); e.setValue(lContext, value); Object a = expectedValue; Object b = e.getValue(lContext); if (!a.equals(b)) { fail("Not the same: ["+a+"] type="+a.getClass()+" ["+b+"] type="+b.getClass()); // assertEquals("Retrieved value was not equal to set value", expectedValue, e.getValue(lContext)); } } catch (EvaluationException ee) { ee.printStackTrace(); fail("Unexpected Exception: " + ee.getMessage()); } catch (ParseException pe) { pe.printStackTrace(); fail("Unexpected Exception: " + pe.getMessage()); } } }