/* * 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.webflow.core.collection; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.springframework.binding.collection.MapAccessor; import org.springframework.core.style.StylerUtils; import org.springframework.util.Assert; /** * A generic, mutable attribute map with string keys. * * @author Keith Donald */ public class LocalAttributeMap<V> implements MutableAttributeMap<V>, Serializable { /** * The backing map storing the attributes. */ private Map<String, V> attributes; /** * A helper for accessing attributes. Marked transient and restored on deserialization. */ private transient MapAccessor<String, V> attributeAccessor; /** * Creates a new attribute map, initially empty. */ public LocalAttributeMap() { initAttributes(createTargetMap()); } /** * Creates a new attribute map, initially empty. * @param size the initial size * @param loadFactor the load factor */ public LocalAttributeMap(int size, int loadFactor) { initAttributes(createTargetMap(size, loadFactor)); } /** * Creates a new attribute map with a single entry. */ public LocalAttributeMap(String attributeName, V attributeValue) { initAttributes(createTargetMap(1, 1)); put(attributeName, attributeValue); } /** * Creates a new attribute map wrapping the specified map. */ public LocalAttributeMap(Map<String, V> map) { Assert.notNull(map, "The target map is required"); initAttributes(map); } // implementing attribute map public Map<String, V> asMap() { return attributeAccessor.asMap(); } public int size() { return attributes.size(); } public V get(String attributeName) { return attributes.get(attributeName); } public boolean isEmpty() { return attributes.isEmpty(); } public boolean contains(String attributeName) { return attributes.containsKey(attributeName); } public boolean contains(String attributeName, Class<? extends V> requiredType) throws IllegalArgumentException { return attributeAccessor.containsKey(attributeName, requiredType); } public V get(String attributeName, V defaultValue) { return attributeAccessor.get(attributeName, defaultValue); } public <T extends V> T get(String attributeName, Class<T> requiredType) throws IllegalArgumentException { return attributeAccessor.get(attributeName, requiredType); } public <T extends V> T get(String attributeName, Class<T> requiredType, T defaultValue) throws IllegalStateException { return attributeAccessor.get(attributeName, requiredType, defaultValue); } public V getRequired(String attributeName) throws IllegalArgumentException { return attributeAccessor.getRequired(attributeName); } public <T extends V> T getRequired(String attributeName, Class<T> requiredType) throws IllegalArgumentException { return attributeAccessor.getRequired(attributeName, requiredType); } public String getString(String attributeName) throws IllegalArgumentException { return attributeAccessor.getString(attributeName); } public String getString(String attributeName, String defaultValue) throws IllegalArgumentException { return attributeAccessor.getString(attributeName, defaultValue); } public String getRequiredString(String attributeName) throws IllegalArgumentException { return attributeAccessor.getRequiredString(attributeName); } public Collection<V> getCollection(String attributeName) throws IllegalArgumentException { return attributeAccessor.getCollection(attributeName); } public <T extends Collection<V>> T getCollection(String attributeName, Class<T> requiredType) throws IllegalArgumentException { return attributeAccessor.getCollection(attributeName, requiredType); } public Collection<V> getRequiredCollection(String attributeName) throws IllegalArgumentException { return attributeAccessor.getRequiredCollection(attributeName); } public <T extends Collection<V>> T getRequiredCollection(String attributeName, Class<T> requiredType) throws IllegalArgumentException { return attributeAccessor.getRequiredCollection(attributeName, requiredType); } public <T extends V> T[] getArray(String attributeName, Class<? extends T[]> requiredType) throws IllegalArgumentException { return attributeAccessor.getArray(attributeName, requiredType); } public <T extends V> T[] getRequiredArray(String attributeName, Class<? extends T[]> requiredType) throws IllegalArgumentException { return attributeAccessor.getRequiredArray(attributeName, requiredType); } public <T extends Number> T getNumber(String attributeName, Class<T> requiredType) throws IllegalArgumentException { return attributeAccessor.getNumber(attributeName, requiredType); } public <T extends Number> T getNumber(String attributeName, Class<T> requiredType, T defaultValue) throws IllegalArgumentException { return attributeAccessor.getNumber(attributeName, requiredType, defaultValue); } public <T extends Number> T getRequiredNumber(String attributeName, Class<T> requiredType) throws IllegalArgumentException { return attributeAccessor.getRequiredNumber(attributeName, requiredType); } public Integer getInteger(String attributeName) throws IllegalArgumentException { return attributeAccessor.getInteger(attributeName); } public Integer getInteger(String attributeName, Integer defaultValue) throws IllegalArgumentException { return attributeAccessor.getInteger(attributeName, defaultValue); } public Integer getRequiredInteger(String attributeName) throws IllegalArgumentException { return attributeAccessor.getRequiredInteger(attributeName); } public Long getLong(String attributeName) throws IllegalArgumentException { return attributeAccessor.getLong(attributeName); } public Long getLong(String attributeName, Long defaultValue) throws IllegalArgumentException { return attributeAccessor.getLong(attributeName, defaultValue); } public Long getRequiredLong(String attributeName) throws IllegalArgumentException { return attributeAccessor.getRequiredLong(attributeName); } public Boolean getBoolean(String attributeName) throws IllegalArgumentException { return attributeAccessor.getBoolean(attributeName); } public Boolean getBoolean(String attributeName, Boolean defaultValue) throws IllegalArgumentException { return attributeAccessor.getBoolean(attributeName, defaultValue); } public Boolean getRequiredBoolean(String attributeName) throws IllegalArgumentException { return attributeAccessor.getRequiredBoolean(attributeName); } public AttributeMap<V> union(AttributeMap<? extends V> attributes) { if (attributes == null) { return new LocalAttributeMap<V>(getMapInternal()); } else { Map<String, V> map = createTargetMap(); map.putAll(getMapInternal()); map.putAll(attributes.asMap()); return new LocalAttributeMap<V>(map); } } // implementing MutableAttributeMap public V put(String attributeName, V attributeValue) { return getMapInternal().put(attributeName, attributeValue); } public MutableAttributeMap<V> putAll(AttributeMap<? extends V> attributes) { if (attributes == null) { return this; } getMapInternal().putAll(attributes.asMap()); return this; } public MutableAttributeMap<V> removeAll(MutableAttributeMap<? extends V> attributes) { if (attributes == null) { return this; } Map<String, V> internal = getMapInternal(); for (String attribute : attributes.asMap().keySet()) { internal.remove(attribute); } return this; } public Object remove(String attributeName) { return getMapInternal().remove(attributeName); } public Object extract(String attributeName) { Map<String, V> map = getMapInternal(); if (map.containsKey(attributeName)) { Object value = map.get(attributeName); map.remove(attributeName); return value; } else { return null; } } public MutableAttributeMap<V> clear() throws UnsupportedOperationException { getMapInternal().clear(); return this; } public MutableAttributeMap<V> replaceWith(AttributeMap<? extends V> attributes) throws UnsupportedOperationException { clear(); putAll(attributes); return this; } // helpers for subclasses /** * Initializes this attribute map. * @param attributes the attributes */ protected void initAttributes(Map<String, V> attributes) { this.attributes = attributes; attributeAccessor = new MapAccessor<String, V>(this.attributes); } /** * Returns the wrapped, modifiable map implementation. */ protected Map<String, V> getMapInternal() { return attributes; } // helpers /** * Factory method that returns the target map storing the data in this attribute map. * @return the target map */ protected Map<String, V> createTargetMap() { return new HashMap<String, V>(); } /** * Factory method that returns the target map storing the data in this attribute map. * @param size the initial size of the map * @param loadFactor the load factor * @return the target map */ protected Map<String, V> createTargetMap(int size, int loadFactor) { return new HashMap<String, V>(size, loadFactor); } @SuppressWarnings("unchecked") public boolean equals(Object o) { if (!(o instanceof LocalAttributeMap)) { return false; } LocalAttributeMap<V> other = (LocalAttributeMap<V>) o; return getMapInternal().equals(other.getMapInternal()); } public int hashCode() { return getMapInternal().hashCode(); } // custom serialization private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); attributeAccessor = new MapAccessor<String, V>(attributes); } public String toString() { return StylerUtils.style(attributes); } }