1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.expression.spel.ast;
18
19 import java.lang.reflect.Array;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import org.springframework.expression.EvaluationException;
28 import org.springframework.expression.TypedValue;
29 import org.springframework.expression.spel.ExpressionState;
30 import org.springframework.expression.spel.SpelEvaluationException;
31 import org.springframework.expression.spel.SpelMessage;
32 import org.springframework.util.Assert;
33 import org.springframework.util.ClassUtils;
34 import org.springframework.util.ObjectUtils;
35
36
37
38
39
40
41
42
43
44
45
46
47
48 public class Selection extends SpelNodeImpl {
49
50 public static final int ALL = 0;
51
52 public static final int FIRST = 1;
53
54 public static final int LAST = 2;
55
56 private final int variant;
57
58 private final boolean nullSafe;
59
60
61 public Selection(boolean nullSafe, int variant, int pos, SpelNodeImpl expression) {
62 super(pos, expression);
63 Assert.notNull(expression, "Expression must not be null");
64 this.nullSafe = nullSafe;
65 this.variant = variant;
66 }
67
68
69 @Override
70 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
71 return getValueRef(state).getValue();
72 }
73
74 @Override
75 protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
76 TypedValue op = state.getActiveContextObject();
77 Object operand = op.getValue();
78
79 SpelNodeImpl selectionCriteria = this.children[0];
80 if (operand instanceof Map) {
81 Map<?, ?> mapdata = (Map<?, ?>) operand;
82
83 Map<Object, Object> result = new HashMap<Object, Object>();
84 Object lastKey = null;
85 for (Map.Entry<?, ?> entry : mapdata.entrySet()) {
86 try {
87 TypedValue kvPair = new TypedValue(entry);
88 state.pushActiveContextObject(kvPair);
89 Object val = selectionCriteria.getValueInternal(state).getValue();
90 if (val instanceof Boolean) {
91 if ((Boolean) val) {
92 if (this.variant == FIRST) {
93 result.put(entry.getKey(), entry.getValue());
94 return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this);
95 }
96 result.put(entry.getKey(), entry.getValue());
97 lastKey = entry.getKey();
98 }
99 }
100 else {
101 throw new SpelEvaluationException(selectionCriteria.getStartPosition(),
102 SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);
103 }
104 }
105 finally {
106 state.popActiveContextObject();
107 }
108 }
109 if ((this.variant == FIRST || this.variant == LAST) && result.isEmpty()) {
110 return new ValueRef.TypedValueHolderValueRef(new TypedValue(null), this);
111 }
112
113 if (this.variant == LAST) {
114 Map<Object, Object> resultMap = new HashMap<Object, Object>();
115 Object lastValue = result.get(lastKey);
116 resultMap.put(lastKey,lastValue);
117 return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultMap),this);
118 }
119
120 return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
121 }
122
123 if ((operand instanceof Collection) || ObjectUtils.isArray(operand)) {
124 List<Object> data = new ArrayList<Object>();
125 Collection<?> coll = (operand instanceof Collection ?
126 (Collection<?>) operand : Arrays.asList(ObjectUtils.toObjectArray(operand)));
127 data.addAll(coll);
128 List<Object> result = new ArrayList<Object>();
129 int index = 0;
130 for (Object element : data) {
131 try {
132 state.pushActiveContextObject(new TypedValue(element));
133 state.enterScope("index", index);
134 Object val = selectionCriteria.getValueInternal(state).getValue();
135 if (val instanceof Boolean) {
136 if ((Boolean) val) {
137 if (this.variant == FIRST) {
138 return new ValueRef.TypedValueHolderValueRef(new TypedValue(element), this);
139 }
140 result.add(element);
141 }
142 }
143 else {
144 throw new SpelEvaluationException(selectionCriteria.getStartPosition(),
145 SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);
146 }
147 index++;
148 }
149 finally {
150 state.exitScope();
151 state.popActiveContextObject();
152 }
153 }
154
155 if ((this.variant == FIRST || this.variant == LAST) && result.size() == 0) {
156 return ValueRef.NullValueRef.INSTANCE;
157 }
158
159 if (this.variant == LAST) {
160 return new ValueRef.TypedValueHolderValueRef(new TypedValue(result.get(result.size() - 1)),this);
161 }
162
163 if (operand instanceof Collection) {
164 return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
165 }
166 Class<?> elementType = ClassUtils.resolvePrimitiveIfNecessary(
167 op.getTypeDescriptor().getElementTypeDescriptor().getType());
168 Object resultArray = Array.newInstance(elementType, result.size());
169 System.arraycopy(result.toArray(), 0, resultArray, 0, result.size());
170 return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultArray),this);
171 }
172 if (operand == null) {
173 if (this.nullSafe) {
174 return ValueRef.NullValueRef.INSTANCE;
175 }
176 throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION, "null");
177 }
178 throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION,
179 operand.getClass().getName());
180 }
181
182 @Override
183 public String toStringAST() {
184 StringBuilder sb = new StringBuilder();
185 switch (this.variant) {
186 case ALL:
187 sb.append("?[");
188 break;
189 case FIRST:
190 sb.append("^[");
191 break;
192 case LAST:
193 sb.append("$[");
194 break;
195 }
196 return sb.append(getChild(0).toStringAST()).append("]").toString();
197 }
198
199 }