View Javadoc

1   /*
2    * $Id: MapCombiner.java 10489 2008-01-23 17:53:38Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.util;
12  
13  import java.util.Collection;
14  import java.util.HashMap;
15  import java.util.Iterator;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.Set;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  
23  /**
24   * This allows a collection (list) of maps to be defined in Spring, via the "list" property, and
25   * then presents all the maps as a single combine map at run time.  For efficiency the combination
26   * of maps is done once and then cached.
27   */
28  public class MapCombiner implements Map
29  {
30  
31      public static final String LIST = "list"; // the setter/getter
32      public static final int UNLIMITED_DEPTH = -1;
33  
34      private Log logger = LogFactory.getLog(getClass());
35      private int maxDepth = UNLIMITED_DEPTH;
36      private List list;
37      private Map cachedMerge = new HashMap();
38      private boolean isMerged = false;
39  
40      private synchronized Map getCachedMerge()
41      {
42          if (!isMerged)
43          {
44              for (Iterator maps = list.iterator(); maps.hasNext();)
45              {
46                  mergeMaps(maxDepth, cachedMerge, (Map) maps.next());
47              }
48              isMerged = true;
49          }
50          return cachedMerge;
51      }
52  
53      public void setMaxDepth(int maxDepth)
54      {
55          this.maxDepth = maxDepth;
56      }
57  
58      private void mergeMaps(int headroom, Map accumulator, Map extra)
59      {
60          for (Iterator keys = extra.keySet().iterator(); keys.hasNext();)
61          {
62              Object key = keys.next();
63              Object valueExtra = extra.get(key);
64              if (accumulator.containsKey(key))
65              {
66                  Object valueOriginal = accumulator.get(key);
67                  if (valueExtra instanceof Map && valueOriginal instanceof Map && headroom != 0)
68                  {
69                      mergeMaps(headroom - 1, (Map) valueOriginal, (Map) valueExtra);
70                  }
71                  else if (valueExtra instanceof Collection && valueOriginal instanceof Collection && headroom != 0)
72                  {
73                      ((Collection) valueOriginal).addAll((Collection) valueExtra);
74                  }
75                  else
76                  {
77                      if (logger.isDebugEnabled())
78                      {
79                          logger.debug("Overwriting " + valueOriginal + " for " + key + " during map merge");
80                      }
81                      accumulator.put(key, valueExtra);
82                  }
83              }
84              else
85              {
86                  accumulator.put(key, valueExtra);
87              }
88          }
89      }
90  
91      public void setList(List list)
92      {
93          assertNotMerged();
94          this.list = list;
95      }
96  
97      public List getList()
98      {
99          assertNotMerged();
100         return list;
101     }
102 
103     private synchronized void assertNotMerged()
104     {
105         if (isMerged)
106         {
107             throw new IllegalStateException("Maps have already been merged");
108         }
109     }
110 
111     // hashcode, equals and toString don't trigger merge
112 
113     public int hashCode()
114     {
115         return cachedMerge.hashCode();
116     }
117 
118     public boolean equals(Object o)
119     {
120         return cachedMerge.equals(o);
121     }
122 
123     public String toString()
124     {
125         if (isMerged)
126         {
127             return "merged: " + cachedMerge.toString();
128         }
129         else
130         {
131             return "unmerged: " + (null == list ? null : list.toString());
132         }
133     }
134 
135     public int size()
136     {
137         return getCachedMerge().size();
138     }
139 
140     public void clear()
141     {
142         getCachedMerge().clear();
143     }
144 
145     public boolean isEmpty()
146     {
147         return getCachedMerge().isEmpty();
148     }
149 
150     public boolean containsKey(Object key)
151     {
152         return getCachedMerge().containsKey(key);
153     }
154 
155     public boolean containsValue(Object value)
156     {
157         return getCachedMerge().containsValue(value);
158     }
159 
160     public Collection values()
161     {
162         return getCachedMerge().values();
163     }
164 
165     public void putAll(Map t)
166     {
167         getCachedMerge().putAll(t);
168     }
169 
170     public Set entrySet()
171     {
172         return getCachedMerge().entrySet();
173     }
174 
175     public Set keySet()
176     {
177         return getCachedMerge().keySet();
178     }
179 
180     public Object get(Object key)
181     {
182         return getCachedMerge().get(key);
183     }
184 
185     public Object remove(Object key)
186     {
187         return getCachedMerge().remove(key);
188     }
189 
190     public Object put(Object key, Object value)
191     {
192         return getCachedMerge().put(key, value);
193     }
194 
195 }