/* * Copyright 2004-2012 the original author or authors. * * 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. */ package org.springframework.binding.collection; import java.util.Collection; import java.util.Map; import org.springframework.util.Assert; /** * A simple, generic decorator for getting attributes out of a map. May be instantiated directly or used as a base class * as a convenience. * * @author Keith Donald */ public class MapAccessor<K, V> implements MapAdaptable<K, V> { /** * The target map. */ private Map<K, V> map; /** * Creates a new attribute map accessor. * @param map the map */ public MapAccessor(Map<K, V> map) { Assert.notNull(map, "The map to decorate is required"); this.map = map; } // implementing MapAdaptable public Map<K, V> asMap() { return map; } /** * Returns a value in the map, returning null if the attribute is not present. * @param key the key * @return the value */ public V get(Object key) { return map.get(key); } /** * Returns a value in the map, returning the defaultValue if no value was found. * @param key the key * @param defaultValue the default * @return the attribute value */ public V get(Object key, V defaultValue) { if (!map.containsKey(key)) { return defaultValue; } return map.get(key); } /** * Returns a value in the map, asserting it is of the required type if present and returning <code>null</code> if * not found. * @param key the key * @param requiredType the required type * @return the value * @throws IllegalArgumentException if the key is present but the value is not of the required type */ public <T extends V> T get(Object key, Class<T> requiredType) throws IllegalArgumentException { return get(key, requiredType, null); } /** * Returns a value in the map of the specified type, returning the defaultValue if no value is found. * @param key the key * @param requiredType the required type * @param defaultValue the default * @return the attribute value * @throws IllegalArgumentException if the key is present but the value is not of the required type */ public <T extends V> T get(Object key, Class<T> requiredType, T defaultValue) { if (!map.containsKey(key)) { return defaultValue; } return assertKeyValueOfType(key, requiredType); } /** * Returns a value in the map, throwing an exception if the attribute is not present and of the correct type. * @param key the key * @return the value */ public V getRequired(Object key) throws IllegalArgumentException { assertContainsKey(key); return map.get(key); } /** * Returns an value in the map, asserting it is present and of the required type. * @param key the key * @param requiredType the required type * @return the value */ public <T extends V> T getRequired(Object key, Class<T> requiredType) throws IllegalArgumentException { assertContainsKey(key); return assertKeyValueOfType(key, requiredType); } /** * Returns a string value in the map, returning <code>null</code> if no value was found. * @param key the key * @return the string value * @throws IllegalArgumentException if the key is present but the value is not a string */ public String getString(Object key) throws IllegalArgumentException { return getString(key, null); } /** * Returns a string value in the map, returning the defaultValue if no value was found. * @param key the key * @param defaultValue the default * @return the string value * @throws IllegalArgumentException if the key is present but the value is not a string */ public String getString(Object key, String defaultValue) throws IllegalArgumentException { if (!map.containsKey(key)) { return defaultValue; } return assertKeyValueOfType(key, String.class); } /** * Returns a string value in the map, throwing an exception if the attribute is not present and of the correct type. * @param key the key * @return the string value * @throws IllegalArgumentException if the key is not present or present but the value is not a string */ public String getRequiredString(Object key) throws IllegalArgumentException { assertContainsKey(key); return assertKeyValueOfType(key, String.class); } /** * Returns a collection value in the map, returning <code>null</code> if no value was found. * @param key the key * @return the collection value * @throws IllegalArgumentException if the key is present but the value is not a collection */ @SuppressWarnings("unchecked") public Collection<V> getCollection(Object key) throws IllegalArgumentException { if (!map.containsKey(key)) { return null; } return assertKeyValueOfType(key, Collection.class); } /** * Returns a collection value in the map, asserting it is of the required type if present and returning * <code>null</code> if not found. * @param key the key * @return the collection value * @throws IllegalArgumentException if the key is present but the value is not a collection */ public <T extends Collection<V>> T getCollection(Object key, Class<T> requiredType) throws IllegalArgumentException { if (!map.containsKey(key)) { return null; } assertAssignableTo(Collection.class, requiredType); return assertKeyValueOfType(key, requiredType); } /** * Returns a collection value in the map, throwing an exception if not found. * @param key the key * @return the collection value * @throws IllegalArgumentException if the key is not present or present but the value is not a collection */ @SuppressWarnings("unchecked") public Collection<V> getRequiredCollection(Object key) throws IllegalArgumentException { assertContainsKey(key); return assertKeyValueOfType(key, Collection.class); } /** * Returns a collection value in the map, asserting it is of the required type if present and throwing an exception * if not found. * @param key the key * @return the collection value * @throws IllegalArgumentException if the key is not present or present but the value is not a collection of the * required type */ public <T extends Collection<V>> T getRequiredCollection(Object key, Class<T> requiredType) throws IllegalArgumentException { assertContainsKey(key); assertAssignableTo(Collection.class, requiredType); return assertKeyValueOfType(key, requiredType); } /** * Returns a array value in the map, asserting it is of the required type if present and returning <code>null</code> * if not found. * @param key the key * @return the array value * @throws IllegalArgumentException if the key is present but the value is not an array of the required type */ public <T extends V> T[] getArray(Object key, Class<? extends T[]> requiredType) throws IllegalArgumentException { assertAssignableTo(Object[].class, requiredType); if (!map.containsKey(key)) { return null; } return assertKeyValueOfType(key, requiredType); } /** * Returns an array value in the map, asserting it is of the required type if present and throwing an exception if * not found. * @param key the key * @return the array value * @throws IllegalArgumentException if the key is not present or present but the value is not a array of the * required type */ public <T extends V> T[] getRequiredArray(Object key, Class<? extends T[]> requiredType) throws IllegalArgumentException { assertContainsKey(key); assertAssignableTo(Object[].class, requiredType); return assertKeyValueOfType(key, requiredType); } /** * Returns a number value in the map that is of the specified type, returning <code>null</code> if no value was * found. * @param key the key * @param requiredType the required number type * @return the number value * @throws IllegalArgumentException if the key is present but the value is not a number of the required type */ public <T extends Number> T getNumber(Object key, Class<T> requiredType) throws IllegalArgumentException { return getNumber(key, requiredType, null); } /** * Returns a number attribute value in the map of the specified type, returning the defaultValue if no value was * found. * @param key the attribute name * @return the number value * @param defaultValue the default * @throws IllegalArgumentException if the key is present but the value is not a number of the required type */ public <T extends Number> T getNumber(Object key, Class<T> requiredType, T defaultValue) throws IllegalArgumentException { if (!map.containsKey(key)) { return defaultValue; } assertAssignableTo(Number.class, requiredType); return assertKeyValueOfType(key, requiredType); } /** * Returns a number value in the map, throwing an exception if the attribute is not present and of the correct type. * @param key the key * @return the number value * @throws IllegalArgumentException if the key is not present or present but the value is not a number of the * required type */ public <T extends Number> T getRequiredNumber(Object key, Class<T> requiredType) throws IllegalArgumentException { assertContainsKey(key); return assertKeyValueOfType(key, requiredType); } /** * Returns an integer value in the map, returning <code>null</code> if no value was found. * @param key the key * @return the integer value * @throws IllegalArgumentException if the key is present but the value is not an integer */ public Integer getInteger(Object key) throws IllegalArgumentException { return getInteger(key, null); } /** * Returns an integer value in the map, returning the defaultValue if no value was found. * @param key the key * @param defaultValue the default * @return the integer value * @throws IllegalArgumentException if the key is present but the value is not an integer */ public Integer getInteger(Object key, Integer defaultValue) throws IllegalArgumentException { return getNumber(key, Integer.class, defaultValue); } /** * Returns an integer value in the map, throwing an exception if the value is not present and of the correct type. * @param key the attribute name * @return the integer attribute value * @throws IllegalArgumentException if the key is not present or present but the value is not an integer */ public Integer getRequiredInteger(Object key) throws IllegalArgumentException { return getRequiredNumber(key, Integer.class); } /** * Returns a long value in the map, returning <code>null</code> if no value was found. * @param key the key * @return the long value * @throws IllegalArgumentException if the key is present but not a long */ public Long getLong(Object key) throws IllegalArgumentException { return getLong(key, null); } /** * Returns a long value in the map, returning the defaultValue if no value was found. * @param key the key * @param defaultValue the default * @return the long attribute value * @throws IllegalArgumentException if the key is present but the value is not a long */ public Long getLong(Object key, Long defaultValue) throws IllegalArgumentException { return getNumber(key, Long.class, defaultValue); } /** * Returns a long value in the map, throwing an exception if the value is not present and of the correct type. * @param key the key * @return the long attribute value * @throws IllegalArgumentException if the key is not present or present but the value is not a long */ public Long getRequiredLong(Object key) throws IllegalArgumentException { return getRequiredNumber(key, Long.class); } /** * Returns a boolean value in the map, returning <code>null</code> if no value was found. * @param key the key * @return the boolean value * @throws IllegalArgumentException if the key is present but the value is not a boolean */ public Boolean getBoolean(Object key) throws IllegalArgumentException { return getBoolean(key, null); } /** * Returns a boolean value in the map, returning the defaultValue if no value was found. * @param key the key * @param defaultValue the default * @return the boolean value * @throws IllegalArgumentException if the key is present but the value is not a boolean */ public Boolean getBoolean(Object key, Boolean defaultValue) throws IllegalArgumentException { if (!map.containsKey(key)) { return defaultValue; } return assertKeyValueOfType(key, Boolean.class); } /** * Returns a boolean value in the map, throwing an exception if the value is not present and of the correct type. * @param key the attribute * @return the boolean value * @throws IllegalArgumentException if the key is not present or present but the value is not a boolean */ public Boolean getRequiredBoolean(Object key) throws IllegalArgumentException { assertContainsKey(key); return assertKeyValueOfType(key, Boolean.class); } /** * Asserts that the attribute is present in the attribute map. * @param key the key * @throws IllegalArgumentException if the key is not present */ public void assertContainsKey(Object key) throws IllegalArgumentException { if (!map.containsKey(key)) { throw new IllegalArgumentException("Required attribute '" + key + "' is not present in map; attributes present are [" + asMap() + "]"); } } /** * Indicates if the attribute is present in the attribute map and of the required type. * @param key the attribute name * @return true if present and of the required type, false if not present. */ public boolean containsKey(Object key, Class<?> requiredType) throws IllegalArgumentException { if (map.containsKey(key)) { assertKeyValueOfType(key, requiredType); return true; } else { return false; } } /** * Assert that value of the map key, if non-null, is of the required type. * @param key the attribute name * @param requiredType the required attribute value type * @return the attribute value */ public <T> T assertKeyValueOfType(Object key, Class<T> requiredType) { return assertKeyValueInstanceOf(key, map.get(key), requiredType); } /** * Assert that the key value, if non null, is an instance of the required type. * @param key the key * @param value the value * @param requiredType the required type * @return the value */ @SuppressWarnings("unchecked") public <T> T assertKeyValueInstanceOf(Object key, Object value, Class<T> requiredType) { Assert.notNull(requiredType, "The required type to assert is required"); if (value != null && !requiredType.isInstance(value)) { throw new IllegalArgumentException("Map key '" + key + "' has value [" + value + "] that is not of expected type [" + requiredType + "], instead it is of type [" + value.getClass().getName() + "]"); } return (T) value; } private void assertAssignableTo(Class<?> clazz, Class<?> requiredType) { Assert.isTrue(clazz.isAssignableFrom(requiredType), "The provided required type must be assignable to [" + clazz + "]"); } }