From beab8ab4e752fa7da27a1df3904fe4f628236e06 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 28 Feb 2022 14:19:29 +0100 Subject: [PATCH 1/2] Test claims regarding SpEL support for T(Character) See gh-28112 --- .../springframework/expression/spel/EvaluationTests.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index 98b9704ea336..a1c1a0eedaee 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 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. @@ -420,7 +420,11 @@ public void testIndexerError() { @Test public void testStaticRef02() { - evaluate("T(java.awt.Color).green.getRGB()!=0", "true", Boolean.class); + evaluate("T(java.awt.Color).green.getRGB() != 0", true, Boolean.class); + evaluate("(T(java.lang.Math).random() * 100.0 ) > 0", true, Boolean.class); + evaluate("(T(Math).random() * 100.0) > 0", true, Boolean.class); + evaluate("T(Character).isUpperCase('Test'.charAt(0)) ? 'uppercase' : 'lowercase'", "uppercase", String.class); + evaluate("T(Character).isUpperCase('Test'.charAt(1)) ? 'uppercase' : 'lowercase'", "lowercase", String.class); } // variables and functions From 84de100fc64cbb96719d7ac18e977e7aa668074a Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 28 Feb 2022 17:15:35 +0100 Subject: [PATCH 2/2] Polishing --- .../expression/MethodResolver.java | 6 +- .../expression/spel/ast/MethodReference.java | 3 +- .../expression/spel/EvaluationTests.java | 2390 +++++++++-------- .../spel/SpelDocumentationTests.java | 130 +- .../expression/spel/TestScenarioCreator.java | 2 +- 5 files changed, 1270 insertions(+), 1261 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java b/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java index db555ce7b238..d4216d2a55c3 100644 --- a/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java +++ b/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2021 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. @@ -22,8 +22,8 @@ import org.springframework.lang.Nullable; /** - * A method resolver attempts locate a method and returns a command executor that can be - * used to invoke that method. The command executor will be cached but if it 'goes stale' + * A method resolver attempts to locate a method and returns a command executor that can be + * used to invoke that method. The command executor will be cached, but if it 'goes stale' * the resolvers will be called again. * * @author Andy Clement 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 cbaf1c8d1add..1ecb6187001a 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 @@ -200,8 +200,7 @@ private MethodExecutor findAccessorForMethod(List argumentTypes, EvaluationContext evaluationContext) throws SpelEvaluationException { AccessException accessException = null; - List methodResolvers = evaluationContext.getMethodResolvers(); - for (MethodResolver methodResolver : methodResolvers) { + for (MethodResolver methodResolver : evaluationContext.getMethodResolvers()) { try { MethodExecutor methodExecutor = methodResolver.resolve( evaluationContext, targetObject, this.name, argumentTypes); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index a1c1a0eedaee..097daf526da6 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -16,25 +16,23 @@ package org.springframework.expression.spel; -import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.springframework.core.convert.TypeDescriptor; import org.springframework.expression.AccessException; import org.springframework.expression.BeanResolver; import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; -import org.springframework.expression.MethodExecutor; import org.springframework.expression.MethodFilter; -import org.springframework.expression.MethodResolver; import org.springframework.expression.ParseException; import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -57,1305 +55,1333 @@ * @author Giovanni Dall'Oglio Risso * @since 3.0 */ -public class EvaluationTests extends AbstractExpressionTests { - - @Test - public void testCreateListsOnAttemptToIndexNull01() throws EvaluationException, ParseException { - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e = parser.parseExpression("list[0]"); - TestClass testClass = new TestClass(); - - Object o = e.getValue(new StandardEvaluationContext(testClass)); - assertThat(o).isEqualTo(""); - o = parser.parseExpression("list[3]").getValue(new StandardEvaluationContext(testClass)); - assertThat(o).isEqualTo(""); - assertThat(testClass.list.size()).isEqualTo(4); - - assertThatExceptionOfType(EvaluationException.class).isThrownBy(() -> - parser.parseExpression("list2[3]").getValue(new StandardEvaluationContext(testClass))); - - o = parser.parseExpression("foo[3]").getValue(new StandardEvaluationContext(testClass)); - assertThat(o).isEqualTo(""); - assertThat(testClass.getFoo().size()).isEqualTo(4); - } +class EvaluationTests extends AbstractExpressionTests { - @Test - public void testCreateMapsOnAttemptToIndexNull01() { - TestClass testClass = new TestClass(); - StandardEvaluationContext ctx = new StandardEvaluationContext(testClass); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + @Nested + class MiscellaneousTests { - Object o = parser.parseExpression("map['a']").getValue(ctx); - assertThat(o).isNull(); - o = parser.parseExpression("map").getValue(ctx); - assertThat(o).isNotNull(); + @Test + void createListsOnAttemptToIndexNull01() throws EvaluationException, ParseException { + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e = parser.parseExpression("list[0]"); + TestClass testClass = new TestClass(); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - parser.parseExpression("map2['a']").getValue(ctx)); - // map2 should be null, there is no setter - } + Object o = e.getValue(new StandardEvaluationContext(testClass)); + assertThat(o).isEqualTo(""); + o = parser.parseExpression("list[3]").getValue(new StandardEvaluationContext(testClass)); + assertThat(o).isEqualTo(""); + assertThat(testClass.list.size()).isEqualTo(4); - // wibble2 should be null (cannot be initialized dynamically), there is no setter - @Test - public void testCreateObjectsOnAttemptToReferenceNull() { - TestClass testClass = new TestClass(); - StandardEvaluationContext ctx = new StandardEvaluationContext(testClass); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + assertThatExceptionOfType(EvaluationException.class).isThrownBy(() -> + parser.parseExpression("list2[3]").getValue(new StandardEvaluationContext(testClass))); - Object o = parser.parseExpression("wibble.bar").getValue(ctx); - assertThat(o).isEqualTo("hello"); - o = parser.parseExpression("wibble").getValue(ctx); - assertThat(o).isNotNull(); + o = parser.parseExpression("foo[3]").getValue(new StandardEvaluationContext(testClass)); + assertThat(o).isEqualTo(""); + assertThat(testClass.getFoo().size()).isEqualTo(4); + } - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - parser.parseExpression("wibble2.bar").getValue(ctx)); - } + @Test + void createMapsOnAttemptToIndexNull() { + TestClass testClass = new TestClass(); + StandardEvaluationContext ctx = new StandardEvaluationContext(testClass); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - @Test - public void testElvis01() { - evaluate("'Andy'?:'Dave'", "Andy", String.class); - evaluate("null?:'Dave'", "Dave", String.class); - } + Object o = parser.parseExpression("map['a']").getValue(ctx); + assertThat(o).isNull(); + o = parser.parseExpression("map").getValue(ctx); + assertThat(o).isNotNull(); - @Test - public void testSafeNavigation() { - evaluate("null?.null?.null", null, null); - } + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + parser.parseExpression("map2['a']").getValue(ctx)); + // map2 should be null, there is no setter + } - @Test - public void testRelOperatorGT01() { - evaluate("3 > 6", "false", Boolean.class); - } + // wibble2 should be null (cannot be initialized dynamically), there is no setter + @Test + void createObjectsOnAttemptToReferenceNull() { + TestClass testClass = new TestClass(); + StandardEvaluationContext ctx = new StandardEvaluationContext(testClass); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - @Test - public void testRelOperatorLT01() { - evaluate("3 < 6", "true", Boolean.class); - } + Object o = parser.parseExpression("wibble.bar").getValue(ctx); + assertThat(o).isEqualTo("hello"); + o = parser.parseExpression("wibble").getValue(ctx); + assertThat(o).isNotNull(); - @Test - public void testRelOperatorLE01() { - evaluate("3 <= 6", "true", Boolean.class); - } + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + parser.parseExpression("wibble2.bar").getValue(ctx)); + } - @Test - public void testRelOperatorGE01() { - evaluate("3 >= 6", "false", Boolean.class); - } + @Test + void elvisOperator() { + evaluate("'Andy'?:'Dave'", "Andy", String.class); + evaluate("null?:'Dave'", "Dave", String.class); + } - @Test - public void testRelOperatorGE02() { - evaluate("3 >= 3", "true", Boolean.class); - } + @Test + void safeNavigation() { + evaluate("null?.null?.null", null, null); + } - @Test - public void testRelOperatorsInstanceof01() { - evaluate("'xyz' instanceof T(int)", "false", Boolean.class); - } + @Test // SPR-16731 + void matchesWithPatternAccessThreshold() { + String pattern = "^(?=[a-z0-9-]{1,47})([a-z0-9]+[-]{0,1}){1,47}[a-z0-9]{1}$"; + String expression = "'abcde-fghijklmn-o42pasdfasdfasdf.qrstuvwxyz10x.xx.yyy.zasdfasfd' matches \'" + pattern + "\'"; + Expression expr = parser.parseExpression(expression); + assertThatExceptionOfType(SpelEvaluationException.class) + .isThrownBy(expr::getValue) + .withCauseInstanceOf(IllegalStateException.class) + .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.FLAWED_PATTERN)); + } - @Test - public void testRelOperatorsInstanceof04() { - evaluate("null instanceof T(String)", "false", Boolean.class); - } + // mixing operators + @Test + void mixingOperators() { + evaluate("true and 5>3", "true", Boolean.class); + } - @Test - public void testRelOperatorsInstanceof05() { - evaluate("null instanceof T(Integer)", "false", Boolean.class); - } + // assignment + @Test + void assignmentToVariables() { + evaluate("#var1='value1'", "value1", String.class); + } - @Test - public void testRelOperatorsInstanceof06() { - evaluateAndCheckError("'A' instanceof null", SpelMessage.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND, 15, "null"); - } + @Test + void operatorVariants() { + SpelExpression e = (SpelExpression) parser.parseExpression("#a < #b"); + EvaluationContext ctx = new StandardEvaluationContext(); + ctx.setVariable("a", (short) 3); + ctx.setVariable("b", (short) 6); + assertThat(e.getValue(ctx, Boolean.class)).isTrue(); + ctx.setVariable("b", (byte) 6); + assertThat(e.getValue(ctx, Boolean.class)).isTrue(); + ctx.setVariable("a", (byte) 9); + ctx.setVariable("b", (byte) 6); + assertThat(e.getValue(ctx, Boolean.class)).isFalse(); + ctx.setVariable("a", 10L); + ctx.setVariable("b", (short) 30); + assertThat(e.getValue(ctx, Boolean.class)).isTrue(); + ctx.setVariable("a", (byte) 3); + ctx.setVariable("b", (short) 30); + assertThat(e.getValue(ctx, Boolean.class)).isTrue(); + ctx.setVariable("a", (byte) 3); + ctx.setVariable("b", 30L); + assertThat(e.getValue(ctx, Boolean.class)).isTrue(); + ctx.setVariable("a", (byte) 3); + ctx.setVariable("b", 30f); + assertThat(e.getValue(ctx, Boolean.class)).isTrue(); + ctx.setVariable("a", new BigInteger("10")); + ctx.setVariable("b", new BigInteger("20")); + assertThat(e.getValue(ctx, Boolean.class)).isTrue(); + } - @Test - public void testRelOperatorsMatches01() { - evaluate("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "false", Boolean.class); - } + @Test + void indexer03() { + evaluate("'christian'[8]", "n", String.class); + } - @Test - public void testRelOperatorsMatches02() { - evaluate("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "true", Boolean.class); - } + @Test + void indexerError() { + evaluateAndCheckError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]", + SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE); + } - @Test - public void testRelOperatorsMatches03() { - evaluateAndCheckError("null matches '^.*$'", SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, 0, null); - } + @Test + void stringType() { + evaluateAndAskForReturnType("getPlaceOfBirth().getCity()", "SmilJan", String.class); + } - @Test - public void testRelOperatorsMatches04() { - evaluateAndCheckError("'abc' matches null", SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, 14, null); - } + @Test + void numbers() { + evaluateAndAskForReturnType("3*4+5", 17, Integer.class); + evaluateAndAskForReturnType("3*4+5", 17L, Long.class); + evaluateAndAskForReturnType("65", 'A', Character.class); + evaluateAndAskForReturnType("3*4+5", (short) 17, Short.class); + evaluateAndAskForReturnType("3*4+5", "17", String.class); + } - @Test - public void testRelOperatorsMatches05() { - evaluate("27 matches '^.*2.*$'", true, Boolean.class); // conversion int>string - } + @Test + void advancedNumerics() { + int twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Integer.class); + assertThat(twentyFour).isEqualTo(24); + double one = parser.parseExpression("8.0 / 5e0 % 2").getValue(Double.class); + assertThat((float) one).isCloseTo((float) 1.6d, within((float) 0d)); + int o = parser.parseExpression("8.0 / 5e0 % 2").getValue(Integer.class); + assertThat(o).isEqualTo(1); + int sixteen = parser.parseExpression("-2 ^ 4").getValue(Integer.class); + assertThat(sixteen).isEqualTo(16); + int minusFortyFive = parser.parseExpression("1+2-3*8^2/2/2").getValue(Integer.class); + assertThat(minusFortyFive).isEqualTo(-45); + } - @Test // SPR-16731 - public void testMatchesWithPatternAccessThreshold() { - String pattern = "^(?=[a-z0-9-]{1,47})([a-z0-9]+[-]{0,1}){1,47}[a-z0-9]{1}$"; - String expression = "'abcde-fghijklmn-o42pasdfasdfasdf.qrstuvwxyz10x.xx.yyy.zasdfasfd' matches \'" + pattern + "\'"; - Expression expr = parser.parseExpression(expression); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy( - expr::getValue) - .withCauseInstanceOf(IllegalStateException.class) - .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.FLAWED_PATTERN)); - } + @Test + void comparison() { + EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); + boolean trueValue = parser.parseExpression("T(java.util.Date) == Birthdate.Class").getValue( + context, Boolean.class); + assertThat(trueValue).isTrue(); + } - // mixing operators - @Test - public void testMixingOperators01() { - evaluate("true and 5>3", "true", Boolean.class); - } + @Test + void resolvingList() { + StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); + assertThatExceptionOfType(EvaluationException.class).isThrownBy(() -> + parser.parseExpression("T(List)!=null").getValue(context, Boolean.class)); + ((StandardTypeLocator) context.getTypeLocator()).registerImport("java.util"); + assertThat(parser.parseExpression("T(List)!=null").getValue(context, Boolean.class)).isTrue(); + } + + @Test + void resolvingString() { + Class stringClass = parser.parseExpression("T(String)").getValue(Class.class); + assertThat(stringClass).isEqualTo(String.class); + } + + /** + * SPR-6984: attempting to index a collection on write using an index that + * doesn't currently exist in the collection (address.crossStreets[0] below) + */ + @Test + void initializingCollectionElementsOnWrite() { + TestPerson person = new TestPerson(); + EvaluationContext context = new StandardEvaluationContext(person); + SpelParserConfiguration config = new SpelParserConfiguration(true, true); + ExpressionParser parser = new SpelExpressionParser(config); + Expression e = parser.parseExpression("name"); + e.setValue(context, "Oleg"); + assertThat(person.getName()).isEqualTo("Oleg"); + + e = parser.parseExpression("address.street"); + e.setValue(context, "123 High St"); + assertThat(person.getAddress().getStreet()).isEqualTo("123 High St"); + + e = parser.parseExpression("address.crossStreets[0]"); + e.setValue(context, "Blah"); + assertThat(person.getAddress().getCrossStreets().get(0)).isEqualTo("Blah"); + + e = parser.parseExpression("address.crossStreets[3]"); + e.setValue(context, "Wibble"); + assertThat(person.getAddress().getCrossStreets().get(0)).isEqualTo("Blah"); + assertThat(person.getAddress().getCrossStreets().get(3)).isEqualTo("Wibble"); + } + + /** + * Verifies behavior requested in SPR-9613. + */ + @Test + void caseInsensitiveNullLiterals() { + ExpressionParser parser = new SpelExpressionParser(); + + Expression e = parser.parseExpression("null"); + assertThat(e.getValue()).isNull(); + + e = parser.parseExpression("NULL"); + assertThat(e.getValue()).isNull(); + + e = parser.parseExpression("NuLl"); + assertThat(e.getValue()).isNull(); + } + + /** + * Verifies behavior requested in SPR-9621. + */ + @Test + void customMethodFilter() { + StandardEvaluationContext context = new StandardEvaluationContext(); + + // Register a custom MethodResolver... + context.setMethodResolvers(Arrays.asList((evaluationContext, targetObject, name, argumentTypes) -> null)); + + // or simply... + // context.setMethodResolvers(new ArrayList()); + + // Register a custom MethodFilter... + MethodFilter methodFilter = methods -> null; + assertThatIllegalStateException() + .isThrownBy(() -> context.registerMethodFilter(String.class, methodFilter)) + .withMessage("Method filter cannot be set as the reflective method resolver is not in use"); + } + + /** + * This test is checking that with the changes for 9751 that the refactoring in Indexer is + * coping correctly for references beyond collection boundaries. + */ + @Test + void collectionGrowingViaIndexer() { + Spr9751 instance = new Spr9751(); + + // Add a new element to the list + StandardEvaluationContext ctx = new StandardEvaluationContext(instance); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e = parser.parseExpression("listOfStrings[++index3]='def'"); + e.getValue(ctx); + assertThat(instance.listOfStrings.size()).isEqualTo(2); + assertThat(instance.listOfStrings.get(1)).isEqualTo("def"); + + // Check reference beyond end of collection + ctx = new StandardEvaluationContext(instance); + parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + e = parser.parseExpression("listOfStrings[0]"); + String value = e.getValue(ctx, String.class); + assertThat(value).isEqualTo("abc"); + e = parser.parseExpression("listOfStrings[1]"); + value = e.getValue(ctx, String.class); + assertThat(value).isEqualTo("def"); + e = parser.parseExpression("listOfStrings[2]"); + value = e.getValue(ctx, String.class); + assertThat(value).isEqualTo(""); + + // Now turn off growing and reference off the end + StandardEvaluationContext failCtx = new StandardEvaluationContext(instance); + parser = new SpelExpressionParser(new SpelParserConfiguration(false, false)); + Expression failExp = parser.parseExpression("listOfStrings[3]"); + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + failExp.getValue(failCtx, String.class)) + .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS)); + } + + @Test + void limitCollectionGrowing() { + TestClass instance = new TestClass(); + StandardEvaluationContext ctx = new StandardEvaluationContext(instance); + SpelExpressionParser parser = new SpelExpressionParser( new SpelParserConfiguration(true, true, 3)); + Expression e = parser.parseExpression("foo[2]"); + e.setValue(ctx, "2"); + assertThat(instance.getFoo().size()).isEqualTo(3); + e = parser.parseExpression("foo[3]"); + try { + e.setValue(ctx, "3"); + } + catch (SpelEvaluationException see) { + assertThat(see.getMessageCode()).isEqualTo(SpelMessage.UNABLE_TO_GROW_COLLECTION); + assertThat(instance.getFoo().size()).isEqualTo(3); + } + } - // property access - @Test - public void testPropertyField01() { - evaluate("name", "Nikola Tesla", String.class, false); - // not writable because (1) name is private (2) there is no setter, only a getter - evaluateAndCheckError("madeup", SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, 0, "madeup", - "org.springframework.expression.spel.testresources.Inventor"); } - @Test - public void testPropertyField02_SPR7100() { - evaluate("_name", "Nikola Tesla", String.class); - evaluate("_name_", "Nikola Tesla", String.class); + @Nested + class RelationalOperatorTests { + + @Test + void relOperatorGT() { + evaluate("3 > 6", "false", Boolean.class); + } + + @Test + void relOperatorLT() { + evaluate("3 < 6", "true", Boolean.class); + } + + @Test + void relOperatorLE() { + evaluate("3 <= 6", "true", Boolean.class); + } + + @Test + void relOperatorGE01() { + evaluate("3 >= 6", "false", Boolean.class); + } + + @Test + void relOperatorGE02() { + evaluate("3 >= 3", "true", Boolean.class); + } + + @Test + void relOperatorsInstanceof01() { + evaluate("'xyz' instanceof T(int)", "false", Boolean.class); + } + + @Test + void relOperatorsInstanceof04() { + evaluate("null instanceof T(String)", "false", Boolean.class); + } + + @Test + void relOperatorsInstanceof05() { + evaluate("null instanceof T(Integer)", "false", Boolean.class); + } + + @Test + void relOperatorsInstanceof06() { + evaluateAndCheckError("'A' instanceof null", SpelMessage.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND, 15, "null"); + } + + @Test + void relOperatorsMatches01() { + evaluate("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "false", Boolean.class); + } + + @Test + void relOperatorsMatches02() { + evaluate("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "true", Boolean.class); + } + + @Test + void relOperatorsMatches03() { + evaluateAndCheckError("null matches '^.*$'", SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, 0, null); + } + + @Test + void relOperatorsMatches04() { + evaluateAndCheckError("'abc' matches null", SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, 14, null); + } + + @Test + void relOperatorsMatches05() { + evaluate("27 matches '^.*2.*$'", true, Boolean.class); // conversion int>string + } + } - @Test - public void testRogueTrailingDotCausesNPE_SPR6866() { - assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> - new SpelExpressionParser().parseExpression("placeOfBirth.foo.")) + @Nested + class PropertyAccessTests { + + @Test + void propertyField() { + evaluate("name", "Nikola Tesla", String.class, false); + // not writable because (1) name is private (2) there is no setter, only a getter + evaluateAndCheckError("madeup", SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, 0, "madeup", + "org.springframework.expression.spel.testresources.Inventor"); + } + + @Test + void propertyField_SPR7100() { + evaluate("_name", "Nikola Tesla", String.class); + evaluate("_name_", "Nikola Tesla", String.class); + } + + @Test + void rogueTrailingDotCausesNPE_SPR6866() { + assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> + new SpelExpressionParser().parseExpression("placeOfBirth.foo.")) .satisfies(ex -> { assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OOD); assertThat(ex.getPosition()).isEqualTo(16); }); - } + } - // nested properties - @Test - public void testPropertiesNested01() { - evaluate("placeOfBirth.city", "SmilJan", String.class, true); - } + @Nested + class NestedPropertiesTests { - @Test - public void testPropertiesNested02() { - evaluate("placeOfBirth.doubleIt(12)", "24", Integer.class); - } - - @Test - public void testPropertiesNested03() throws ParseException { - assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> - new SpelExpressionParser().parseRaw("placeOfBirth.23")) - .satisfies(ex -> { - assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.UNEXPECTED_DATA_AFTER_DOT); - assertThat(ex.getInserts()[0]).isEqualTo("23"); - }); - } + // nested properties + @Test + void propertiesNested01() { + evaluate("placeOfBirth.city", "SmilJan", String.class, true); + } - // methods - @Test - public void testMethods01() { - evaluate("echo(12)", "12", String.class); - } + @Test + void propertiesNested02() { + evaluate("placeOfBirth.doubleIt(12)", "24", Integer.class); + } - @Test - public void testMethods02() { - evaluate("echo(name)", "Nikola Tesla", String.class); - } + @Test + void propertiesNested03() throws ParseException { + assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> + new SpelExpressionParser().parseRaw("placeOfBirth.23")) + .satisfies(ex -> { + assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.UNEXPECTED_DATA_AFTER_DOT); + assertThat(ex.getInserts()[0]).isEqualTo("23"); + }); + } - // constructors - @Test - public void testConstructorInvocation01() { - evaluate("new String('hello')", "hello", String.class); - } + } - @Test - public void testConstructorInvocation05() { - evaluate("new java.lang.String('foobar')", "foobar", String.class); } - @Test - public void testConstructorInvocation06() { - // repeated evaluation to drive use of cached executor - SpelExpression e = (SpelExpression) parser.parseExpression("new String('wibble')"); - String newString = e.getValue(String.class); - assertThat(newString).isEqualTo("wibble"); - newString = e.getValue(String.class); - assertThat(newString).isEqualTo("wibble"); - - // not writable - assertThat(e.isWritable(new StandardEvaluationContext())).isFalse(); + @Nested + class MethodAndConstructorTests { - // ast - assertThat(e.toStringAST()).isEqualTo("new String('wibble')"); - } + @Test + void methods01() { + evaluate("echo(12)", "12", String.class); + } - // unary expressions - @Test - public void testUnaryMinus01() { - evaluate("-5", "-5", Integer.class); - } + @Test + void methods02() { + evaluate("echo(name)", "Nikola Tesla", String.class); + } - @Test - public void testUnaryPlus01() { - evaluate("+5", "5", Integer.class); - } + @Test + void constructorInvocation01() { + evaluate("new String('hello')", "hello", String.class); + } - @Test - public void testUnaryNot01() { - evaluate("!true", "false", Boolean.class); - } + @Test + void constructorInvocation05() { + evaluate("new java.lang.String('foobar')", "foobar", String.class); + } - @Test - public void testUnaryNot02() { - evaluate("!false", "true", Boolean.class); - } + @Test + void constructorInvocation06() { + // repeated evaluation to drive use of cached executor + SpelExpression e = (SpelExpression) parser.parseExpression("new String('wibble')"); + String newString = e.getValue(String.class); + assertThat(newString).isEqualTo("wibble"); + newString = e.getValue(String.class); + assertThat(newString).isEqualTo("wibble"); - @Test - public void testUnaryNotWithNullValue() { - assertThatExceptionOfType(EvaluationException.class).isThrownBy( - parser.parseExpression("!null")::getValue); - } + // not writable + assertThat(e.isWritable(new StandardEvaluationContext())).isFalse(); - @Test - public void testAndWithNullValueOnLeft() { - assertThatExceptionOfType(EvaluationException.class).isThrownBy( - parser.parseExpression("null and true")::getValue); - } + // ast + assertThat(e.toStringAST()).isEqualTo("new String('wibble')"); + } - @Test - public void testAndWithNullValueOnRight() { - assertThatExceptionOfType(EvaluationException.class).isThrownBy( - parser.parseExpression("true and null")::getValue); } - @Test - public void testOrWithNullValueOnLeft() { - assertThatExceptionOfType(EvaluationException.class).isThrownBy( - parser.parseExpression("null or false")::getValue); - } + @Nested + class UnaryOperatorTests { - @Test - public void testOrWithNullValueOnRight() { - assertThatExceptionOfType(EvaluationException.class).isThrownBy( - parser.parseExpression("false or null")::getValue); - } + @Test + void unaryMinus() { + evaluate("-5", "-5", Integer.class); + } - // assignment - @Test - public void testAssignmentToVariables01() { - evaluate("#var1='value1'", "value1", String.class); - } + @Test + void unaryPlus() { + evaluate("+5", "5", Integer.class); + } - @Test - public void testTernaryOperator01() { - evaluate("2>4?1:2", 2, Integer.class); - } + @Test + void unaryNot01() { + evaluate("!true", "false", Boolean.class); + } - @Test - public void testTernaryOperator02() { - evaluate("'abc'=='abc'?1:2", 1, Integer.class); - } + @Test + void unaryNot02() { + evaluate("!false", "true", Boolean.class); + } - @Test - public void testTernaryOperator03() { - // cannot convert String to boolean - evaluateAndCheckError("'hello'?1:2", SpelMessage.TYPE_CONVERSION_ERROR); - } + @Test + void unaryNotWithNullValue() { + assertThatExceptionOfType(EvaluationException.class) + .isThrownBy(parser.parseExpression("!null")::getValue); + } - @Test - public void testTernaryOperator04() { - Expression e = parser.parseExpression("1>2?3:4"); - assertThat(e.isWritable(context)).isFalse(); } - @Test - public void testTernaryOperator05() { - evaluate("1>2?#var=4:#var=5", 5, Integer.class); - evaluate("3?:#var=5", 3, Integer.class); - evaluate("null?:#var=5", 5, Integer.class); - evaluate("2>4?(3>2?true:false):(5<3?true:false)", false, Boolean.class); - } + @Nested + class BinaryOperatorTests { - @Test - public void testTernaryOperatorWithNullValue() { - assertThatExceptionOfType(EvaluationException.class).isThrownBy( - parser.parseExpression("null ? 0 : 1")::getValue); - } + @Test + void andWithNullValueOnLeft() { + assertThatExceptionOfType(EvaluationException.class).isThrownBy( + parser.parseExpression("null and true")::getValue); + } - @Test - public void methodCallWithRootReferenceThroughParameter() { - evaluate("placeOfBirth.doubleIt(inventions.length)", 18, Integer.class); - } + @Test + void andWithNullValueOnRight() { + assertThatExceptionOfType(EvaluationException.class).isThrownBy( + parser.parseExpression("true and null")::getValue); + } - @Test - public void ctorCallWithRootReferenceThroughParameter() { - evaluate("new org.springframework.expression.spel.testresources.PlaceOfBirth(inventions[0].toString()).city", - "Telephone repeater", String.class); - } + @Test + void orWithNullValueOnLeft() { + assertThatExceptionOfType(EvaluationException.class).isThrownBy( + parser.parseExpression("null or false")::getValue); + } - @Test - public void fnCallWithRootReferenceThroughParameter() { - evaluate("#reverseInt(inventions.length, inventions.length, inventions.length)", "int[3]{9,9,9}", int[].class); - } + @Test + void orWithNullValueOnRight() { + assertThatExceptionOfType(EvaluationException.class).isThrownBy( + parser.parseExpression("false or null")::getValue); + } - @Test - public void methodCallWithRootReferenceThroughParameterThatIsAFunctionCall() { - evaluate("placeOfBirth.doubleIt(#reverseInt(inventions.length,2,3)[2])", 18, Integer.class); } - @Test - public void testIndexer03() { - evaluate("'christian'[8]", "n", String.class); - } + @Nested + class TernaryOperatorTests { - @Test - public void testIndexerError() { - evaluateAndCheckError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]", - SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE); - } + @Test + void ternaryOperator01() { + evaluate("2>4?1:2", 2, Integer.class); + } - @Test - public void testStaticRef02() { - evaluate("T(java.awt.Color).green.getRGB() != 0", true, Boolean.class); - evaluate("(T(java.lang.Math).random() * 100.0 ) > 0", true, Boolean.class); - evaluate("(T(Math).random() * 100.0) > 0", true, Boolean.class); - evaluate("T(Character).isUpperCase('Test'.charAt(0)) ? 'uppercase' : 'lowercase'", "uppercase", String.class); - evaluate("T(Character).isUpperCase('Test'.charAt(1)) ? 'uppercase' : 'lowercase'", "lowercase", String.class); - } + @Test + void ternaryOperator02() { + evaluate("'abc'=='abc'?1:2", 1, Integer.class); + } - // variables and functions - @Test - public void testVariableAccess01() { - evaluate("#answer", "42", Integer.class, true); - } + @Test + void ternaryOperator03() { + // cannot convert String to boolean + evaluateAndCheckError("'hello'?1:2", SpelMessage.TYPE_CONVERSION_ERROR); + } - @Test - public void testFunctionAccess01() { - evaluate("#reverseInt(1,2,3)", "int[3]{3,2,1}", int[].class); - } + @Test + void ternaryOperator04() { + Expression e = parser.parseExpression("1>2?3:4"); + assertThat(e.isWritable(context)).isFalse(); + } - @Test - public void testFunctionAccess02() { - evaluate("#reverseString('hello')", "olleh", String.class); - } + @Test + void ternaryOperator05() { + evaluate("1>2?#var=4:#var=5", 5, Integer.class); + evaluate("3?:#var=5", 3, Integer.class); + evaluate("null?:#var=5", 5, Integer.class); + evaluate("2>4?(3>2?true:false):(5<3?true:false)", false, Boolean.class); + } - // type references - @Test - public void testTypeReferences01() { - evaluate("T(java.lang.String)", "class java.lang.String", Class.class); - } + @Test + void ternaryOperatorWithNullValue() { + assertThatExceptionOfType(EvaluationException.class).isThrownBy( + parser.parseExpression("null ? 0 : 1")::getValue); + } - @Test - public void testTypeReferencesAndQualifiedIdentifierCaching() { - SpelExpression e = (SpelExpression) parser.parseExpression("T(java.lang.String)"); - assertThat(e.isWritable(new StandardEvaluationContext())).isFalse(); - assertThat(e.toStringAST()).isEqualTo("T(java.lang.String)"); - assertThat(e.getValue(Class.class)).isEqualTo(String.class); - // use cached QualifiedIdentifier: - assertThat(e.toStringAST()).isEqualTo("T(java.lang.String)"); - assertThat(e.getValue(Class.class)).isEqualTo(String.class); } - @Test - public void operatorVariants() { - SpelExpression e = (SpelExpression)parser.parseExpression("#a < #b"); - EvaluationContext ctx = new StandardEvaluationContext(); - ctx.setVariable("a", (short) 3); - ctx.setVariable("b", (short) 6); - assertThat(e.getValue(ctx, Boolean.class)).isTrue(); - ctx.setVariable("b", (byte) 6); - assertThat(e.getValue(ctx, Boolean.class)).isTrue(); - ctx.setVariable("a", (byte) 9); - ctx.setVariable("b", (byte) 6); - assertThat(e.getValue(ctx, Boolean.class)).isFalse(); - ctx.setVariable("a", 10L); - ctx.setVariable("b", (short) 30); - assertThat(e.getValue(ctx, Boolean.class)).isTrue(); - ctx.setVariable("a", (byte) 3); - ctx.setVariable("b", (short) 30); - assertThat(e.getValue(ctx, Boolean.class)).isTrue(); - ctx.setVariable("a", (byte) 3); - ctx.setVariable("b", 30L); - assertThat(e.getValue(ctx, Boolean.class)).isTrue(); - ctx.setVariable("a", (byte) 3); - ctx.setVariable("b", 30f); - assertThat(e.getValue(ctx, Boolean.class)).isTrue(); - ctx.setVariable("a", new BigInteger("10")); - ctx.setVariable("b", new BigInteger("20")); - assertThat(e.getValue(ctx, Boolean.class)).isTrue(); - } + @Nested + class MethodConstructorAndFunctionInvocationTests { - @Test - public void testTypeReferencesPrimitive() { - evaluate("T(int)", "int", Class.class); - evaluate("T(byte)", "byte", Class.class); - evaluate("T(char)", "char", Class.class); - evaluate("T(boolean)", "boolean", Class.class); - evaluate("T(long)", "long", Class.class); - evaluate("T(short)", "short", Class.class); - evaluate("T(double)", "double", Class.class); - evaluate("T(float)", "float", Class.class); - } + @Test + void methodCallWithRootReferenceThroughParameter() { + evaluate("placeOfBirth.doubleIt(inventions.length)", 18, Integer.class); + } - @Test - public void testTypeReferences02() { - evaluate("T(String)", "class java.lang.String", Class.class); - } + @Test + void ctorCallWithRootReferenceThroughParameter() { + evaluate("new org.springframework.expression.spel.testresources.PlaceOfBirth(inventions[0].toString()).city", + "Telephone repeater", String.class); + } - @Test - public void testStringType() { - evaluateAndAskForReturnType("getPlaceOfBirth().getCity()", "SmilJan", String.class); - } + @Test + void fnCallWithRootReferenceThroughParameter() { + evaluate("#reverseInt(inventions.length, inventions.length, inventions.length)", "int[3]{9,9,9}", int[].class); + } - @Test - public void testNumbers01() { - evaluateAndAskForReturnType("3*4+5", 17, Integer.class); - evaluateAndAskForReturnType("3*4+5", 17L, Long.class); - evaluateAndAskForReturnType("65", 'A', Character.class); - evaluateAndAskForReturnType("3*4+5", (short) 17, Short.class); - evaluateAndAskForReturnType("3*4+5", "17", String.class); - } + @Test + void methodCallWithRootReferenceThroughParameterThatIsAFunctionCall() { + evaluate("placeOfBirth.doubleIt(#reverseInt(inventions.length,2,3)[2])", 18, Integer.class); + } - @Test - public void testAdvancedNumerics() { - int twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Integer.class); - assertThat(twentyFour).isEqualTo(24); - double one = parser.parseExpression("8.0 / 5e0 % 2").getValue(Double.class); - assertThat((float) one).isCloseTo((float) 1.6d, within((float) 0d)); - int o = parser.parseExpression("8.0 / 5e0 % 2").getValue(Integer.class); - assertThat(o).isEqualTo(1); - int sixteen = parser.parseExpression("-2 ^ 4").getValue(Integer.class); - assertThat(sixteen).isEqualTo(16); - int minusFortyFive = parser.parseExpression("1+2-3*8^2/2/2").getValue(Integer.class); - assertThat(minusFortyFive).isEqualTo(-45); } - @Test - public void testComparison() { - EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); - boolean trueValue = parser.parseExpression("T(java.util.Date) == Birthdate.Class").getValue( - context, Boolean.class); - assertThat(trueValue).isTrue(); - } + @Nested + class VariableAndFunctionAccessTests { - @Test - public void testResolvingList() { - StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); - assertThatExceptionOfType(EvaluationException.class).isThrownBy(() -> - parser.parseExpression("T(List)!=null").getValue(context, Boolean.class)); - ((StandardTypeLocator) context.getTypeLocator()).registerImport("java.util"); - assertThat(parser.parseExpression("T(List)!=null").getValue(context, Boolean.class)).isTrue(); - } + @Test + void variableAccess() { + evaluate("#answer", "42", Integer.class, true); + } - @Test - public void testResolvingString() { - Class stringClass = parser.parseExpression("T(String)").getValue(Class.class); - assertThat(stringClass).isEqualTo(String.class); - } + @Test + void functionAccess() { + evaluate("#reverseInt(1,2,3)", "int[3]{3,2,1}", int[].class); + evaluate("#reverseString('hello')", "olleh", String.class); + } - /** - * SPR-6984: attempting to index a collection on write using an index that - * doesn't currently exist in the collection (address.crossStreets[0] below) - */ - @Test - public void initializingCollectionElementsOnWrite() { - TestPerson person = new TestPerson(); - EvaluationContext context = new StandardEvaluationContext(person); - SpelParserConfiguration config = new SpelParserConfiguration(true, true); - ExpressionParser parser = new SpelExpressionParser(config); - Expression e = parser.parseExpression("name"); - e.setValue(context, "Oleg"); - assertThat(person.getName()).isEqualTo("Oleg"); - - e = parser.parseExpression("address.street"); - e.setValue(context, "123 High St"); - assertThat(person.getAddress().getStreet()).isEqualTo("123 High St"); - - e = parser.parseExpression("address.crossStreets[0]"); - e.setValue(context, "Blah"); - assertThat(person.getAddress().getCrossStreets().get(0)).isEqualTo("Blah"); - - e = parser.parseExpression("address.crossStreets[3]"); - e.setValue(context, "Wibble"); - assertThat(person.getAddress().getCrossStreets().get(0)).isEqualTo("Blah"); - assertThat(person.getAddress().getCrossStreets().get(3)).isEqualTo("Wibble"); } - /** - * Verifies behavior requested in SPR-9613. - */ - @Test - public void caseInsensitiveNullLiterals() { - ExpressionParser parser = new SpelExpressionParser(); + @Nested + class TypeReferenceTests { - Expression e = parser.parseExpression("null"); - assertThat(e.getValue()).isNull(); + @Test + void typeReferences() { + evaluate("T(java.lang.String)", "class java.lang.String", Class.class); + evaluate("T(String)", "class java.lang.String", Class.class); + } - e = parser.parseExpression("NULL"); - assertThat(e.getValue()).isNull(); + @Test + void typeReferencesAndQualifiedIdentifierCaching() { + SpelExpression e = (SpelExpression) parser.parseExpression("T(java.lang.String)"); + assertThat(e.isWritable(new StandardEvaluationContext())).isFalse(); + assertThat(e.toStringAST()).isEqualTo("T(java.lang.String)"); + assertThat(e.getValue(Class.class)).isEqualTo(String.class); + // use cached QualifiedIdentifier: + assertThat(e.toStringAST()).isEqualTo("T(java.lang.String)"); + assertThat(e.getValue(Class.class)).isEqualTo(String.class); + } - e = parser.parseExpression("NuLl"); - assertThat(e.getValue()).isNull(); - } + @Test + void typeReferencesPrimitive() { + evaluate("T(int)", "int", Class.class); + evaluate("T(byte)", "byte", Class.class); + evaluate("T(char)", "char", Class.class); + evaluate("T(boolean)", "boolean", Class.class); + evaluate("T(long)", "long", Class.class); + evaluate("T(short)", "short", Class.class); + evaluate("T(double)", "double", Class.class); + evaluate("T(float)", "float", Class.class); + } - /** - * Verifies behavior requested in SPR-9621. - */ - @Test - public void customMethodFilter() { - StandardEvaluationContext context = new StandardEvaluationContext(); - - // Register a custom MethodResolver... - List customResolvers = new ArrayList<>(); - customResolvers.add(new CustomMethodResolver()); - context.setMethodResolvers(customResolvers); - - // or simply... - // context.setMethodResolvers(new ArrayList()); - - // Register a custom MethodFilter... - MethodFilter filter = new CustomMethodFilter(); - assertThatIllegalStateException().isThrownBy(() -> - context.registerMethodFilter(String.class, filter)) - .withMessage("Method filter cannot be set as the reflective method resolver is not in use"); - } + @Test + void staticMethodReferences() { + evaluate("T(java.awt.Color).green.getRGB() != 0", true, Boolean.class); + evaluate("(T(java.lang.Math).random() * 100.0 ) > 0", true, Boolean.class); + evaluate("(T(Math).random() * 100.0) > 0", true, Boolean.class); + evaluate("T(Character).isUpperCase('Test'.charAt(0)) ? 'uppercase' : 'lowercase'", "uppercase", String.class); + evaluate("T(Character).isUpperCase('Test'.charAt(1)) ? 'uppercase' : 'lowercase'", "lowercase", String.class); + } - /** - * This test is checking that with the changes for 9751 that the refactoring in Indexer is - * coping correctly for references beyond collection boundaries. - */ - @Test - public void collectionGrowingViaIndexer() { - Spr9751 instance = new Spr9751(); - - // Add a new element to the list - StandardEvaluationContext ctx = new StandardEvaluationContext(instance); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e = parser.parseExpression("listOfStrings[++index3]='def'"); - e.getValue(ctx); - assertThat(instance.listOfStrings.size()).isEqualTo(2); - assertThat(instance.listOfStrings.get(1)).isEqualTo("def"); - - // Check reference beyond end of collection - ctx = new StandardEvaluationContext(instance); - parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - e = parser.parseExpression("listOfStrings[0]"); - String value = e.getValue(ctx, String.class); - assertThat(value).isEqualTo("abc"); - e = parser.parseExpression("listOfStrings[1]"); - value = e.getValue(ctx, String.class); - assertThat(value).isEqualTo("def"); - e = parser.parseExpression("listOfStrings[2]"); - value = e.getValue(ctx, String.class); - assertThat(value).isEqualTo(""); - - // Now turn off growing and reference off the end - StandardEvaluationContext failCtx = new StandardEvaluationContext(instance); - parser = new SpelExpressionParser(new SpelParserConfiguration(false, false)); - Expression failExp = parser.parseExpression("listOfStrings[3]"); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - failExp.getValue(failCtx, String.class)) - .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS)); } - @Test - public void limitCollectionGrowing() { - TestClass instance = new TestClass(); - StandardEvaluationContext ctx = new StandardEvaluationContext(instance); - SpelExpressionParser parser = new SpelExpressionParser( new SpelParserConfiguration(true, true, 3)); - Expression e = parser.parseExpression("foo[2]"); - e.setValue(ctx, "2"); - assertThat(instance.getFoo().size()).isEqualTo(3); - e = parser.parseExpression("foo[3]"); - try { - e.setValue(ctx, "3"); - } - catch (SpelEvaluationException see) { - assertThat(see.getMessageCode()).isEqualTo(SpelMessage.UNABLE_TO_GROW_COLLECTION); - assertThat(instance.getFoo().size()).isEqualTo(3); - } - } + @Nested + class IncrementAndDecrementTests { - // For now I am making #this not assignable - @Test - public void increment01root() { - Integer i = 42; - StandardEvaluationContext ctx = new StandardEvaluationContext(i); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e = parser.parseExpression("#this++"); - assertThat(i.intValue()).isEqualTo(42); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - e.getValue(ctx, Integer.class)) + // For now I am making #this not assignable + @Test + void increment01root() { + Integer i = 42; + StandardEvaluationContext ctx = new StandardEvaluationContext(i); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e = parser.parseExpression("#this++"); + assertThat(i.intValue()).isEqualTo(42); + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + e.getValue(ctx, Integer.class)) .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.NOT_ASSIGNABLE)); - } + } - @Test - public void increment02postfix() { - Spr9751 helper = new Spr9751(); - StandardEvaluationContext ctx = new StandardEvaluationContext(helper); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e; - - // BigDecimal - e = parser.parseExpression("bd++"); - assertThat(new BigDecimal("2").equals(helper.bd)).isTrue(); - BigDecimal return_bd = e.getValue(ctx, BigDecimal.class); - assertThat(new BigDecimal("2").equals(return_bd)).isTrue(); - assertThat(new BigDecimal("3").equals(helper.bd)).isTrue(); - - // double - e = parser.parseExpression("ddd++"); - assertThat((float) helper.ddd).isCloseTo((float) 2.0d, within((float) 0d)); - double return_ddd = e.getValue(ctx, Double.TYPE); - assertThat((float) return_ddd).isCloseTo((float) 2.0d, within((float) 0d)); - assertThat((float) helper.ddd).isCloseTo((float) 3.0d, within((float) 0d)); - - // float - e = parser.parseExpression("fff++"); - assertThat(helper.fff).isCloseTo(3.0f, within((float) 0d)); - float return_fff = e.getValue(ctx, Float.TYPE); - assertThat(return_fff).isCloseTo(3.0f, within((float) 0d)); - assertThat(helper.fff).isCloseTo(4.0f, within((float) 0d)); - - // long - e = parser.parseExpression("lll++"); - assertThat(helper.lll).isEqualTo(66666L); - long return_lll = e.getValue(ctx, Long.TYPE); - assertThat(return_lll).isEqualTo(66666L); - assertThat(helper.lll).isEqualTo(66667L); - - // int - e = parser.parseExpression("iii++"); - assertThat(helper.iii).isEqualTo(42); - int return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_iii).isEqualTo(42); - assertThat(helper.iii).isEqualTo(43); - return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_iii).isEqualTo(43); - assertThat(helper.iii).isEqualTo(44); - - // short - e = parser.parseExpression("sss++"); - assertThat(helper.sss).isEqualTo((short) 15); - short return_sss = e.getValue(ctx, Short.TYPE); - assertThat(return_sss).isEqualTo((short) 15); - assertThat(helper.sss).isEqualTo((short) 16); - } + @Test + void increment02postfix() { + Spr9751 helper = new Spr9751(); + StandardEvaluationContext ctx = new StandardEvaluationContext(helper); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e; + + // BigDecimal + e = parser.parseExpression("bd++"); + assertThat(new BigDecimal("2").equals(helper.bd)).isTrue(); + BigDecimal return_bd = e.getValue(ctx, BigDecimal.class); + assertThat(new BigDecimal("2").equals(return_bd)).isTrue(); + assertThat(new BigDecimal("3").equals(helper.bd)).isTrue(); + + // double + e = parser.parseExpression("ddd++"); + assertThat((float) helper.ddd).isCloseTo((float) 2.0d, within((float) 0d)); + double return_ddd = e.getValue(ctx, Double.TYPE); + assertThat((float) return_ddd).isCloseTo((float) 2.0d, within((float) 0d)); + assertThat((float) helper.ddd).isCloseTo((float) 3.0d, within((float) 0d)); + + // float + e = parser.parseExpression("fff++"); + assertThat(helper.fff).isCloseTo(3.0f, within((float) 0d)); + float return_fff = e.getValue(ctx, Float.TYPE); + assertThat(return_fff).isCloseTo(3.0f, within((float) 0d)); + assertThat(helper.fff).isCloseTo(4.0f, within((float) 0d)); + + // long + e = parser.parseExpression("lll++"); + assertThat(helper.lll).isEqualTo(66666L); + long return_lll = e.getValue(ctx, Long.TYPE); + assertThat(return_lll).isEqualTo(66666L); + assertThat(helper.lll).isEqualTo(66667L); + + // int + e = parser.parseExpression("iii++"); + assertThat(helper.iii).isEqualTo(42); + int return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_iii).isEqualTo(42); + assertThat(helper.iii).isEqualTo(43); + return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_iii).isEqualTo(43); + assertThat(helper.iii).isEqualTo(44); + + // short + e = parser.parseExpression("sss++"); + assertThat(helper.sss).isEqualTo((short) 15); + short return_sss = e.getValue(ctx, Short.TYPE); + assertThat(return_sss).isEqualTo((short) 15); + assertThat(helper.sss).isEqualTo((short) 16); + } - @Test - public void increment02prefix() { - Spr9751 helper = new Spr9751(); - StandardEvaluationContext ctx = new StandardEvaluationContext(helper); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e; - - - // BigDecimal - e = parser.parseExpression("++bd"); - assertThat(new BigDecimal("2").equals(helper.bd)).isTrue(); - BigDecimal return_bd = e.getValue(ctx, BigDecimal.class); - assertThat(new BigDecimal("3").equals(return_bd)).isTrue(); - assertThat(new BigDecimal("3").equals(helper.bd)).isTrue(); - - // double - e = parser.parseExpression("++ddd"); - assertThat((float) helper.ddd).isCloseTo((float) 2.0d, within((float) 0d)); - double return_ddd = e.getValue(ctx, Double.TYPE); - assertThat((float) return_ddd).isCloseTo((float) 3.0d, within((float) 0d)); - assertThat((float) helper.ddd).isCloseTo((float) 3.0d, within((float) 0d)); - - // float - e = parser.parseExpression("++fff"); - assertThat(helper.fff).isCloseTo(3.0f, within((float) 0d)); - float return_fff = e.getValue(ctx, Float.TYPE); - assertThat(return_fff).isCloseTo(4.0f, within((float) 0d)); - assertThat(helper.fff).isCloseTo(4.0f, within((float) 0d)); - - // long - e = parser.parseExpression("++lll"); - assertThat(helper.lll).isEqualTo(66666L); - long return_lll = e.getValue(ctx, Long.TYPE); - assertThat(return_lll).isEqualTo(66667L); - assertThat(helper.lll).isEqualTo(66667L); - - // int - e = parser.parseExpression("++iii"); - assertThat(helper.iii).isEqualTo(42); - int return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_iii).isEqualTo(43); - assertThat(helper.iii).isEqualTo(43); - return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_iii).isEqualTo(44); - assertThat(helper.iii).isEqualTo(44); - - // short - e = parser.parseExpression("++sss"); - assertThat(helper.sss).isEqualTo((short) 15); - int return_sss = (Integer) e.getValue(ctx); - assertThat(return_sss).isEqualTo((short) 16); - assertThat(helper.sss).isEqualTo((short) 16); - } + @Test + void increment02prefix() { + Spr9751 helper = new Spr9751(); + StandardEvaluationContext ctx = new StandardEvaluationContext(helper); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e; + + // BigDecimal + e = parser.parseExpression("++bd"); + assertThat(new BigDecimal("2").equals(helper.bd)).isTrue(); + BigDecimal return_bd = e.getValue(ctx, BigDecimal.class); + assertThat(new BigDecimal("3").equals(return_bd)).isTrue(); + assertThat(new BigDecimal("3").equals(helper.bd)).isTrue(); + + // double + e = parser.parseExpression("++ddd"); + assertThat((float) helper.ddd).isCloseTo((float) 2.0d, within((float) 0d)); + double return_ddd = e.getValue(ctx, Double.TYPE); + assertThat((float) return_ddd).isCloseTo((float) 3.0d, within((float) 0d)); + assertThat((float) helper.ddd).isCloseTo((float) 3.0d, within((float) 0d)); + + // float + e = parser.parseExpression("++fff"); + assertThat(helper.fff).isCloseTo(3.0f, within((float) 0d)); + float return_fff = e.getValue(ctx, Float.TYPE); + assertThat(return_fff).isCloseTo(4.0f, within((float) 0d)); + assertThat(helper.fff).isCloseTo(4.0f, within((float) 0d)); + + // long + e = parser.parseExpression("++lll"); + assertThat(helper.lll).isEqualTo(66666L); + long return_lll = e.getValue(ctx, Long.TYPE); + assertThat(return_lll).isEqualTo(66667L); + assertThat(helper.lll).isEqualTo(66667L); + + // int + e = parser.parseExpression("++iii"); + assertThat(helper.iii).isEqualTo(42); + int return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_iii).isEqualTo(43); + assertThat(helper.iii).isEqualTo(43); + return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_iii).isEqualTo(44); + assertThat(helper.iii).isEqualTo(44); + + // short + e = parser.parseExpression("++sss"); + assertThat(helper.sss).isEqualTo((short) 15); + int return_sss = (Integer) e.getValue(ctx); + assertThat(return_sss).isEqualTo((short) 16); + assertThat(helper.sss).isEqualTo((short) 16); + } - @Test - public void increment03() { - Spr9751 helper = new Spr9751(); - StandardEvaluationContext ctx = new StandardEvaluationContext(helper); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + @Test + void increment03() { + Spr9751 helper = new Spr9751(); + StandardEvaluationContext ctx = new StandardEvaluationContext(helper); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e1 = parser.parseExpression("m()++"); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - e1.getValue(ctx, Double.TYPE)) + Expression e1 = parser.parseExpression("m()++"); + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + e1.getValue(ctx, Double.TYPE)) .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERAND_NOT_INCREMENTABLE)); - Expression e2 = parser.parseExpression("++m()"); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - e2.getValue(ctx, Double.TYPE)) + Expression e2 = parser.parseExpression("++m()"); + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + e2.getValue(ctx, Double.TYPE)) .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERAND_NOT_INCREMENTABLE)); - } + } - @Test - public void increment04() { - Integer i = 42; - StandardEvaluationContext ctx = new StandardEvaluationContext(i); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e1 = parser.parseExpression("++1"); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - e1.getValue(ctx, Double.TYPE)) + @Test + void increment04() { + Integer i = 42; + StandardEvaluationContext ctx = new StandardEvaluationContext(i); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e1 = parser.parseExpression("++1"); + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + e1.getValue(ctx, Double.TYPE)) .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.NOT_ASSIGNABLE)); - Expression e2 = parser.parseExpression("1++"); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - e2.getValue(ctx, Double.TYPE)) + Expression e2 = parser.parseExpression("1++"); + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + e2.getValue(ctx, Double.TYPE)) .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.NOT_ASSIGNABLE)); - } + } - @Test - public void decrement01root() { - Integer i = 42; - StandardEvaluationContext ctx = new StandardEvaluationContext(i); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e = parser.parseExpression("#this--"); - assertThat(i.intValue()).isEqualTo(42); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - e.getValue(ctx, Integer.class)) + @Test + void decrement01root() { + Integer i = 42; + StandardEvaluationContext ctx = new StandardEvaluationContext(i); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e = parser.parseExpression("#this--"); + assertThat(i.intValue()).isEqualTo(42); + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + e.getValue(ctx, Integer.class)) .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.NOT_ASSIGNABLE)); - } + } - @Test - public void decrement02postfix() { - Spr9751 helper = new Spr9751(); - StandardEvaluationContext ctx = new StandardEvaluationContext(helper); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e; - - // BigDecimal - e = parser.parseExpression("bd--"); - assertThat(new BigDecimal("2").equals(helper.bd)).isTrue(); - BigDecimal return_bd = e.getValue(ctx,BigDecimal.class); - assertThat(new BigDecimal("2").equals(return_bd)).isTrue(); - assertThat(new BigDecimal("1").equals(helper.bd)).isTrue(); - - // double - e = parser.parseExpression("ddd--"); - assertThat((float) helper.ddd).isCloseTo((float) 2.0d, within((float) 0d)); - double return_ddd = e.getValue(ctx, Double.TYPE); - assertThat((float) return_ddd).isCloseTo((float) 2.0d, within((float) 0d)); - assertThat((float) helper.ddd).isCloseTo((float) 1.0d, within((float) 0d)); - - // float - e = parser.parseExpression("fff--"); - assertThat(helper.fff).isCloseTo(3.0f, within((float) 0d)); - float return_fff = e.getValue(ctx, Float.TYPE); - assertThat(return_fff).isCloseTo(3.0f, within((float) 0d)); - assertThat(helper.fff).isCloseTo(2.0f, within((float) 0d)); - - // long - e = parser.parseExpression("lll--"); - assertThat(helper.lll).isEqualTo(66666L); - long return_lll = e.getValue(ctx, Long.TYPE); - assertThat(return_lll).isEqualTo(66666L); - assertThat(helper.lll).isEqualTo(66665L); - - // int - e = parser.parseExpression("iii--"); - assertThat(helper.iii).isEqualTo(42); - int return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_iii).isEqualTo(42); - assertThat(helper.iii).isEqualTo(41); - return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_iii).isEqualTo(41); - assertThat(helper.iii).isEqualTo(40); - - // short - e = parser.parseExpression("sss--"); - assertThat(helper.sss).isEqualTo((short) 15); - short return_sss = e.getValue(ctx, Short.TYPE); - assertThat(return_sss).isEqualTo((short) 15); - assertThat(helper.sss).isEqualTo((short) 14); - } + @Test + void decrement02postfix() { + Spr9751 helper = new Spr9751(); + StandardEvaluationContext ctx = new StandardEvaluationContext(helper); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e; + + // BigDecimal + e = parser.parseExpression("bd--"); + assertThat(new BigDecimal("2").equals(helper.bd)).isTrue(); + BigDecimal return_bd = e.getValue(ctx,BigDecimal.class); + assertThat(new BigDecimal("2").equals(return_bd)).isTrue(); + assertThat(new BigDecimal("1").equals(helper.bd)).isTrue(); + + // double + e = parser.parseExpression("ddd--"); + assertThat((float) helper.ddd).isCloseTo((float) 2.0d, within((float) 0d)); + double return_ddd = e.getValue(ctx, Double.TYPE); + assertThat((float) return_ddd).isCloseTo((float) 2.0d, within((float) 0d)); + assertThat((float) helper.ddd).isCloseTo((float) 1.0d, within((float) 0d)); + + // float + e = parser.parseExpression("fff--"); + assertThat(helper.fff).isCloseTo(3.0f, within((float) 0d)); + float return_fff = e.getValue(ctx, Float.TYPE); + assertThat(return_fff).isCloseTo(3.0f, within((float) 0d)); + assertThat(helper.fff).isCloseTo(2.0f, within((float) 0d)); + + // long + e = parser.parseExpression("lll--"); + assertThat(helper.lll).isEqualTo(66666L); + long return_lll = e.getValue(ctx, Long.TYPE); + assertThat(return_lll).isEqualTo(66666L); + assertThat(helper.lll).isEqualTo(66665L); + + // int + e = parser.parseExpression("iii--"); + assertThat(helper.iii).isEqualTo(42); + int return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_iii).isEqualTo(42); + assertThat(helper.iii).isEqualTo(41); + return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_iii).isEqualTo(41); + assertThat(helper.iii).isEqualTo(40); + + // short + e = parser.parseExpression("sss--"); + assertThat(helper.sss).isEqualTo((short) 15); + short return_sss = e.getValue(ctx, Short.TYPE); + assertThat(return_sss).isEqualTo((short) 15); + assertThat(helper.sss).isEqualTo((short) 14); + } - @Test - public void decrement02prefix() { - Spr9751 helper = new Spr9751(); - StandardEvaluationContext ctx = new StandardEvaluationContext(helper); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e; - - // BigDecimal - e = parser.parseExpression("--bd"); - assertThat(new BigDecimal("2").equals(helper.bd)).isTrue(); - BigDecimal return_bd = e.getValue(ctx,BigDecimal.class); - assertThat(new BigDecimal("1").equals(return_bd)).isTrue(); - assertThat(new BigDecimal("1").equals(helper.bd)).isTrue(); - - // double - e = parser.parseExpression("--ddd"); - assertThat((float) helper.ddd).isCloseTo((float) 2.0d, within((float) 0d)); - double return_ddd = e.getValue(ctx, Double.TYPE); - assertThat((float) return_ddd).isCloseTo((float) 1.0d, within((float) 0d)); - assertThat((float) helper.ddd).isCloseTo((float) 1.0d, within((float) 0d)); - - // float - e = parser.parseExpression("--fff"); - assertThat(helper.fff).isCloseTo(3.0f, within((float) 0d)); - float return_fff = e.getValue(ctx, Float.TYPE); - assertThat(return_fff).isCloseTo(2.0f, within((float) 0d)); - assertThat(helper.fff).isCloseTo(2.0f, within((float) 0d)); - - // long - e = parser.parseExpression("--lll"); - assertThat(helper.lll).isEqualTo(66666L); - long return_lll = e.getValue(ctx, Long.TYPE); - assertThat(return_lll).isEqualTo(66665L); - assertThat(helper.lll).isEqualTo(66665L); - - // int - e = parser.parseExpression("--iii"); - assertThat(helper.iii).isEqualTo(42); - int return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_iii).isEqualTo(41); - assertThat(helper.iii).isEqualTo(41); - return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_iii).isEqualTo(40); - assertThat(helper.iii).isEqualTo(40); - - // short - e = parser.parseExpression("--sss"); - assertThat(helper.sss).isEqualTo((short) 15); - int return_sss = (Integer)e.getValue(ctx); - assertThat(return_sss).isEqualTo(14); - assertThat(helper.sss).isEqualTo((short) 14); - } + @Test + void decrement02prefix() { + Spr9751 helper = new Spr9751(); + StandardEvaluationContext ctx = new StandardEvaluationContext(helper); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e; + + // BigDecimal + e = parser.parseExpression("--bd"); + assertThat(new BigDecimal("2").equals(helper.bd)).isTrue(); + BigDecimal return_bd = e.getValue(ctx,BigDecimal.class); + assertThat(new BigDecimal("1").equals(return_bd)).isTrue(); + assertThat(new BigDecimal("1").equals(helper.bd)).isTrue(); + + // double + e = parser.parseExpression("--ddd"); + assertThat((float) helper.ddd).isCloseTo((float) 2.0d, within((float) 0d)); + double return_ddd = e.getValue(ctx, Double.TYPE); + assertThat((float) return_ddd).isCloseTo((float) 1.0d, within((float) 0d)); + assertThat((float) helper.ddd).isCloseTo((float) 1.0d, within((float) 0d)); + + // float + e = parser.parseExpression("--fff"); + assertThat(helper.fff).isCloseTo(3.0f, within((float) 0d)); + float return_fff = e.getValue(ctx, Float.TYPE); + assertThat(return_fff).isCloseTo(2.0f, within((float) 0d)); + assertThat(helper.fff).isCloseTo(2.0f, within((float) 0d)); + + // long + e = parser.parseExpression("--lll"); + assertThat(helper.lll).isEqualTo(66666L); + long return_lll = e.getValue(ctx, Long.TYPE); + assertThat(return_lll).isEqualTo(66665L); + assertThat(helper.lll).isEqualTo(66665L); + + // int + e = parser.parseExpression("--iii"); + assertThat(helper.iii).isEqualTo(42); + int return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_iii).isEqualTo(41); + assertThat(helper.iii).isEqualTo(41); + return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_iii).isEqualTo(40); + assertThat(helper.iii).isEqualTo(40); + + // short + e = parser.parseExpression("--sss"); + assertThat(helper.sss).isEqualTo((short) 15); + int return_sss = (Integer)e.getValue(ctx); + assertThat(return_sss).isEqualTo(14); + assertThat(helper.sss).isEqualTo((short) 14); + } - @Test - public void decrement03() { - Spr9751 helper = new Spr9751(); - StandardEvaluationContext ctx = new StandardEvaluationContext(helper); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + @Test + void decrement03() { + Spr9751 helper = new Spr9751(); + StandardEvaluationContext ctx = new StandardEvaluationContext(helper); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e1 = parser.parseExpression("m()--"); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - e1.getValue(ctx, Double.TYPE)) + Expression e1 = parser.parseExpression("m()--"); + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + e1.getValue(ctx, Double.TYPE)) .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERAND_NOT_DECREMENTABLE)); - Expression e2 = parser.parseExpression("--m()"); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - e2.getValue(ctx, Double.TYPE)) + Expression e2 = parser.parseExpression("--m()"); + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + e2.getValue(ctx, Double.TYPE)) .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERAND_NOT_DECREMENTABLE)); - } - + } - @Test - public void decrement04() { - Integer i = 42; - StandardEvaluationContext ctx = new StandardEvaluationContext(i); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e1 = parser.parseExpression("--1"); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - e1.getValue(ctx, Integer.class)) + @Test + void decrement04() { + Integer i = 42; + StandardEvaluationContext ctx = new StandardEvaluationContext(i); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e1 = parser.parseExpression("--1"); + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + e1.getValue(ctx, Integer.class)) .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.NOT_ASSIGNABLE)); - Expression e2 = parser.parseExpression("1--"); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> - e2.getValue(ctx, Integer.class)) + Expression e2 = parser.parseExpression("1--"); + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> + e2.getValue(ctx, Integer.class)) .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.NOT_ASSIGNABLE)); - } - - @Test - public void incdecTogether() { - Spr9751 helper = new Spr9751(); - StandardEvaluationContext ctx = new StandardEvaluationContext(helper); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e; - - // index1 is 2 at the start - the 'intArray[#root.index1++]' should not be evaluated twice! - // intArray[2] is 3 - e = parser.parseExpression("intArray[#root.index1++]++"); - e.getValue(ctx, Integer.class); - assertThat(helper.index1).isEqualTo(3); - assertThat(helper.intArray[2]).isEqualTo(4); - - // index1 is 3 intArray[3] is 4 - e = parser.parseExpression("intArray[#root.index1++]--"); - assertThat(e.getValue(ctx, Integer.class).intValue()).isEqualTo(4); - assertThat(helper.index1).isEqualTo(4); - assertThat(helper.intArray[3]).isEqualTo(3); - - // index1 is 4, intArray[3] is 3 - e = parser.parseExpression("intArray[--#root.index1]++"); - assertThat(e.getValue(ctx, Integer.class).intValue()).isEqualTo(3); - assertThat(helper.index1).isEqualTo(3); - assertThat(helper.intArray[3]).isEqualTo(4); - } - - - // Verify how all the nodes behave with assignment (++, --, =) - @Test - public void incrementAllNodeTypes() throws SecurityException, NoSuchMethodException { - Spr9751 helper = new Spr9751(); - StandardEvaluationContext ctx = new StandardEvaluationContext(helper); - ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression e; - - // BooleanLiteral - expectFailNotAssignable(parser, ctx, "true++"); - expectFailNotAssignable(parser, ctx, "--false"); - expectFailSetValueNotSupported(parser, ctx, "true=false"); - - // IntLiteral - expectFailNotAssignable(parser, ctx, "12++"); - expectFailNotAssignable(parser, ctx, "--1222"); - expectFailSetValueNotSupported(parser, ctx, "12=16"); - - // LongLiteral - expectFailNotAssignable(parser, ctx, "1.0d++"); - expectFailNotAssignable(parser, ctx, "--3.4d"); - expectFailSetValueNotSupported(parser, ctx, "1.0d=3.2d"); - - // NullLiteral - expectFailNotAssignable(parser, ctx, "null++"); - expectFailNotAssignable(parser, ctx, "--null"); - expectFailSetValueNotSupported(parser, ctx, "null=null"); - expectFailSetValueNotSupported(parser, ctx, "null=123"); - - // OpAnd - expectFailNotAssignable(parser, ctx, "(true && false)++"); - expectFailNotAssignable(parser, ctx, "--(false AND true)"); - expectFailSetValueNotSupported(parser, ctx, "(true && false)=(false && true)"); - - // OpDivide - expectFailNotAssignable(parser, ctx, "(3/4)++"); - expectFailNotAssignable(parser, ctx, "--(2/5)"); - expectFailSetValueNotSupported(parser, ctx, "(1/2)=(3/4)"); - - // OpEq - expectFailNotAssignable(parser, ctx, "(3==4)++"); - expectFailNotAssignable(parser, ctx, "--(2==5)"); - expectFailSetValueNotSupported(parser, ctx, "(1==2)=(3==4)"); - - // OpGE - expectFailNotAssignable(parser, ctx, "(3>=4)++"); - expectFailNotAssignable(parser, ctx, "--(2>=5)"); - expectFailSetValueNotSupported(parser, ctx, "(1>=2)=(3>=4)"); - - // OpGT - expectFailNotAssignable(parser, ctx, "(3>4)++"); - expectFailNotAssignable(parser, ctx, "--(2>5)"); - expectFailSetValueNotSupported(parser, ctx, "(1>2)=(3>4)"); - - // OpLE - expectFailNotAssignable(parser, ctx, "(3<=4)++"); - expectFailNotAssignable(parser, ctx, "--(2<=5)"); - expectFailSetValueNotSupported(parser, ctx, "(1<=2)=(3<=4)"); - - // OpLT - expectFailNotAssignable(parser, ctx, "(3<4)++"); - expectFailNotAssignable(parser, ctx, "--(2<5)"); - expectFailSetValueNotSupported(parser, ctx, "(1<2)=(3<4)"); - - // OpMinus - expectFailNotAssignable(parser, ctx, "(3-4)++"); - expectFailNotAssignable(parser, ctx, "--(2-5)"); - expectFailSetValueNotSupported(parser, ctx, "(1-2)=(3-4)"); - - // OpModulus - expectFailNotAssignable(parser, ctx, "(3%4)++"); - expectFailNotAssignable(parser, ctx, "--(2%5)"); - expectFailSetValueNotSupported(parser, ctx, "(1%2)=(3%4)"); - - // OpMultiply - expectFailNotAssignable(parser, ctx, "(3*4)++"); - expectFailNotAssignable(parser, ctx, "--(2*5)"); - expectFailSetValueNotSupported(parser, ctx, "(1*2)=(3*4)"); - - // OpNE - expectFailNotAssignable(parser, ctx, "(3!=4)++"); - expectFailNotAssignable(parser, ctx, "--(2!=5)"); - expectFailSetValueNotSupported(parser, ctx, "(1!=2)=(3!=4)"); - - // OpOr - expectFailNotAssignable(parser, ctx, "(true || false)++"); - expectFailNotAssignable(parser, ctx, "--(false OR true)"); - expectFailSetValueNotSupported(parser, ctx, "(true || false)=(false OR true)"); - - // OpPlus - expectFailNotAssignable(parser, ctx, "(3+4)++"); - expectFailNotAssignable(parser, ctx, "--(2+5)"); - expectFailSetValueNotSupported(parser, ctx, "(1+2)=(3+4)"); - - // RealLiteral - expectFailNotAssignable(parser, ctx, "1.0d++"); - expectFailNotAssignable(parser, ctx, "--2.0d"); - expectFailSetValueNotSupported(parser, ctx, "(1.0d)=(3.0d)"); - expectFailNotAssignable(parser, ctx, "1.0f++"); - expectFailNotAssignable(parser, ctx, "--2.0f"); - expectFailSetValueNotSupported(parser, ctx, "(1.0f)=(3.0f)"); - - // StringLiteral - expectFailNotAssignable(parser, ctx, "'abc'++"); - expectFailNotAssignable(parser, ctx, "--'def'"); - expectFailSetValueNotSupported(parser, ctx, "'abc'='def'"); - - // Ternary - expectFailNotAssignable(parser, ctx, "(true?true:false)++"); - expectFailNotAssignable(parser, ctx, "--(true?true:false)"); - expectFailSetValueNotSupported(parser, ctx, "(true?true:false)=(true?true:false)"); - - // TypeReference - expectFailNotAssignable(parser, ctx, "T(String)++"); - expectFailNotAssignable(parser, ctx, "--T(Integer)"); - expectFailSetValueNotSupported(parser, ctx, "T(String)=T(Integer)"); - - // OperatorBetween - expectFailNotAssignable(parser, ctx, "(3 between {1,5})++"); - expectFailNotAssignable(parser, ctx, "--(3 between {1,5})"); - expectFailSetValueNotSupported(parser, ctx, "(3 between {1,5})=(3 between {1,5})"); - - // OperatorInstanceOf - expectFailNotAssignable(parser, ctx, "(type instanceof T(String))++"); - expectFailNotAssignable(parser, ctx, "--(type instanceof T(String))"); - expectFailSetValueNotSupported(parser, ctx, "(type instanceof T(String))=(type instanceof T(String))"); - - // Elvis - expectFailNotAssignable(parser, ctx, "(true?:false)++"); - expectFailNotAssignable(parser, ctx, "--(true?:false)"); - expectFailSetValueNotSupported(parser, ctx, "(true?:false)=(true?:false)"); - - // OpInc - expectFailNotAssignable(parser, ctx, "(iii++)++"); - expectFailNotAssignable(parser, ctx, "--(++iii)"); - expectFailSetValueNotSupported(parser, ctx, "(iii++)=(++iii)"); - - // OpDec - expectFailNotAssignable(parser, ctx, "(iii--)++"); - expectFailNotAssignable(parser, ctx, "--(--iii)"); - expectFailSetValueNotSupported(parser, ctx, "(iii--)=(--iii)"); - - // OperatorNot - expectFailNotAssignable(parser, ctx, "(!true)++"); - expectFailNotAssignable(parser, ctx, "--(!false)"); - expectFailSetValueNotSupported(parser, ctx, "(!true)=(!false)"); - - // OperatorPower - expectFailNotAssignable(parser, ctx, "(iii^2)++"); - expectFailNotAssignable(parser, ctx, "--(iii^2)"); - expectFailSetValueNotSupported(parser, ctx, "(iii^2)=(iii^3)"); - - // Assign - // iii=42 - e = parser.parseExpression("iii=iii++"); - assertThat(helper.iii).isEqualTo(42); - int return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(helper.iii).isEqualTo(42); - assertThat(return_iii).isEqualTo(42); - - // Identifier - e = parser.parseExpression("iii++"); - assertThat(helper.iii).isEqualTo(42); - return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_iii).isEqualTo(42); - assertThat(helper.iii).isEqualTo(43); - - e = parser.parseExpression("--iii"); - assertThat(helper.iii).isEqualTo(43); - return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_iii).isEqualTo(42); - assertThat(helper.iii).isEqualTo(42); - - e = parser.parseExpression("iii=99"); - assertThat(helper.iii).isEqualTo(42); - return_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_iii).isEqualTo(99); - assertThat(helper.iii).isEqualTo(99); - - // CompoundExpression - // foo.iii == 99 - e = parser.parseExpression("foo.iii++"); - assertThat(helper.foo.iii).isEqualTo(99); - int return_foo_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_foo_iii).isEqualTo(99); - assertThat(helper.foo.iii).isEqualTo(100); - - e = parser.parseExpression("--foo.iii"); - assertThat(helper.foo.iii).isEqualTo(100); - return_foo_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_foo_iii).isEqualTo(99); - assertThat(helper.foo.iii).isEqualTo(99); - - e = parser.parseExpression("foo.iii=999"); - assertThat(helper.foo.iii).isEqualTo(99); - return_foo_iii = e.getValue(ctx, Integer.TYPE); - assertThat(return_foo_iii).isEqualTo(999); - assertThat(helper.foo.iii).isEqualTo(999); - - // ConstructorReference - expectFailNotAssignable(parser, ctx, "(new String('abc'))++"); - expectFailNotAssignable(parser, ctx, "--(new String('abc'))"); - expectFailSetValueNotSupported(parser, ctx, "(new String('abc'))=(new String('abc'))"); - - // MethodReference - expectFailNotIncrementable(parser, ctx, "m()++"); - expectFailNotDecrementable(parser, ctx, "--m()"); - expectFailSetValueNotSupported(parser, ctx, "m()=m()"); - - // OperatorMatches - expectFailNotAssignable(parser, ctx, "('abc' matches '^a..')++"); - expectFailNotAssignable(parser, ctx, "--('abc' matches '^a..')"); - expectFailSetValueNotSupported(parser, ctx, "('abc' matches '^a..')=('abc' matches '^a..')"); - - // Selection - ctx.registerFunction("isEven", Spr9751.class.getDeclaredMethod("isEven", Integer.TYPE)); - - expectFailNotIncrementable(parser, ctx, "({1,2,3}.?[#isEven(#this)])++"); - expectFailNotDecrementable(parser, ctx, "--({1,2,3}.?[#isEven(#this)])"); - expectFailNotAssignable(parser, ctx, "({1,2,3}.?[#isEven(#this)])=({1,2,3}.?[#isEven(#this)])"); - - // slightly diff here because return value isn't a list, it is a single entity - expectFailNotAssignable(parser, ctx, "({1,2,3}.^[#isEven(#this)])++"); - expectFailNotAssignable(parser, ctx, "--({1,2,3}.^[#isEven(#this)])"); - expectFailNotAssignable(parser, ctx, "({1,2,3}.^[#isEven(#this)])=({1,2,3}.^[#isEven(#this)])"); - - expectFailNotAssignable(parser, ctx, "({1,2,3}.$[#isEven(#this)])++"); - expectFailNotAssignable(parser, ctx, "--({1,2,3}.$[#isEven(#this)])"); - expectFailNotAssignable(parser, ctx, "({1,2,3}.$[#isEven(#this)])=({1,2,3}.$[#isEven(#this)])"); - - // FunctionReference - expectFailNotAssignable(parser, ctx, "#isEven(3)++"); - expectFailNotAssignable(parser, ctx, "--#isEven(4)"); - expectFailSetValueNotSupported(parser, ctx, "#isEven(3)=#isEven(5)"); - - // VariableReference - ctx.setVariable("wibble", "hello world"); - expectFailNotIncrementable(parser, ctx, "#wibble++"); - expectFailNotDecrementable(parser, ctx, "--#wibble"); - e = parser.parseExpression("#wibble=#wibble+#wibble"); - String s = e.getValue(ctx, String.class); - assertThat(s).isEqualTo("hello worldhello world"); - assertThat(ctx.lookupVariable("wibble")).isEqualTo("hello worldhello world"); - - ctx.setVariable("wobble", 3); - e = parser.parseExpression("#wobble++"); - assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(3); - int r = e.getValue(ctx, Integer.TYPE); - assertThat(r).isEqualTo(3); - assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(4); - - e = parser.parseExpression("--#wobble"); - assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(4); - r = e.getValue(ctx, Integer.TYPE); - assertThat(r).isEqualTo(3); - assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(3); - - e = parser.parseExpression("#wobble=34"); - assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(3); - r = e.getValue(ctx, Integer.TYPE); - assertThat(r).isEqualTo(34); - assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(34); - - // Projection - expectFailNotIncrementable(parser, ctx, "({1,2,3}.![#isEven(#this)])++"); // projection would be {false,true,false} - expectFailNotDecrementable(parser, ctx, "--({1,2,3}.![#isEven(#this)])"); // projection would be {false,true,false} - expectFailNotAssignable(parser, ctx, "({1,2,3}.![#isEven(#this)])=({1,2,3}.![#isEven(#this)])"); - - // InlineList - expectFailNotAssignable(parser, ctx, "({1,2,3})++"); - expectFailNotAssignable(parser, ctx, "--({1,2,3})"); - expectFailSetValueNotSupported(parser, ctx, "({1,2,3})=({1,2,3})"); - - // InlineMap - expectFailNotAssignable(parser, ctx, "({'a':1,'b':2,'c':3})++"); - expectFailNotAssignable(parser, ctx, "--({'a':1,'b':2,'c':3})"); - expectFailSetValueNotSupported(parser, ctx, "({'a':1,'b':2,'c':3})=({'a':1,'b':2,'c':3})"); - - // BeanReference - ctx.setBeanResolver(new MyBeanResolver()); - expectFailNotAssignable(parser, ctx, "@foo++"); - expectFailNotAssignable(parser, ctx, "--@foo"); - expectFailSetValueNotSupported(parser, ctx, "@foo=@bar"); - - // PropertyOrFieldReference - helper.iii = 42; - e = parser.parseExpression("iii++"); - assertThat(helper.iii).isEqualTo(42); - r = e.getValue(ctx, Integer.TYPE); - assertThat(r).isEqualTo(42); - assertThat(helper.iii).isEqualTo(43); - - e = parser.parseExpression("--iii"); - assertThat(helper.iii).isEqualTo(43); - r = e.getValue(ctx, Integer.TYPE); - assertThat(r).isEqualTo(42); - assertThat(helper.iii).isEqualTo(42); - - e = parser.parseExpression("iii=100"); - assertThat(helper.iii).isEqualTo(42); - r = e.getValue(ctx, Integer.TYPE); - assertThat(r).isEqualTo(100); - assertThat(helper.iii).isEqualTo(100); - } - - private void expectFailNotAssignable(ExpressionParser parser, EvaluationContext eContext, String expressionString) { - expectFail(parser, eContext, expressionString, SpelMessage.NOT_ASSIGNABLE); - } - - private void expectFailSetValueNotSupported(ExpressionParser parser, EvaluationContext eContext, String expressionString) { - expectFail(parser, eContext, expressionString, SpelMessage.SETVALUE_NOT_SUPPORTED); - } - - private void expectFailNotIncrementable(ExpressionParser parser, EvaluationContext eContext, String expressionString) { - expectFail(parser, eContext, expressionString, SpelMessage.OPERAND_NOT_INCREMENTABLE); - } + } - private void expectFailNotDecrementable(ExpressionParser parser, EvaluationContext eContext, String expressionString) { - expectFail(parser, eContext, expressionString, SpelMessage.OPERAND_NOT_DECREMENTABLE); - } + @Test + void incrementAndDecrementTogether() { + Spr9751 helper = new Spr9751(); + StandardEvaluationContext ctx = new StandardEvaluationContext(helper); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e; + + // index1 is 2 at the start - the 'intArray[#root.index1++]' should not be evaluated twice! + // intArray[2] is 3 + e = parser.parseExpression("intArray[#root.index1++]++"); + e.getValue(ctx, Integer.class); + assertThat(helper.index1).isEqualTo(3); + assertThat(helper.intArray[2]).isEqualTo(4); + + // index1 is 3 intArray[3] is 4 + e = parser.parseExpression("intArray[#root.index1++]--"); + assertThat(e.getValue(ctx, Integer.class).intValue()).isEqualTo(4); + assertThat(helper.index1).isEqualTo(4); + assertThat(helper.intArray[3]).isEqualTo(3); + + // index1 is 4, intArray[3] is 3 + e = parser.parseExpression("intArray[--#root.index1]++"); + assertThat(e.getValue(ctx, Integer.class).intValue()).isEqualTo(3); + assertThat(helper.index1).isEqualTo(3); + assertThat(helper.intArray[3]).isEqualTo(4); + } - private void expectFail(ExpressionParser parser, EvaluationContext eContext, String expressionString, SpelMessage messageCode) { - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> { - Expression e = parser.parseExpression(expressionString); - SpelUtilities.printAbstractSyntaxTree(System.out, e); - e.getValue(eContext); - }).satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(messageCode)); - } + // Verify how all the nodes behave with assignment (++, --, =) + @Test + void incrementAllNodeTypes() throws SecurityException, NoSuchMethodException { + Spr9751 helper = new Spr9751(); + StandardEvaluationContext ctx = new StandardEvaluationContext(helper); + ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression e; + + // BooleanLiteral + expectFailNotAssignable(parser, ctx, "true++"); + expectFailNotAssignable(parser, ctx, "--false"); + expectFailSetValueNotSupported(parser, ctx, "true=false"); + + // IntLiteral + expectFailNotAssignable(parser, ctx, "12++"); + expectFailNotAssignable(parser, ctx, "--1222"); + expectFailSetValueNotSupported(parser, ctx, "12=16"); + + // LongLiteral + expectFailNotAssignable(parser, ctx, "1.0d++"); + expectFailNotAssignable(parser, ctx, "--3.4d"); + expectFailSetValueNotSupported(parser, ctx, "1.0d=3.2d"); + + // NullLiteral + expectFailNotAssignable(parser, ctx, "null++"); + expectFailNotAssignable(parser, ctx, "--null"); + expectFailSetValueNotSupported(parser, ctx, "null=null"); + expectFailSetValueNotSupported(parser, ctx, "null=123"); + + // OpAnd + expectFailNotAssignable(parser, ctx, "(true && false)++"); + expectFailNotAssignable(parser, ctx, "--(false AND true)"); + expectFailSetValueNotSupported(parser, ctx, "(true && false)=(false && true)"); + + // OpDivide + expectFailNotAssignable(parser, ctx, "(3/4)++"); + expectFailNotAssignable(parser, ctx, "--(2/5)"); + expectFailSetValueNotSupported(parser, ctx, "(1/2)=(3/4)"); + + // OpEq + expectFailNotAssignable(parser, ctx, "(3==4)++"); + expectFailNotAssignable(parser, ctx, "--(2==5)"); + expectFailSetValueNotSupported(parser, ctx, "(1==2)=(3==4)"); + + // OpGE + expectFailNotAssignable(parser, ctx, "(3>=4)++"); + expectFailNotAssignable(parser, ctx, "--(2>=5)"); + expectFailSetValueNotSupported(parser, ctx, "(1>=2)=(3>=4)"); + + // OpGT + expectFailNotAssignable(parser, ctx, "(3>4)++"); + expectFailNotAssignable(parser, ctx, "--(2>5)"); + expectFailSetValueNotSupported(parser, ctx, "(1>2)=(3>4)"); + + // OpLE + expectFailNotAssignable(parser, ctx, "(3<=4)++"); + expectFailNotAssignable(parser, ctx, "--(2<=5)"); + expectFailSetValueNotSupported(parser, ctx, "(1<=2)=(3<=4)"); + + // OpLT + expectFailNotAssignable(parser, ctx, "(3<4)++"); + expectFailNotAssignable(parser, ctx, "--(2<5)"); + expectFailSetValueNotSupported(parser, ctx, "(1<2)=(3<4)"); + + // OpMinus + expectFailNotAssignable(parser, ctx, "(3-4)++"); + expectFailNotAssignable(parser, ctx, "--(2-5)"); + expectFailSetValueNotSupported(parser, ctx, "(1-2)=(3-4)"); + + // OpModulus + expectFailNotAssignable(parser, ctx, "(3%4)++"); + expectFailNotAssignable(parser, ctx, "--(2%5)"); + expectFailSetValueNotSupported(parser, ctx, "(1%2)=(3%4)"); + + // OpMultiply + expectFailNotAssignable(parser, ctx, "(3*4)++"); + expectFailNotAssignable(parser, ctx, "--(2*5)"); + expectFailSetValueNotSupported(parser, ctx, "(1*2)=(3*4)"); + + // OpNE + expectFailNotAssignable(parser, ctx, "(3!=4)++"); + expectFailNotAssignable(parser, ctx, "--(2!=5)"); + expectFailSetValueNotSupported(parser, ctx, "(1!=2)=(3!=4)"); + + // OpOr + expectFailNotAssignable(parser, ctx, "(true || false)++"); + expectFailNotAssignable(parser, ctx, "--(false OR true)"); + expectFailSetValueNotSupported(parser, ctx, "(true || false)=(false OR true)"); + + // OpPlus + expectFailNotAssignable(parser, ctx, "(3+4)++"); + expectFailNotAssignable(parser, ctx, "--(2+5)"); + expectFailSetValueNotSupported(parser, ctx, "(1+2)=(3+4)"); + + // RealLiteral + expectFailNotAssignable(parser, ctx, "1.0d++"); + expectFailNotAssignable(parser, ctx, "--2.0d"); + expectFailSetValueNotSupported(parser, ctx, "(1.0d)=(3.0d)"); + expectFailNotAssignable(parser, ctx, "1.0f++"); + expectFailNotAssignable(parser, ctx, "--2.0f"); + expectFailSetValueNotSupported(parser, ctx, "(1.0f)=(3.0f)"); + + // StringLiteral + expectFailNotAssignable(parser, ctx, "'abc'++"); + expectFailNotAssignable(parser, ctx, "--'def'"); + expectFailSetValueNotSupported(parser, ctx, "'abc'='def'"); + + // Ternary + expectFailNotAssignable(parser, ctx, "(true?true:false)++"); + expectFailNotAssignable(parser, ctx, "--(true?true:false)"); + expectFailSetValueNotSupported(parser, ctx, "(true?true:false)=(true?true:false)"); + + // TypeReference + expectFailNotAssignable(parser, ctx, "T(String)++"); + expectFailNotAssignable(parser, ctx, "--T(Integer)"); + expectFailSetValueNotSupported(parser, ctx, "T(String)=T(Integer)"); + + // OperatorBetween + expectFailNotAssignable(parser, ctx, "(3 between {1,5})++"); + expectFailNotAssignable(parser, ctx, "--(3 between {1,5})"); + expectFailSetValueNotSupported(parser, ctx, "(3 between {1,5})=(3 between {1,5})"); + + // OperatorInstanceOf + expectFailNotAssignable(parser, ctx, "(type instanceof T(String))++"); + expectFailNotAssignable(parser, ctx, "--(type instanceof T(String))"); + expectFailSetValueNotSupported(parser, ctx, "(type instanceof T(String))=(type instanceof T(String))"); + + // Elvis + expectFailNotAssignable(parser, ctx, "(true?:false)++"); + expectFailNotAssignable(parser, ctx, "--(true?:false)"); + expectFailSetValueNotSupported(parser, ctx, "(true?:false)=(true?:false)"); + + // OpInc + expectFailNotAssignable(parser, ctx, "(iii++)++"); + expectFailNotAssignable(parser, ctx, "--(++iii)"); + expectFailSetValueNotSupported(parser, ctx, "(iii++)=(++iii)"); + + // OpDec + expectFailNotAssignable(parser, ctx, "(iii--)++"); + expectFailNotAssignable(parser, ctx, "--(--iii)"); + expectFailSetValueNotSupported(parser, ctx, "(iii--)=(--iii)"); + + // OperatorNot + expectFailNotAssignable(parser, ctx, "(!true)++"); + expectFailNotAssignable(parser, ctx, "--(!false)"); + expectFailSetValueNotSupported(parser, ctx, "(!true)=(!false)"); + + // OperatorPower + expectFailNotAssignable(parser, ctx, "(iii^2)++"); + expectFailNotAssignable(parser, ctx, "--(iii^2)"); + expectFailSetValueNotSupported(parser, ctx, "(iii^2)=(iii^3)"); + + // Assign + // iii=42 + e = parser.parseExpression("iii=iii++"); + assertThat(helper.iii).isEqualTo(42); + int return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(helper.iii).isEqualTo(42); + assertThat(return_iii).isEqualTo(42); + + // Identifier + e = parser.parseExpression("iii++"); + assertThat(helper.iii).isEqualTo(42); + return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_iii).isEqualTo(42); + assertThat(helper.iii).isEqualTo(43); + + e = parser.parseExpression("--iii"); + assertThat(helper.iii).isEqualTo(43); + return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_iii).isEqualTo(42); + assertThat(helper.iii).isEqualTo(42); + + e = parser.parseExpression("iii=99"); + assertThat(helper.iii).isEqualTo(42); + return_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_iii).isEqualTo(99); + assertThat(helper.iii).isEqualTo(99); + + // CompoundExpression + // foo.iii == 99 + e = parser.parseExpression("foo.iii++"); + assertThat(helper.foo.iii).isEqualTo(99); + int return_foo_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_foo_iii).isEqualTo(99); + assertThat(helper.foo.iii).isEqualTo(100); + + e = parser.parseExpression("--foo.iii"); + assertThat(helper.foo.iii).isEqualTo(100); + return_foo_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_foo_iii).isEqualTo(99); + assertThat(helper.foo.iii).isEqualTo(99); + + e = parser.parseExpression("foo.iii=999"); + assertThat(helper.foo.iii).isEqualTo(99); + return_foo_iii = e.getValue(ctx, Integer.TYPE); + assertThat(return_foo_iii).isEqualTo(999); + assertThat(helper.foo.iii).isEqualTo(999); + + // ConstructorReference + expectFailNotAssignable(parser, ctx, "(new String('abc'))++"); + expectFailNotAssignable(parser, ctx, "--(new String('abc'))"); + expectFailSetValueNotSupported(parser, ctx, "(new String('abc'))=(new String('abc'))"); + + // MethodReference + expectFailNotIncrementable(parser, ctx, "m()++"); + expectFailNotDecrementable(parser, ctx, "--m()"); + expectFailSetValueNotSupported(parser, ctx, "m()=m()"); + + // OperatorMatches + expectFailNotAssignable(parser, ctx, "('abc' matches '^a..')++"); + expectFailNotAssignable(parser, ctx, "--('abc' matches '^a..')"); + expectFailSetValueNotSupported(parser, ctx, "('abc' matches '^a..')=('abc' matches '^a..')"); + + // Selection + ctx.registerFunction("isEven", Spr9751.class.getDeclaredMethod("isEven", Integer.TYPE)); + + expectFailNotIncrementable(parser, ctx, "({1,2,3}.?[#isEven(#this)])++"); + expectFailNotDecrementable(parser, ctx, "--({1,2,3}.?[#isEven(#this)])"); + expectFailNotAssignable(parser, ctx, "({1,2,3}.?[#isEven(#this)])=({1,2,3}.?[#isEven(#this)])"); + + // slightly diff here because return value isn't a list, it is a single entity + expectFailNotAssignable(parser, ctx, "({1,2,3}.^[#isEven(#this)])++"); + expectFailNotAssignable(parser, ctx, "--({1,2,3}.^[#isEven(#this)])"); + expectFailNotAssignable(parser, ctx, "({1,2,3}.^[#isEven(#this)])=({1,2,3}.^[#isEven(#this)])"); + + expectFailNotAssignable(parser, ctx, "({1,2,3}.$[#isEven(#this)])++"); + expectFailNotAssignable(parser, ctx, "--({1,2,3}.$[#isEven(#this)])"); + expectFailNotAssignable(parser, ctx, "({1,2,3}.$[#isEven(#this)])=({1,2,3}.$[#isEven(#this)])"); + + // FunctionReference + expectFailNotAssignable(parser, ctx, "#isEven(3)++"); + expectFailNotAssignable(parser, ctx, "--#isEven(4)"); + expectFailSetValueNotSupported(parser, ctx, "#isEven(3)=#isEven(5)"); + + // VariableReference + ctx.setVariable("wibble", "hello world"); + expectFailNotIncrementable(parser, ctx, "#wibble++"); + expectFailNotDecrementable(parser, ctx, "--#wibble"); + e = parser.parseExpression("#wibble=#wibble+#wibble"); + String s = e.getValue(ctx, String.class); + assertThat(s).isEqualTo("hello worldhello world"); + assertThat(ctx.lookupVariable("wibble")).isEqualTo("hello worldhello world"); + + ctx.setVariable("wobble", 3); + e = parser.parseExpression("#wobble++"); + assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(3); + int r = e.getValue(ctx, Integer.TYPE); + assertThat(r).isEqualTo(3); + assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(4); + + e = parser.parseExpression("--#wobble"); + assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(4); + r = e.getValue(ctx, Integer.TYPE); + assertThat(r).isEqualTo(3); + assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(3); + + e = parser.parseExpression("#wobble=34"); + assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(3); + r = e.getValue(ctx, Integer.TYPE); + assertThat(r).isEqualTo(34); + assertThat(((Integer) ctx.lookupVariable("wobble")).intValue()).isEqualTo(34); + + // Projection + expectFailNotIncrementable(parser, ctx, "({1,2,3}.![#isEven(#this)])++"); // projection would be {false,true,false} + expectFailNotDecrementable(parser, ctx, "--({1,2,3}.![#isEven(#this)])"); // projection would be {false,true,false} + expectFailNotAssignable(parser, ctx, "({1,2,3}.![#isEven(#this)])=({1,2,3}.![#isEven(#this)])"); + + // InlineList + expectFailNotAssignable(parser, ctx, "({1,2,3})++"); + expectFailNotAssignable(parser, ctx, "--({1,2,3})"); + expectFailSetValueNotSupported(parser, ctx, "({1,2,3})=({1,2,3})"); + + // InlineMap + expectFailNotAssignable(parser, ctx, "({'a':1,'b':2,'c':3})++"); + expectFailNotAssignable(parser, ctx, "--({'a':1,'b':2,'c':3})"); + expectFailSetValueNotSupported(parser, ctx, "({'a':1,'b':2,'c':3})=({'a':1,'b':2,'c':3})"); + + // BeanReference + BeanResolver beanResolver = (context, beanName) -> { + if (beanName.equals("foo") || beanName.equals("bar")) { + return new Spr9751_2(); + } + throw new AccessException("unknown bean " + beanName); + }; + ctx.setBeanResolver(beanResolver); + expectFailNotAssignable(parser, ctx, "@foo++"); + expectFailNotAssignable(parser, ctx, "--@foo"); + expectFailSetValueNotSupported(parser, ctx, "@foo=@bar"); + + // PropertyOrFieldReference + helper.iii = 42; + e = parser.parseExpression("iii++"); + assertThat(helper.iii).isEqualTo(42); + r = e.getValue(ctx, Integer.TYPE); + assertThat(r).isEqualTo(42); + assertThat(helper.iii).isEqualTo(43); + + e = parser.parseExpression("--iii"); + assertThat(helper.iii).isEqualTo(43); + r = e.getValue(ctx, Integer.TYPE); + assertThat(r).isEqualTo(42); + assertThat(helper.iii).isEqualTo(42); + + e = parser.parseExpression("iii=100"); + assertThat(helper.iii).isEqualTo(42); + r = e.getValue(ctx, Integer.TYPE); + assertThat(r).isEqualTo(100); + assertThat(helper.iii).isEqualTo(100); + } - static class CustomMethodResolver implements MethodResolver { + private void expectFailNotAssignable(ExpressionParser parser, EvaluationContext eContext, String expressionString) { + expectFail(parser, eContext, expressionString, SpelMessage.NOT_ASSIGNABLE); + } - @Override - public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, - List argumentTypes) throws AccessException { - return null; + private void expectFailSetValueNotSupported(ExpressionParser parser, EvaluationContext eContext, String expressionString) { + expectFail(parser, eContext, expressionString, SpelMessage.SETVALUE_NOT_SUPPORTED); } - } + private void expectFailNotIncrementable(ExpressionParser parser, EvaluationContext eContext, String expressionString) { + expectFail(parser, eContext, expressionString, SpelMessage.OPERAND_NOT_INCREMENTABLE); + } - static class CustomMethodFilter implements MethodFilter { + private void expectFailNotDecrementable(ExpressionParser parser, EvaluationContext eContext, String expressionString) { + expectFail(parser, eContext, expressionString, SpelMessage.OPERAND_NOT_DECREMENTABLE); + } - @Override - public List filter(List methods) { - return null; + private void expectFail(ExpressionParser parser, EvaluationContext eContext, String expressionString, SpelMessage messageCode) { + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> { + Expression e = parser.parseExpression(expressionString); + SpelUtilities.printAbstractSyntaxTree(System.out, e); + e.getValue(eContext); + }).satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(messageCode)); } } @@ -1433,16 +1459,4 @@ static class Spr9751_2 { public int iii = 99; } - - static class MyBeanResolver implements BeanResolver { - - @Override - public Object resolve(EvaluationContext context, String beanName) throws AccessException { - if (beanName.equals("foo") || beanName.equals("bar")) { - return new Spr9751_2(); - } - throw new AccessException("not heard of " + beanName); - } - } - } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelDocumentationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelDocumentationTests.java index dcc4511f4cd8..280b452007b0 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelDocumentationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 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. @@ -41,16 +41,17 @@ /** * Test the examples specified in the documentation. * - * NOTE: any outgoing changes from this file upon synchronizing with the repo may indicate that + *

NOTE: any outgoing changes from this file upon synchronizing with the repo may indicate that * you need to update the documentation too ! * * @author Andy Clement */ @SuppressWarnings("rawtypes") -public class SpelDocumentationTests extends AbstractExpressionTests { +class SpelDocumentationTests extends AbstractExpressionTests { - static Inventor tesla ; - static Inventor pupin ; + static Inventor tesla; + + static Inventor pupin; static { GregorianCalendar c = new GregorianCalendar(); @@ -65,53 +66,24 @@ public class SpelDocumentationTests extends AbstractExpressionTests { pupin.setPlaceOfBirth(new PlaceOfBirth("Idvor")); } - static class IEEE { - private String name; - - - public Inventor[] Members = new Inventor[1]; - public List Members2 = new ArrayList(); - public Map officers = new HashMap<>(); - - public List> reverse = new ArrayList<>(); - - @SuppressWarnings("unchecked") - IEEE() { - officers.put("president",pupin); - List linv = new ArrayList(); - linv.add(tesla); - officers.put("advisors",linv); - Members2.add(tesla); - Members2.add(pupin); - - reverse.add(officers); - } - - public boolean isMember(String name) { - return true; - } - - public String getName() { return name; } - public void setName(String n) { this.name = n; } - } @Test - public void testMethodInvocation() { + void methodInvocation() { evaluate("'Hello World'.concat('!')","Hello World!",String.class); } @Test - public void testBeanPropertyAccess() { + void beanPropertyAccess() { evaluate("new String('Hello World'.bytes)","Hello World",String.class); } @Test - public void testArrayLengthAccess() { + void arrayLengthAccess() { evaluate("'Hello World'.bytes.length",11,Integer.class); } @Test - public void testRootObject() throws Exception { + void rootObject() throws Exception { GregorianCalendar c = new GregorianCalendar(); c.set(1856, 7, 9); @@ -129,7 +101,7 @@ public void testRootObject() throws Exception { } @Test - public void testEqualityCheck() throws Exception { + void equalityCheck() throws Exception { ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); @@ -143,13 +115,13 @@ public void testEqualityCheck() throws Exception { // Section 7.4.1 @Test - public void testXMLBasedConfig() { + void xmlBasedConfig() { evaluate("(T(java.lang.Math).random() * 100.0 )>0",true,Boolean.class); } // Section 7.5 @Test - public void testLiterals() throws Exception { + void literals() throws Exception { ExpressionParser parser = new SpelExpressionParser(); String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); // evals to "Hello World" @@ -169,7 +141,7 @@ public void testLiterals() throws Exception { } @Test - public void testPropertyAccess() throws Exception { + void propertyAccess() throws Exception { EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); // 1856 assertThat(year).isEqualTo(1856); @@ -179,12 +151,12 @@ public void testPropertyAccess() throws Exception { } @Test - public void testPropertyNavigation() throws Exception { + void propertyNavigation() throws Exception { ExpressionParser parser = new SpelExpressionParser(); // Inventions Array StandardEvaluationContext teslaContext = TestScenarioCreator.getTestEvaluationContext(); -// teslaContext.setRootObject(tesla); + // teslaContext.setRootObject(tesla); // evaluates to "Induction motor" String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, String.class); @@ -206,9 +178,8 @@ public void testPropertyNavigation() throws Exception { assertThat(invention).isEqualTo("Wireless communication"); } - @Test - public void testDictionaryAccess() throws Exception { + void dictionaryAccess() throws Exception { StandardEvaluationContext societyContext = new StandardEvaluationContext(); societyContext.setRootObject(new IEEE()); // Officer's Dictionary @@ -233,7 +204,7 @@ public void testDictionaryAccess() throws Exception { // 7.5.3 @Test - public void testMethodInvocation2() throws Exception { + void methodInvocation2() throws Exception { // string literal, evaluates to "bc" String c = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class); assertThat(c).isEqualTo("bc"); @@ -248,7 +219,7 @@ public void testMethodInvocation2() throws Exception { // 7.5.4.1 @Test - public void testRelationalOperators() throws Exception { + void relationalOperators() throws Exception { boolean result = parser.parseExpression("2 == 2").getValue(Boolean.class); assertThat(result).isTrue(); // evaluates to false @@ -261,7 +232,7 @@ public void testRelationalOperators() throws Exception { } @Test - public void testOtherOperators() throws Exception { + void otherOperators() throws Exception { // evaluates to false boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class); assertThat(falseValue).isFalse(); @@ -278,7 +249,7 @@ public void testOtherOperators() throws Exception { // 7.5.4.2 @Test - public void testLogicalOperators() throws Exception { + void logicalOperators() throws Exception { StandardEvaluationContext societyContext = new StandardEvaluationContext(); societyContext.setRootObject(new IEEE()); @@ -319,7 +290,7 @@ public void testLogicalOperators() throws Exception { // 7.5.4.3 @Test - public void testNumericalOperators() throws Exception { + void numericalOperators() throws Exception { // Addition int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 assertThat(two).isEqualTo(2); @@ -363,7 +334,7 @@ public void testNumericalOperators() throws Exception { // 7.5.5 @Test - public void testAssignment() throws Exception { + void assignment() throws Exception { Inventor inventor = new Inventor(); StandardEvaluationContext inventorContext = new StandardEvaluationContext(); inventorContext.setRootObject(inventor); @@ -381,7 +352,7 @@ public void testAssignment() throws Exception { // 7.5.6 @Test - public void testTypes() throws Exception { + void types() throws Exception { Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); assertThat(dateClass).isEqualTo(Date.class); boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class); @@ -391,7 +362,7 @@ public void testTypes() throws Exception { // 7.5.7 @Test - public void testConstructors() throws Exception { + void constructors() throws Exception { StandardEvaluationContext societyContext = new StandardEvaluationContext(); societyContext.setRootObject(new IEEE()); Inventor einstein = @@ -404,7 +375,7 @@ public void testConstructors() throws Exception { // 7.5.8 @Test - public void testVariables() throws Exception { + void variables() throws Exception { Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("newName", "Mike Tesla"); @@ -416,9 +387,9 @@ public void testVariables() throws Exception { assertThat(tesla.getFoo()).isEqualTo("Mike Tesla"); } - @SuppressWarnings("unchecked") @Test - public void testSpecialVariables() throws Exception { + @SuppressWarnings("unchecked") + void specialVariables() throws Exception { // create an array of integers List primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17); @@ -435,7 +406,7 @@ public void testSpecialVariables() throws Exception { // 7.5.9 @Test - public void testFunctions() throws Exception { + void functions() throws Exception { ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.registerFunction("reverseString", StringUtils.class.getDeclaredMethod("reverseString", String.class)); @@ -447,7 +418,7 @@ public void testFunctions() throws Exception { // 7.5.10 @Test - public void testTernary() throws Exception { + void ternary() throws Exception { String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class); assertThat(falseString).isEqualTo("falseExp"); @@ -468,9 +439,9 @@ public void testTernary() throws Exception { // 7.5.11 - @SuppressWarnings("unchecked") @Test - public void testSelection() throws Exception { + @SuppressWarnings("unchecked") + void selection() throws Exception { StandardEvaluationContext societyContext = new StandardEvaluationContext(); societyContext.setRootObject(new IEEE()); List list = (List) parser.parseExpression("Members2.?[nationality == 'Serbian']").getValue(societyContext); @@ -481,7 +452,7 @@ public void testSelection() throws Exception { // 7.5.12 @Test - public void testTemplating() throws Exception { + void templating() throws Exception { String randomPhrase = parser.parseExpression("random number is ${T(java.lang.Math).random()}", new TemplatedParserContext()).getValue(String.class); assertThat(randomPhrase.startsWith("random number")).isTrue(); @@ -505,14 +476,39 @@ public boolean isTemplate() { } } + static class IEEE { + private String name; + + public Inventor[] Members = new Inventor[1]; + public List Members2 = new ArrayList(); + public Map officers = new HashMap<>(); + + public List> reverse = new ArrayList<>(); + + @SuppressWarnings("unchecked") + IEEE() { + officers.put("president",pupin); + List linv = new ArrayList(); + linv.add(tesla); + officers.put("advisors",linv); + Members2.add(tesla); + Members2.add(pupin); + + reverse.add(officers); + } + + public boolean isMember(String name) { + return true; + } + + public String getName() { return name; } + public void setName(String n) { this.name = n; } + } + static class StringUtils { public static String reverseString(String input) { - StringBuilder backwards = new StringBuilder(); - for (int i = 0; i < input.length(); i++) { - backwards.append(input.charAt(input.length() - 1 - i)); - } - return backwards.toString(); + return new StringBuilder(input).reverse().toString(); } } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java b/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java index 4bb3f7da0cc3..ebeebcf7e0ce 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java @@ -30,7 +30,7 @@ *

  • The root context object is an Inventor instance {@link Inventor} * */ -public class TestScenarioCreator { +class TestScenarioCreator { public static StandardEvaluationContext getTestEvaluationContext() { StandardEvaluationContext testContext = new StandardEvaluationContext();