001: /*
002: * Copyright 2004-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.springframework.webflow.core.collection;
017:
018: import java.io.IOException;
019: import java.io.ObjectInputStream;
020: import java.io.ObjectOutputStream;
021: import java.io.Serializable;
022: import java.lang.reflect.Array;
023: import java.util.ArrayList;
024: import java.util.Collections;
025: import java.util.List;
026: import java.util.Map;
027:
028: import org.springframework.binding.collection.MapAccessor;
029: import org.springframework.binding.convert.ConversionException;
030: import org.springframework.binding.convert.ConversionExecutor;
031: import org.springframework.binding.convert.ConversionService;
032: import org.springframework.binding.convert.support.DefaultConversionService;
033: import org.springframework.core.style.StylerUtils;
034: import org.springframework.util.Assert;
035: import org.springframework.web.multipart.MultipartFile;
036:
037: /**
038: * An immutable parameter map storing String-keyed, String-valued parameters
039: * in a backing {@link Map} implementation. This base provides convenient
040: * operations for accessing parameters in a typed-manner.
041: *
042: * @author Keith Donald
043: */
044: public class LocalParameterMap implements ParameterMap, Serializable {
045:
046: /**
047: * The backing map storing the parameters.
048: */
049: private Map parameters;
050:
051: /**
052: * A helper for accessing parameters. Marked transient and restored on
053: * deserialization.
054: */
055: private transient MapAccessor parameterAccessor;
056:
057: /**
058: * A helper for converting string parameter values. Marked transient and
059: * restored on deserialization.
060: */
061: private transient ConversionService conversionService;
062:
063: /**
064: * Creates a new parameter map from the provided map.
065: * <p>
066: * It is expected that the contents of the backing map adhere to the
067: * parameter map contract; that is, map entries have string keys, string
068: * values, and remain unmodifiable.
069: * @param parameters the contents of this parameter map
070: */
071: public LocalParameterMap(Map parameters) {
072: this (parameters, new DefaultConversionService());
073: }
074:
075: /**
076: * Creates a new parameter map from the provided map.
077: * <p>
078: * It is expected that the contents of the backing map adhere to the
079: * parameter map contract; that is, map entries have string keys, string
080: * values, and remain unmodifiable.
081: * @param parameters the contents of this parameter map
082: * @param conversionService a helper for performing type conversion of map
083: * entry values
084: */
085: public LocalParameterMap(Map parameters,
086: ConversionService conversionService) {
087: initParameters(parameters);
088: this .conversionService = conversionService;
089: }
090:
091: public boolean equals(Object o) {
092: if (!(o instanceof LocalParameterMap)) {
093: return false;
094: }
095: LocalParameterMap other = (LocalParameterMap) o;
096: return parameters.equals(other.parameters);
097: }
098:
099: public int hashCode() {
100: return parameters.hashCode();
101: }
102:
103: public Map asMap() {
104: return Collections.unmodifiableMap(parameterAccessor.asMap());
105: }
106:
107: public boolean isEmpty() {
108: return parameters.isEmpty();
109: }
110:
111: public int size() {
112: return parameters.size();
113: }
114:
115: public boolean contains(String parameterName) {
116: return parameters.containsKey(parameterName);
117: }
118:
119: public String get(String parameterName) {
120: return get(parameterName, (String) null);
121: }
122:
123: public String get(String parameterName, String defaultValue) {
124: if (!parameters.containsKey(parameterName)) {
125: return defaultValue;
126: }
127: Object value = parameters.get(parameterName);
128: if (value.getClass().isArray()) {
129: parameterAccessor.assertKeyValueInstanceOf(parameterName,
130: value, String[].class);
131: String[] array = (String[]) value;
132: if (array.length == 0) {
133: return null;
134: } else {
135: Object first = ((String[]) value)[0];
136: parameterAccessor.assertKeyValueInstanceOf(
137: parameterName, first, String.class);
138: return (String) first;
139: }
140:
141: } else {
142: parameterAccessor.assertKeyValueInstanceOf(parameterName,
143: value, String.class);
144: return (String) value;
145: }
146: }
147:
148: public String[] getArray(String parameterName) {
149: if (!parameters.containsKey(parameterName)) {
150: return null;
151: }
152: Object value = parameters.get(parameterName);
153: if (value.getClass().isArray()) {
154: parameterAccessor.assertKeyValueInstanceOf(parameterName,
155: value, String[].class);
156: return (String[]) value;
157: } else {
158: parameterAccessor.assertKeyValueInstanceOf(parameterName,
159: value, String.class);
160: return new String[] { (String) value };
161: }
162: }
163:
164: public Object[] getArray(String parameterName,
165: Class targetElementType) throws ConversionException {
166: String[] parameters = getArray(parameterName);
167: return parameters != null ? convert(parameters,
168: targetElementType) : null;
169: }
170:
171: public Object get(String parameterName, Class targetType)
172: throws ConversionException {
173: return get(parameterName, targetType, null);
174: }
175:
176: public Object get(String parameterName, Class targetType,
177: Object defaultValue) throws ConversionException {
178: if (defaultValue != null) {
179: assertAssignableTo(targetType, defaultValue.getClass());
180: }
181: String parameter = get(parameterName);
182: return parameter != null ? convert(parameter, targetType)
183: : defaultValue;
184: }
185:
186: public String getRequired(String parameterName)
187: throws IllegalArgumentException {
188: parameterAccessor.assertContainsKey(parameterName);
189: return get(parameterName);
190: }
191:
192: public String[] getRequiredArray(String parameterName)
193: throws IllegalArgumentException {
194: parameterAccessor.assertContainsKey(parameterName);
195: return getArray(parameterName);
196: }
197:
198: public Object[] getRequiredArray(String parameterName,
199: Class targetElementType) throws IllegalArgumentException,
200: ConversionException {
201: String[] parameters = getRequiredArray(parameterName);
202: return convert(parameters, targetElementType);
203: }
204:
205: public Object getRequired(String parameterName, Class targetType)
206: throws IllegalArgumentException, ConversionException {
207: return convert(getRequired(parameterName), targetType);
208: }
209:
210: public Number getNumber(String parameterName, Class targetType)
211: throws ConversionException {
212: assertAssignableTo(Number.class, targetType);
213: return (Number) get(parameterName, targetType);
214: }
215:
216: public Number getNumber(String parameterName, Class targetType,
217: Number defaultValue) throws ConversionException {
218: assertAssignableTo(Number.class, targetType);
219: return (Number) get(parameterName, targetType, defaultValue);
220: }
221:
222: public Number getRequiredNumber(String parameterName,
223: Class targetType) throws IllegalArgumentException,
224: ConversionException {
225: assertAssignableTo(Number.class, targetType);
226: return (Number) getRequired(parameterName, targetType);
227: }
228:
229: public Integer getInteger(String parameterName)
230: throws ConversionException {
231: return (Integer) get(parameterName, Integer.class);
232: }
233:
234: public Integer getInteger(String parameterName, Integer defaultValue)
235: throws ConversionException {
236: return (Integer) get(parameterName, Integer.class, defaultValue);
237: }
238:
239: public Integer getRequiredInteger(String parameterName)
240: throws IllegalArgumentException, ConversionException {
241: return (Integer) getRequired(parameterName, Integer.class);
242: }
243:
244: public Long getLong(String parameterName)
245: throws ConversionException {
246: return (Long) get(parameterName, Long.class);
247: }
248:
249: public Long getLong(String parameterName, Long defaultValue)
250: throws ConversionException {
251: return (Long) get(parameterName, Long.class, defaultValue);
252: }
253:
254: public Long getRequiredLong(String parameterName)
255: throws IllegalArgumentException, ConversionException {
256: return (Long) getRequired(parameterName, Long.class);
257: }
258:
259: public Boolean getBoolean(String parameterName)
260: throws ConversionException {
261: return (Boolean) get(parameterName, Boolean.class);
262: }
263:
264: public Boolean getBoolean(String parameterName, Boolean defaultValue)
265: throws ConversionException {
266: return (Boolean) get(parameterName, Boolean.class, defaultValue);
267: }
268:
269: public Boolean getRequiredBoolean(String parameterName)
270: throws IllegalArgumentException, ConversionException {
271: return (Boolean) getRequired(parameterName, Boolean.class);
272: }
273:
274: public MultipartFile getMultipartFile(String parameterName) {
275: return (MultipartFile) parameterAccessor.get(parameterName,
276: MultipartFile.class);
277: }
278:
279: public MultipartFile getRequiredMultipartFile(String parameterName)
280: throws IllegalArgumentException {
281: return (MultipartFile) parameterAccessor.getRequired(
282: parameterName, MultipartFile.class);
283: }
284:
285: public AttributeMap asAttributeMap() {
286: return new LocalAttributeMap(getMapInternal());
287: }
288:
289: /**
290: * Initializes this parameter map.
291: * @param parameters the parameters
292: */
293: protected void initParameters(Map parameters) {
294: this .parameters = parameters;
295: parameterAccessor = new MapAccessor(this .parameters);
296: }
297:
298: /**
299: * Returns the wrapped, modifiable map implementation.
300: */
301: protected Map getMapInternal() {
302: return parameters;
303: }
304:
305: // internal helpers
306:
307: /**
308: * Convert given String parameter to specified target type.
309: */
310: private Object convert(String parameter, Class targetType)
311: throws ConversionException {
312: return conversionService.getConversionExecutor(String.class,
313: targetType).execute(parameter);
314: }
315:
316: /**
317: * Convert given array of String parameters to specified target type and
318: * return the resulting array.
319: */
320: private Object[] convert(String[] parameters,
321: Class targetElementType) throws ConversionException {
322: List list = new ArrayList(parameters.length);
323: ConversionExecutor converter = conversionService
324: .getConversionExecutor(String.class, targetElementType);
325: for (int i = 0; i < parameters.length; i++) {
326: list.add(converter.execute(parameters[i]));
327: }
328: return list.toArray((Object[]) Array.newInstance(
329: targetElementType, parameters.length));
330: }
331:
332: /**
333: * Make sure clazz is assignable from requiredType.
334: */
335: private void assertAssignableTo(Class clazz, Class requiredType) {
336: Assert.isTrue(clazz.isAssignableFrom(requiredType),
337: "The provided required type must be assignable to ["
338: + clazz + "]");
339: }
340:
341: // custom serialization
342:
343: private void writeObject(ObjectOutputStream out) throws IOException {
344: out.defaultWriteObject();
345: }
346:
347: private void readObject(ObjectInputStream in) throws IOException,
348: ClassNotFoundException {
349: in.defaultReadObject();
350: parameterAccessor = new MapAccessor(parameters);
351: conversionService = new DefaultConversionService();
352: }
353:
354: public String toString() {
355: return StylerUtils.style(parameters);
356: }
357: }
|