001: /*
002: * $Id: MapCombiner.java 10489 2008-01-23 17:53:38Z dfeist $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.util;
012:
013: import java.util.Collection;
014: import java.util.HashMap;
015: import java.util.Iterator;
016: import java.util.List;
017: import java.util.Map;
018: import java.util.Set;
019:
020: import org.apache.commons.logging.Log;
021: import org.apache.commons.logging.LogFactory;
022:
023: /**
024: * This allows a collection (list) of maps to be defined in Spring, via the "list" property, and
025: * then presents all the maps as a single combine map at run time. For efficiency the combination
026: * of maps is done once and then cached.
027: */
028: public class MapCombiner implements Map {
029:
030: public static final String LIST = "list"; // the setter/getter
031: public static final int UNLIMITED_DEPTH = -1;
032:
033: private Log logger = LogFactory.getLog(getClass());
034: private int maxDepth = UNLIMITED_DEPTH;
035: private List list;
036: private Map cachedMerge = new HashMap();
037: private boolean isMerged = false;
038:
039: private synchronized Map getCachedMerge() {
040: if (!isMerged) {
041: for (Iterator maps = list.iterator(); maps.hasNext();) {
042: mergeMaps(maxDepth, cachedMerge, (Map) maps.next());
043: }
044: isMerged = true;
045: }
046: return cachedMerge;
047: }
048:
049: public void setMaxDepth(int maxDepth) {
050: this .maxDepth = maxDepth;
051: }
052:
053: private void mergeMaps(int headroom, Map accumulator, Map extra) {
054: for (Iterator keys = extra.keySet().iterator(); keys.hasNext();) {
055: Object key = keys.next();
056: Object valueExtra = extra.get(key);
057: if (accumulator.containsKey(key)) {
058: Object valueOriginal = accumulator.get(key);
059: if (valueExtra instanceof Map
060: && valueOriginal instanceof Map
061: && headroom != 0) {
062: mergeMaps(headroom - 1, (Map) valueOriginal,
063: (Map) valueExtra);
064: } else if (valueExtra instanceof Collection
065: && valueOriginal instanceof Collection
066: && headroom != 0) {
067: ((Collection) valueOriginal)
068: .addAll((Collection) valueExtra);
069: } else {
070: if (logger.isDebugEnabled()) {
071: logger.debug("Overwriting " + valueOriginal
072: + " for " + key + " during map merge");
073: }
074: accumulator.put(key, valueExtra);
075: }
076: } else {
077: accumulator.put(key, valueExtra);
078: }
079: }
080: }
081:
082: public void setList(List list) {
083: assertNotMerged();
084: this .list = list;
085: }
086:
087: public List getList() {
088: assertNotMerged();
089: return list;
090: }
091:
092: private synchronized void assertNotMerged() {
093: if (isMerged) {
094: throw new IllegalStateException(
095: "Maps have already been merged");
096: }
097: }
098:
099: // hashcode, equals and toString don't trigger merge
100:
101: public int hashCode() {
102: return cachedMerge.hashCode();
103: }
104:
105: public boolean equals(Object o) {
106: return cachedMerge.equals(o);
107: }
108:
109: public String toString() {
110: if (isMerged) {
111: return "merged: " + cachedMerge.toString();
112: } else {
113: return "unmerged: "
114: + (null == list ? null : list.toString());
115: }
116: }
117:
118: public int size() {
119: return getCachedMerge().size();
120: }
121:
122: public void clear() {
123: getCachedMerge().clear();
124: }
125:
126: public boolean isEmpty() {
127: return getCachedMerge().isEmpty();
128: }
129:
130: public boolean containsKey(Object key) {
131: return getCachedMerge().containsKey(key);
132: }
133:
134: public boolean containsValue(Object value) {
135: return getCachedMerge().containsValue(value);
136: }
137:
138: public Collection values() {
139: return getCachedMerge().values();
140: }
141:
142: public void putAll(Map t) {
143: getCachedMerge().putAll(t);
144: }
145:
146: public Set entrySet() {
147: return getCachedMerge().entrySet();
148: }
149:
150: public Set keySet() {
151: return getCachedMerge().keySet();
152: }
153:
154: public Object get(Object key) {
155: return getCachedMerge().get(key);
156: }
157:
158: public Object remove(Object key) {
159: return getCachedMerge().remove(key);
160: }
161:
162: public Object put(Object key, Object value) {
163: return getCachedMerge().put(key, value);
164: }
165:
166: }
|