Selection.java 5.2 KB
Newer Older
A
Andy Clement 已提交
1
/*
2
 * Copyright 2002-2009 the original author or authors.
A
Andy Clement 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
16

A
Andy Clement 已提交
17 18 19 20
package org.springframework.expression.spel.ast;

import java.util.ArrayList;
import java.util.Collection;
21
import java.util.HashMap;
A
Andy Clement 已提交
22 23 24 25
import java.util.List;
import java.util.Map;

import org.antlr.runtime.Token;
26
import org.springframework.core.convert.TypeDescriptor;
A
Andy Clement 已提交
27
import org.springframework.expression.EvaluationException;
28
import org.springframework.expression.TypedValue;
29
import org.springframework.expression.spel.ExpressionState;
A
Andy Clement 已提交
30 31 32 33 34 35 36 37 38 39 40 41
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;

/**
 * Represents selection over a map or collection. For example: {1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'} returns
 * [2, 4, 6, 8, 10]
 * 
 * Basically a subset of the input data is returned based on the evaluation of the expression supplied as selection
 * criteria.
 * 
 * @author Andy Clement
 */
42
public class Selection extends SpelNodeImpl {
A
Andy Clement 已提交
43 44 45 46 47 48 49 50 51 52 53 54 55

	public final static int ALL = 0; // ?{}
	public final static int FIRST = 1; // ^{}
	public final static int LAST = 2; // ${}

	private final int variant;

	public Selection(Token payload, int variant) {
		super(payload);
		this.variant = variant;
	}

	@Override
56 57 58 59
	public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
		TypedValue op = state.getActiveContextObject();
		Object operand = op.getValue();
		
60
		SpelNodeImpl selectionCriteria = getChild(0);
A
Andy Clement 已提交
61 62
		if (operand instanceof Map) {
			Map<?, ?> mapdata = (Map<?, ?>) operand;
63 64
			// TODO don't lose generic info for the new map
			Map<Object,Object> result = new HashMap<Object,Object>();
65
			Object lastKey = null;
66
			for (Map.Entry entry : mapdata.entrySet()) {
A
Andy Clement 已提交
67
				try {
68 69
					lastKey = entry.getKey();
					KeyValuePair kvp = new KeyValuePair(entry.getKey(),entry.getValue());
70
					TypedValue kvpair = new TypedValue(kvp,TypeDescriptor.valueOf(KeyValuePair.class));
A
Andy Clement 已提交
71
					state.pushActiveContextObject(kvpair);
72
					Object o = selectionCriteria.getValueInternal(state).getValue();
A
Andy Clement 已提交
73 74
					if (o instanceof Boolean) {
						if (((Boolean) o).booleanValue() == true) {
75 76 77 78 79
							if (variant == FIRST) {
								result.put(kvp.key,kvp.value);
								return new TypedValue(result);
							}
							result.put(kvp.key,kvp.value);
A
Andy Clement 已提交
80 81 82 83 84 85 86 87
						}
					} else {
						throw new SpelException(selectionCriteria.getCharPositionInLine(),
								SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
					}
				} finally {
					state.popActiveContextObject();
				}
88 89 90 91 92 93
			}
			if ((variant == FIRST || variant == LAST) && result.size() == 0) {
				return new TypedValue(null,TypeDescriptor.NULL_TYPE_DESCRIPTOR);
			}
			if (variant == LAST) {
				Map resultMap = new HashMap();
94 95
				Object lastValue = result.get(lastKey);
				resultMap.put(lastKey,lastValue);
96
				return new TypedValue(resultMap,TypeDescriptor.valueOf(Map.class));
A
Andy Clement 已提交
97
			}
98
			return new TypedValue(result,op.getTypeDescriptor());
A
Andy Clement 已提交
99 100 101 102 103 104 105
		} else if (operand instanceof Collection) {
			List<Object> data = new ArrayList<Object>();
			data.addAll((Collection<?>) operand);
			List<Object> result = new ArrayList<Object>();
			int idx = 0;
			for (Object element : data) {
				try {
106
					state.pushActiveContextObject(new TypedValue(element,TypeDescriptor.valueOf(op.getTypeDescriptor().getElementType())));
A
Andy Clement 已提交
107
					state.enterScope("index", idx);
108
					Object o = selectionCriteria.getValueInternal(state).getValue();
A
Andy Clement 已提交
109 110 111
					if (o instanceof Boolean) {
						if (((Boolean) o).booleanValue() == true) {
							if (variant == FIRST)
112
								return new TypedValue(element,TypeDescriptor.valueOf(op.getTypeDescriptor().getElementType()));
A
Andy Clement 已提交
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
							result.add(element);
						}
					} else {
						throw new SpelException(selectionCriteria.getCharPositionInLine(),
								SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
					}
					idx++;
				} finally {
					state.exitScope();
					state.popActiveContextObject();
				}
			}
			if ((variant == FIRST || variant == LAST) && result.size() == 0) {
				return null;
			}
			if (variant == LAST) {
129
				return new TypedValue(result.get(result.size() - 1),TypeDescriptor.valueOf(op.getTypeDescriptor().getElementType()));
A
Andy Clement 已提交
130
			}
131
			return new TypedValue(result,op.getTypeDescriptor());
A
Andy Clement 已提交
132 133 134 135 136 137 138 139
		} else {
			throw new SpelException(getCharPositionInLine(), SpelMessages.INVALID_TYPE_FOR_SELECTION,
					(operand == null ? "null" : operand.getClass().getName()));
		}
	}

	@Override
	public String toStringAST() {
J
Juergen Hoeller 已提交
140
		StringBuilder sb = new StringBuilder();
A
Andy Clement 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
		switch (variant) {
		case ALL:
			sb.append("?{");
			break;
		case FIRST:
			sb.append("^{");
			break;
		case LAST:
			sb.append("${");
			break;
		}
		return sb.append(getChild(0).toStringAST()).append("}").toString();
	}

}