001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.util;
021
022import java.util.Collection;
023import java.util.Map;
024import java.util.Set;
025import java.util.concurrent.ConcurrentHashMap;
026import java.util.concurrent.ConcurrentMap;
027
028import org.apache.mina.core.buffer.IoBuffer;
029
030/**
031 * This map is specially useful when reads are much more frequent than writes and 
032 * if the cost of instantiating the values is high like allocating an 
033 * {@link IoBuffer} for example.
034 *  
035 * Based on the final implementation of Memoizer written by Brian Goetz and Tim
036 * Peierls. This implementation will return an
037 * {@link UnsupportedOperationException} on each method that is not intended to
038 * be called by user code for performance reasons.
039 * 
040 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
041 * @since MINA 2.0.0-M2
042 */
043public class LazyInitializedCacheMap<K, V> implements Map<K, V> {
044    private ConcurrentMap<K, LazyInitializer<V>> cache;
045
046    /**
047     * This class provides a noop {@link LazyInitializer} meaning it 
048     * will return the same object it received when instantiated.
049     */
050    public class NoopInitializer extends LazyInitializer<V> {
051        private V value;
052
053        public NoopInitializer(V value) {
054            this.value = value;
055        }
056
057        public V init() {
058            return value;
059        }
060    }
061
062    /**
063     * Default constructor. Uses the default parameters to initialize its internal
064     * {@link ConcurrentHashMap}.
065     */
066    public LazyInitializedCacheMap() {
067        this.cache = new ConcurrentHashMap<K, LazyInitializer<V>>();
068    }
069
070    /**
071     * This constructor allows to provide a fine tuned {@link ConcurrentHashMap}
072     * to stick with each special case the user needs.
073     * 
074     * @param map The map to use as a cache
075     */
076    public LazyInitializedCacheMap(final ConcurrentHashMap<K, LazyInitializer<V>> map) {
077        this.cache = map;
078    }
079
080    /**
081     * {@inheritDoc}
082     */
083    public V get(Object key) {
084        LazyInitializer<V> c = cache.get(key);
085        if (c != null) {
086            return c.get();
087        }
088
089        return null;
090    }
091
092    /**
093     * {@inheritDoc}
094     */
095    public V remove(Object key) {
096        LazyInitializer<V> c = cache.remove(key);
097        if (c != null) {
098            return c.get();
099        }
100
101        return null;
102    }
103
104    /**
105     * If the specified key is not already associated
106     * with a value, associate it with the given value.
107     * This is equivalent to
108     * <pre>
109     *   if (!map.containsKey(key))
110     *       return map.put(key, value);
111     *   else
112     *       return map.get(key);</pre>
113     * except that the action is performed atomically.
114     *
115     * @param key key with which the specified value is to be associated
116     * @param value a lazy initialized value object.
117     * 
118     * @return the previous value associated with the specified key,
119     *         or <tt>null</tt> if there was no mapping for the key
120     */
121    public V putIfAbsent(K key, LazyInitializer<V> value) {
122        LazyInitializer<V> v = cache.get(key);
123        if (v == null) {
124            v = cache.putIfAbsent(key, value);
125            if (v == null) {
126                return value.get();
127            }
128        }
129
130        return v.get();
131    }
132
133    /**
134     * {@inheritDoc}
135     */
136    public V put(K key, V value) {
137        LazyInitializer<V> c = cache.put(key, new NoopInitializer(value));
138        if (c != null) {
139            return c.get();
140        }
141
142        return null;
143    }
144
145    /**
146     * Throws {@link UnsupportedOperationException} as this method would imply
147     *         performance drops.
148     */
149    public boolean containsValue(Object value) {
150        throw new UnsupportedOperationException();
151    }
152
153    /**
154     * Throws {@link UnsupportedOperationException} as this method would imply
155     *         performance drops.
156     */
157    public Collection<V> values() {
158        throw new UnsupportedOperationException();
159    }
160
161    /**
162     * Throws {@link UnsupportedOperationException} as this method would imply
163     *         performance drops.
164     */
165    public Set<java.util.Map.Entry<K, V>> entrySet() {
166        throw new UnsupportedOperationException();
167    }
168
169    /**
170     * {@inheritDoc}
171     */
172    public void putAll(Map<? extends K, ? extends V> m) {
173        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
174            cache.put(e.getKey(), new NoopInitializer(e.getValue()));
175        }
176    }
177
178    /**
179     * @return return the values from the cache
180     */
181    public Collection<LazyInitializer<V>> getValues() {
182        return cache.values();
183    }
184
185    /**
186     * {@inheritDoc}
187     */
188    public void clear() {
189        cache.clear();
190    }
191
192    /**
193     * {@inheritDoc}
194     */
195    public boolean containsKey(Object key) {
196        return cache.containsKey(key);
197    }
198
199    /**
200     * {@inheritDoc}
201     */
202    public boolean isEmpty() {
203        return cache.isEmpty();
204    }
205
206    /**
207     * {@inheritDoc}
208     */
209    public Set<K> keySet() {
210        return cache.keySet();
211    }
212
213    /**
214     * {@inheritDoc}
215     */
216    public int size() {
217        return cache.size();
218    }
219}