001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 The ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package org.conqat.lib.commons.collections;
018
019import java.util.Collection;
020import java.util.Map;
021import java.util.Set;
022
023import org.conqat.lib.commons.assertion.CCSMAssert;
024
025/**
026 * A hybrid map is a map which starts with one map implementation, but switches
027 * to another one after a certain size is reached.
028 */
029public abstract class HybridMapBase<K, V> implements Map<K, V> {
030
031        /** The inner map. */
032        private Map<K, V> map;
033
034        /** Constructor. */
035        protected HybridMapBase(Map<K, V> initialMap) {
036                CCSMAssert.isNotNull(initialMap);
037                map = initialMap;
038        }
039
040        /**
041         * Template method for deciding that a switch of map implementation should
042         * be performed before the next insertion. This will be called right before
043         * each put operation. If this returns true, the new map implementation is
044         * obtained via {@link #obtainNewMap()} and all values are copied.
045         * 
046         * @param map
047         *            the currently used map.
048         */
049        protected abstract boolean shouldSwitch(Map<K, V> map);
050
051        /**
052         * Template method for obtaining a new map implementation after
053         * {@link #shouldSwitch(Map)} returned true.
054         */
055        protected abstract Map<K, V> obtainNewMap();
056
057        /** {@inheritDoc} */
058        @Override
059        public void clear() {
060                map.clear();
061        }
062
063        /** {@inheritDoc} */
064        @Override
065        public boolean containsKey(Object key) {
066                return map.containsKey(key);
067        }
068
069        /** {@inheritDoc} */
070        @Override
071        public boolean containsValue(Object value) {
072                return map.containsValue(value);
073        }
074
075        /** {@inheritDoc} */
076        @Override
077        public Set<java.util.Map.Entry<K, V>> entrySet() {
078                return map.entrySet();
079        }
080
081        /** {@inheritDoc} */
082        @Override
083        public V get(Object key) {
084                return map.get(key);
085        }
086
087        /** {@inheritDoc} */
088        @Override
089        public boolean isEmpty() {
090                return map.isEmpty();
091        }
092
093        /** {@inheritDoc} */
094        @Override
095        public Set<K> keySet() {
096                return map.keySet();
097        }
098
099        /** {@inheritDoc} */
100        @Override
101        public V put(K key, V value) {
102                if (shouldSwitch(map)) {
103                        Map<K, V> oldMap = map;
104                        map = obtainNewMap();
105                        map.putAll(oldMap);
106                }
107
108                return map.put(key, value);
109        }
110
111        /** {@inheritDoc} */
112        @Override
113        public void putAll(Map<? extends K, ? extends V> m) {
114                // must use explicit loop instead of putAll, as otherwise switching of
115                // the map implementation will not happen
116                for (java.util.Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
117                        put(entry.getKey(), entry.getValue());
118                }
119        }
120
121        /** {@inheritDoc} */
122        @Override
123        public V remove(Object key) {
124                return map.remove(key);
125        }
126
127        /** {@inheritDoc} */
128        @Override
129        public int size() {
130                return map.size();
131        }
132
133        /** {@inheritDoc} */
134        @Override
135        public Collection<V> values() {
136                return map.values();
137        }
138
139        /** {@inheritDoc} */
140        @Override
141        public String toString() {
142                return map.toString();
143        }
144}