View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.expression.spel.ast;
18  
19  import java.util.Collections;
20  import java.util.LinkedHashMap;
21  import java.util.Map;
22  
23  import org.springframework.expression.EvaluationException;
24  import org.springframework.expression.TypedValue;
25  import org.springframework.expression.spel.ExpressionState;
26  import org.springframework.expression.spel.SpelNode;
27  
28  /**
29   * Represent a map in an expression, e.g. '{name:'foo',age:12}'
30   *
31   * @author Andy Clement
32   * @since 4.1
33   */
34  public class InlineMap extends SpelNodeImpl {
35  
36  	// If the map is purely literals, it is a constant value and can be computed and cached
37  	private TypedValue constant = null;
38  
39  
40  	public InlineMap(int pos, SpelNodeImpl... args) {
41  		super(pos, args);
42  		checkIfConstant();
43  	}
44  
45  
46  	/**
47  	 * If all the components of the list are constants, or lists/maps that themselves
48  	 * contain constants, then a constant list can be built to represent this node.
49  	 * This will speed up later getValue calls and reduce the amount of garbage created.
50  	 */
51  	private void checkIfConstant() {
52  		boolean isConstant = true;
53  		for (int c = 0, max = getChildCount(); c < max; c++) {
54  			SpelNode child = getChild(c);
55  			if (!(child instanceof Literal)) {
56  				if (child instanceof InlineList) {
57  					InlineList inlineList = (InlineList) child;
58  					if (!inlineList.isConstant()) {
59  						isConstant = false;
60  						break;
61  					}
62  				}
63  				else if (child instanceof InlineMap) {
64  					InlineMap inlineMap = (InlineMap) child;
65  					if (!inlineMap.isConstant()) {
66  						isConstant = false;
67  						break;
68  					}
69  				}
70  				else if (!((c%2)==0 && (child instanceof PropertyOrFieldReference))) {					
71  					isConstant = false;
72  					break;
73  				}
74  			}
75  		}
76  		if (isConstant) {
77  			Map<Object,Object> constantMap = new LinkedHashMap<Object,Object>();			
78  			int childCount = getChildCount();
79  			for (int c = 0; c < childCount; c++) {
80  				SpelNode keyChild = getChild(c++);
81  				SpelNode valueChild = getChild(c);
82  				Object key = null;
83  				Object value = null;
84  				if (keyChild instanceof Literal) {
85  					key = ((Literal) keyChild).getLiteralValue().getValue();
86  				}
87  				else if (keyChild instanceof PropertyOrFieldReference) {
88  					key = ((PropertyOrFieldReference) keyChild).getName();
89  				}
90  				else {
91  					return;
92  				}
93  				if (valueChild instanceof Literal) {
94  					value = ((Literal) valueChild).getLiteralValue().getValue();
95  				}
96  				else if (valueChild instanceof InlineList) {
97  					value = ((InlineList) valueChild).getConstantValue();
98  				}
99  				else if (valueChild instanceof InlineMap) {
100 					value = ((InlineMap) valueChild).getConstantValue();
101 				}
102 				constantMap.put(key, value);
103 			}
104 			this.constant = new TypedValue(Collections.unmodifiableMap(constantMap));
105 		}
106 	}
107 
108 	@Override
109 	public TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException {
110 		if (this.constant != null) {
111 			return this.constant;
112 		}
113 		else {
114 			Map<Object, Object> returnValue = new LinkedHashMap<Object, Object>();
115 			int childcount = getChildCount();
116 			for (int c = 0; c < childcount; c++) {
117 				// TODO allow for key being PropertyOrFieldReference like Indexer on maps
118 				SpelNode keyChild = getChild(c++);
119 				Object key = null;
120 				if (keyChild instanceof PropertyOrFieldReference) {
121 					PropertyOrFieldReference reference = (PropertyOrFieldReference) keyChild;
122 					key = reference.getName();
123 				}
124 				else {
125 					key = keyChild.getValue(expressionState);
126 				}
127 				Object value = getChild(c).getValue(expressionState);
128 				returnValue.put(key,  value);
129 			}
130 			return new TypedValue(returnValue);
131 		}
132 	}
133 
134 	@Override
135 	public String toStringAST() {
136 		StringBuilder sb = new StringBuilder("{");
137 		int count = getChildCount();
138 		for (int c = 0; c < count; c++) {
139 			if (c > 0) {
140 				sb.append(",");
141 			}
142 			sb.append(getChild(c++).toStringAST());
143 			sb.append(":");
144 			sb.append(getChild(c).toStringAST());
145 		}
146 		sb.append("}");
147 		return sb.toString();
148 	}
149 
150 	/**
151 	 * @return whether this list is a constant value
152 	 */
153 	public boolean isConstant() {
154 		return this.constant != null;
155 	}
156 
157 	@SuppressWarnings("unchecked")
158 	public Map<Object,Object> getConstantValue() {
159 		return (Map<Object,Object>) this.constant.getValue();
160 	}
161 
162 }