| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 5a3eea8 commit 91ed5b6
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,5 @@ | |||
| 1 | 1 | /* | |
| 2 | - * Copyright 2002-2013 the original author or authors. | ||
| 2 | + * Copyright 2002-2015 the original author or authors. | ||
| 3 | 3 | * | |
| 4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 | 5 | * you may not use this file except in compliance with the License. | |
@@ -16,6 +16,7 @@ | |||
| 16 | 16 | ||
| 17 | 17 | package org.springframework.expression.spel; | |
| 18 | 18 | ||
| 19 | + import java.util.Collections; | ||
| 19 | 20 | import java.util.HashMap; | |
| 20 | 21 | import java.util.List; | |
| 21 | 22 | import java.util.Map; | |
@@ -52,6 +53,15 @@ public class ExpressionState { | |||
| 52 | 53 | ||
| 53 | 54 | private final TypedValue rootObject; | |
| 54 | 55 | ||
| 56 | + // When entering a new scope there is a new base object which should be used | ||
| 57 | + // for '#this' references (or to act as a target for unqualified references). | ||
| 58 | + // This stack captures those objects at each nested scope level. | ||
| 59 | + // For example: | ||
| 60 | + // #list1.?[#list2.contains(#this)] | ||
| 61 | + // On entering the selection we enter a new scope, and #this is now the | ||
| 62 | + // element from list1 | ||
| 63 | + private Stack<TypedValue> scopeRootObjects; | ||
| 64 | + | ||
| 55 | 65 | private final SpelParserConfiguration configuration; | |
| 56 | 66 | ||
| 57 | 67 | private Stack<VariableScope> variableScopes; | |
@@ -86,6 +96,9 @@ private void ensureVariableScopesInitialized() { | |||
| 86 | 96 | // top level empty variable scope | |
| 87 | 97 | this.variableScopes.add(new VariableScope()); | |
| 88 | 98 | } | |
| 99 | + if (this.scopeRootObjects == null) { | ||
| 100 | + this.scopeRootObjects = new Stack<TypedValue>(); | ||
| 101 | + } | ||
| 89 | 102 | } | |
| 90 | 103 | ||
| 91 | 104 | /** | |
@@ -116,6 +129,13 @@ public TypedValue getRootContextObject() { | |||
| 116 | 129 | return this.rootObject; | |
| 117 | 130 | } | |
| 118 | 131 | ||
| 132 | + public TypedValue getScopeRootContextObject() { | ||
| 133 | + if (this.scopeRootObjects == null || this.scopeRootObjects.isEmpty()) { | ||
| 134 | + return this.rootObject; | ||
| 135 | + } | ||
| 136 | + return this.scopeRootObjects.peek(); | ||
| 137 | + } | ||
| 138 | + | ||
| 119 | 139 | public void setVariable(String name, Object value) { | |
| 120 | 140 | this.relatedContext.setVariable(name, value); | |
| 121 | 141 | } | |
@@ -158,16 +178,25 @@ public Object convertValue(TypedValue value, TypeDescriptor targetTypeDescriptor | |||
| 158 | 178 | public void enterScope(Map<String, Object> argMap) { | |
| 159 | 179 | ensureVariableScopesInitialized(); | |
| 160 | 180 | this.variableScopes.push(new VariableScope(argMap)); | |
| 181 | + this.scopeRootObjects.push(getActiveContextObject()); | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + public void enterScope() { | ||
| 185 | + ensureVariableScopesInitialized(); | ||
| 186 | + this.variableScopes.push(new VariableScope(Collections.<String,Object>emptyMap())); | ||
| 187 | + this.scopeRootObjects.push(getActiveContextObject()); | ||
| 161 | 188 | } | |
| 162 | 189 | ||
| 163 | 190 | public void enterScope(String name, Object value) { | |
| 164 | 191 | ensureVariableScopesInitialized(); | |
| 165 | 192 | this.variableScopes.push(new VariableScope(name, value)); | |
| 193 | + this.scopeRootObjects.push(getActiveContextObject()); | ||
| 166 | 194 | } | |
| 167 | 195 | ||
| 168 | 196 | public void exitScope() { | |
| 169 | 197 | ensureVariableScopesInitialized(); | |
| 170 | 198 | this.variableScopes.pop(); | |
| 199 | + this.scopeRootObjects.pop(); | ||
| 171 | 200 | } | |
| 172 | 201 | ||
| 173 | 202 | public void setLocalVariable(String name, Object value) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,5 @@ | |||
| 1 | 1 | /* | |
| 2 | - * Copyright 2002-2014 the original author or authors. | ||
| 2 | + * Copyright 2002-2015 the original author or authors. | ||
| 3 | 3 | * | |
| 4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 | 5 | * you may not use this file except in compliance with the License. | |
@@ -150,7 +150,7 @@ private Object[] getArguments(ExpressionState state) { | |||
| 150 | 150 | for (int i = 0; i < arguments.length; i++) { | |
| 151 | 151 | // Make the root object the active context again for evaluating the parameter expressions | |
| 152 | 152 | try { | |
| 153 | - state.pushActiveContextObject(state.getRootContextObject()); | ||
| 153 | + state.pushActiveContextObject(state.getScopeRootContextObject()); | ||
| 154 | 154 | arguments[i] = this.children[i].getValueInternal(state).getValue(); | |
| 155 | 155 | } | |
| 156 | 156 | finally { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,5 @@ | |||
| 1 | 1 | /* | |
| 2 | - * Copyright 2002-2014 the original author or authors. | ||
| 2 | + * Copyright 2002-2015 the original author or authors. | ||
| 3 | 3 | * | |
| 4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 | 5 | * you may not use this file except in compliance with the License. | |
@@ -68,17 +68,19 @@ protected ValueRef getValueRef(ExpressionState state) throws EvaluationException | |||
| 68 | 68 | // before calling the specified operation. This special context object | |
| 69 | 69 | // has two fields 'key' and 'value' that refer to the map entries key | |
| 70 | 70 | // and value, and they can be referenced in the operation | |
| 71 | - // eg. {'a':'y','b':'n'}.!{value=='y'?key:null}" == ['a', null] | ||
| 71 | + // eg. {'a':'y','b':'n'}.![value=='y'?key:null]" == ['a', null] | ||
| 72 | 72 | if (operand instanceof Map) { | |
| 73 | 73 | Map<?, ?> mapData = (Map<?, ?>) operand; | |
| 74 | 74 | List<Object> result = new ArrayList<Object>(); | |
| 75 | 75 | for (Map.Entry<?, ?> entry : mapData.entrySet()) { | |
| 76 | 76 | try { | |
| 77 | 77 | state.pushActiveContextObject(new TypedValue(entry)); | |
| 78 | + state.enterScope(); | ||
| 78 | 79 | result.add(this.children[0].getValueInternal(state).getValue()); | |
| 79 | 80 | } | |
| 80 | 81 | finally { | |
| 81 | 82 | state.popActiveContextObject(); | |
| 83 | + state.exitScope(); | ||
| 82 | 84 | } | |
| 83 | 85 | } | |
| 84 | 86 | return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this); // TODO unable to build correct type descriptor | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,5 @@ | |||
| 1 | 1 | /* | |
| 2 | - * Copyright 2002-2014 the original author or authors. | ||
| 2 | + * Copyright 2002-2015 the original author or authors. | ||
| 3 | 3 | * | |
| 4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 | 5 | * you may not use this file except in compliance with the License. | |
@@ -86,6 +86,7 @@ protected ValueRef getValueRef(ExpressionState state) throws EvaluationException | |||
| 86 | 86 | try { | |
| 87 | 87 | TypedValue kvPair = new TypedValue(entry); | |
| 88 | 88 | state.pushActiveContextObject(kvPair); | |
| 89 | + state.enterScope(); | ||
| 89 | 90 | Object val = selectionCriteria.getValueInternal(state).getValue(); | |
| 90 | 91 | if (val instanceof Boolean) { | |
| 91 | 92 | if ((Boolean) val) { | |
@@ -104,6 +105,7 @@ protected ValueRef getValueRef(ExpressionState state) throws EvaluationException | |||
| 104 | 105 | } | |
| 105 | 106 | finally { | |
| 106 | 107 | state.popActiveContextObject(); | |
| 108 | + state.exitScope(); | ||
| 107 | 109 | } | |
| 108 | 110 | } | |
| 109 | 111 | if ((this.variant == FIRST || this.variant == LAST) && result.isEmpty()) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -36,7 +36,6 @@ | |||
| 36 | 36 | import org.junit.Rule; | |
| 37 | 37 | import org.junit.Test; | |
| 38 | 38 | import org.junit.rules.ExpectedException; | |
| 39 | - | ||
| 40 | 39 | import org.springframework.core.MethodParameter; | |
| 41 | 40 | import org.springframework.core.convert.TypeDescriptor; | |
| 42 | 41 | import org.springframework.expression.AccessException; | |
@@ -1918,6 +1917,164 @@ public void SPR12808() throws Exception { | |||
| 1918 | 1917 | sec.setVariable("no", "1.0"); | |
| 1919 | 1918 | assertTrue(expression.getValue(sec).toString().startsWith("Object")); | |
| 1920 | 1919 | } | |
| 1920 | + | ||
| 1921 | + @Test | ||
| 1922 | + @SuppressWarnings("rawtypes") | ||
| 1923 | + public void SPR13055() throws Exception { | ||
| 1924 | + List<Map<String, Object>> myPayload = new ArrayList<Map<String, Object>>(); | ||
| 1925 | + | ||
| 1926 | + Map<String, Object> v1 = new HashMap<String, Object>(); | ||
| 1927 | + Map<String, Object> v2 = new HashMap<String, Object>(); | ||
| 1928 | + | ||
| 1929 | + v1.put("test11", "test11"); | ||
| 1930 | + v1.put("test12", "test12"); | ||
| 1931 | + v2.put("test21", "test21"); | ||
| 1932 | + v2.put("test22", "test22"); | ||
| 1933 | + | ||
| 1934 | + myPayload.add(v1); | ||
| 1935 | + myPayload.add(v2); | ||
| 1936 | + | ||
| 1937 | + EvaluationContext context = new StandardEvaluationContext(myPayload); | ||
| 1938 | + | ||
| 1939 | + ExpressionParser parser = new SpelExpressionParser(); | ||
| 1940 | + | ||
| 1941 | + String ex = "#root.![T(org.springframework.util.StringUtils).collectionToCommaDelimitedString(#this.values())]"; | ||
| 1942 | + List res = parser.parseExpression(ex).getValue(context, List.class); | ||
| 1943 | + assertEquals("[test12,test11, test22,test21]", res.toString()); | ||
| 1944 | + | ||
| 1945 | + res = parser.parseExpression("#root.![#this.values()]").getValue(context, | ||
| 1946 | + List.class); | ||
| 1947 | + assertEquals("[[test12, test11], [test22, test21]]", res.toString()); | ||
| 1948 | + | ||
| 1949 | + res = parser.parseExpression("#root.![values()]").getValue(context, List.class); | ||
| 1950 | + assertEquals("[[test12, test11], [test22, test21]]", res.toString()); | ||
| 1951 | + } | ||
| 1952 | + | ||
| 1953 | + @Test | ||
| 1954 | + public void SPR12035() { | ||
| 1955 | + ExpressionParser parser = new SpelExpressionParser(); | ||
| 1956 | + | ||
| 1957 | + Expression expression1 = parser.parseExpression("list.?[ value>2 ].size()!=0"); | ||
| 1958 | + assertTrue(expression1.getValue(new BeanClass(new ListOf(1.1), new ListOf(2.2)), | ||
| 1959 | + Boolean.class)); | ||
| 1960 | + | ||
| 1961 | + Expression expression2 = parser.parseExpression("list.?[ T(java.lang.Math).abs(value) > 2 ].size()!=0"); | ||
| 1962 | + assertTrue(expression2.getValue(new BeanClass(new ListOf(1.1), new ListOf(-2.2)), | ||
| 1963 | + Boolean.class)); | ||
| 1964 | + } | ||
| 1965 | + | ||
| 1966 | + static class CCC { | ||
| 1967 | + public boolean method(Object o) { | ||
| 1968 | + System.out.println(o); | ||
| 1969 | + return false; | ||
| 1970 | + } | ||
| 1971 | + } | ||
| 1972 | + | ||
| 1973 | + @Test | ||
| 1974 | + public void SPR13055_maps() { | ||
| 1975 | + EvaluationContext context = new StandardEvaluationContext(); | ||
| 1976 | + ExpressionParser parser = new SpelExpressionParser(); | ||
| 1977 | + | ||
| 1978 | + Expression ex = parser.parseExpression("{'a':'y','b':'n'}.![value=='y'?key:null]"); | ||
| 1979 | + assertEquals("[a, null]", ex.getValue(context).toString()); | ||
| 1980 | + | ||
| 1981 | + ex = parser.parseExpression("{2:4,3:6}.![T(java.lang.Math).abs(#this.key) + 5]"); | ||
| 1982 | + assertEquals("[7, 8]", ex.getValue(context).toString()); | ||
| 1983 | + | ||
| 1984 | + ex = parser.parseExpression("{2:4,3:6}.![T(java.lang.Math).abs(#this.value) + 5]"); | ||
| 1985 | + assertEquals("[9, 11]", ex.getValue(context).toString()); | ||
| 1986 | + } | ||
| 1987 | + | ||
| 1988 | + @Test | ||
| 1989 | + @SuppressWarnings({ "unchecked", "rawtypes" }) | ||
| 1990 | + public void SPR10417() { | ||
| 1991 | + List list1 = new ArrayList(); | ||
| 1992 | + list1.add("a"); | ||
| 1993 | + list1.add("b"); | ||
| 1994 | + list1.add("x"); | ||
| 1995 | + List list2 = new ArrayList(); | ||
| 1996 | + list2.add("c"); | ||
| 1997 | + list2.add("x"); | ||
| 1998 | + EvaluationContext context = new StandardEvaluationContext(); | ||
| 1999 | + context.setVariable("list1", list1); | ||
| 2000 | + context.setVariable("list2", list2); | ||
| 2001 | + | ||
| 2002 | + // #this should be the element from list1 | ||
| 2003 | + Expression ex = parser.parseExpression("#list1.?[#list2.contains(#this)]"); | ||
| 2004 | + Object result = ex.getValue(context); | ||
| 2005 | + assertEquals("[x]", result.toString()); | ||
| 2006 | + | ||
| 2007 | + // toString() should be called on the element from list1 | ||
| 2008 | + ex = parser.parseExpression("#list1.?[#list2.contains(toString())]"); | ||
| 2009 | + result = ex.getValue(context); | ||
| 2010 | + assertEquals("[x]", result.toString()); | ||
| 2011 | + | ||
| 2012 | + List list3 = new ArrayList(); | ||
| 2013 | + list3.add(1); | ||
| 2014 | + list3.add(2); | ||
| 2015 | + list3.add(3); | ||
| 2016 | + list3.add(4); | ||
| 2017 | + | ||
| 2018 | + context = new StandardEvaluationContext(); | ||
| 2019 | + context.setVariable("list3", list3); | ||
| 2020 | + ex = parser.parseExpression("#list3.?[#this > 2]"); | ||
| 2021 | + result = ex.getValue(context); | ||
| 2022 | + assertEquals("[3, 4]", result.toString()); | ||
| 2023 | + | ||
| 2024 | + ex = parser.parseExpression("#list3.?[#this >= T(java.lang.Math).abs(T(java.lang.Math).abs(#this))]"); | ||
| 2025 | + result = ex.getValue(context); | ||
| 2026 | + assertEquals("[1, 2, 3, 4]", result.toString()); | ||
| 2027 | + } | ||
| 2028 | + | ||
| 2029 | + @Test | ||
| 2030 | + @SuppressWarnings({ "unchecked", "rawtypes" }) | ||
| 2031 | + public void SPR10417_maps() { | ||
| 2032 | + Map map1 = new HashMap(); | ||
| 2033 | + map1.put("A", 65); | ||
| 2034 | + map1.put("B", 66); | ||
| 2035 | + map1.put("X", 66); | ||
| 2036 | + Map map2 = new HashMap(); | ||
| 2037 | + map2.put("X", 66); | ||
| 2038 | + | ||
| 2039 | + EvaluationContext context = new StandardEvaluationContext(); | ||
| 2040 | + context.setVariable("map1", map1); | ||
| 2041 | + context.setVariable("map2", map2); | ||
| 2042 | + | ||
| 2043 | + // #this should be the element from list1 | ||
| 2044 | + Expression ex = parser.parseExpression("#map1.?[#map2.containsKey(#this.getKey())]"); | ||
| 2045 | + Object result = ex.getValue(context); | ||
| 2046 | + assertEquals("{X=66}", result.toString()); | ||
| 2047 | + | ||
| 2048 | + ex = parser.parseExpression("#map1.?[#map2.containsKey(key)]"); | ||
| 2049 | + result = ex.getValue(context); | ||
| 2050 | + assertEquals("{X=66}", result.toString()); | ||
| 2051 | + } | ||
| 2052 | + | ||
| 2053 | + public static class ListOf { | ||
| 2054 | + | ||
| 2055 | + private final double value; | ||
| 2056 | + | ||
| 2057 | + public ListOf(double v) { | ||
| 2058 | + this.value = v; | ||
| 2059 | + } | ||
| 2060 | + | ||
| 2061 | + public double getValue() { | ||
| 2062 | + return value; | ||
| 2063 | + } | ||
| 2064 | + } | ||
| 2065 | + | ||
| 2066 | + public static class BeanClass { | ||
| 2067 | + | ||
| 2068 | + private final List<ListOf> list; | ||
| 2069 | + | ||
| 2070 | + public BeanClass(ListOf... list) { | ||
| 2071 | + this.list = Arrays.asList(list); | ||
| 2072 | + } | ||
| 2073 | + | ||
| 2074 | + public List<ListOf> getList() { | ||
| 2075 | + return list; | ||
| 2076 | + } | ||
| 2077 | + } | ||
| 1921 | 2078 | ||
| 1922 | 2079 | ||
| 1923 | 2080 | private static enum ABC { A, B, C } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments