View Javadoc

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