1   package com.atlassian.seraph.util;
2   
3   import java.util.Collection;
4   import java.util.Map;
5   import java.util.concurrent.ConcurrentHashMap;
6   import java.util.concurrent.ConcurrentMap;
7   
8   /**
9    * Caches the results of the {@link PathMapper}
10   */
11  public class CachedPathMapper implements IPathMapper
12  {
13      private static final String NULL = new String("NULL");
14  
15      private final PathMapper delegate = new PathMapper();
16      private final ConcurrentMap<String, String> cacheMap;
17      private final ConcurrentMap<String, Collection<String>> cacheAllMap;
18  
19      /**
20       * Creates a CachedPathMapper object that will cache the results of the {@link #get(String)} and the {@link #getAll(String)} calls.
21       * <p>
22       * Use the passed in maps for caches. The maps must be thread-safe as far as {@link Map#get(Object)} calls are concerned as gets may happen
23       * concurrently. An access ordered map should be wrapped in a synchronizedMap wrapper.
24       *
25       * @param cacheMap
26       *            for caching results of {@link #get(String)} calls
27       * @param cacheAllMap
28       *            for caching results of {@link #getAll(String)} calls
29       */
30      public CachedPathMapper()
31      {
32          this(new ConcurrentHashMap<String, String>(), new ConcurrentHashMap<String, Collection<String>>());
33      }
34  
35      /**
36       * Creates a CachedPathMapper object that will use cacheMap to cache the results of the {@link #get(String)} calls and cacheAllMap to cache the
37       * results of the {@link #getAll(String)} class.
38       * <p>
39       * Use the passed in maps for caches. The maps must be thread-safe as far as {@link Map#get(Object)} calls are concerned as gets may happen
40       * concurrently. An access ordered map should be wrapped in a synchronizedMap wrapper.
41       *
42       * @param cacheMap
43       *            for caching results of {@link #get(String)} calls
44       * @param cacheAllMap
45       *            for caching results of {@link #getAll(String)} calls
46       */
47      public CachedPathMapper(final ConcurrentMap<String, String> cacheMap, final ConcurrentMap<String, Collection<String>> cacheAllMap)
48      {
49          this.cacheMap = cacheMap;
50          this.cacheAllMap = cacheAllMap;
51      }
52  
53      public String get(final String path)
54      {
55          // Check the cache
56          String result = cacheMap.get(path);
57          while (result == null)
58          {
59              String value = delegate.get(path);
60              if (value == null)
61              {
62                  value = NULL;
63              }
64              cacheMap.putIfAbsent(path, value);
65              result = cacheMap.get(path);
66          }
67          return (result == NULL) ? null : result;
68      }
69  
70      public Collection<String> getAll(final String path)
71      {
72          Collection<String> result = cacheAllMap.get(path);
73          // Check the cache
74          while (result == null)
75          {
76              cacheAllMap.putIfAbsent(path, delegate.getAll(path));
77              // The result for this key is cached, return the value
78              result = cacheAllMap.get(path);
79          }
80          return result;
81      }
82  
83      public void set(final Map<String, String> patterns)
84      {
85          for (final Map.Entry<String, String> entry2 : patterns.entrySet())
86          {
87              final Map.Entry<String, String> entry = entry2;
88              put(entry.getKey(), entry.getValue());
89          }
90      }
91  
92      public void put(final String key, final String pattern)
93      {
94          // Let the delegate mapper update the patterns
95          delegate.put(key, pattern);
96          cacheMap.remove(key);
97          cacheAllMap.remove(key);
98      }
99  
100     @Override
101     public String toString()
102     {
103         return delegate.toString();
104     }
105 }