001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  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.apache.wicket.util.collections;
018
019import java.io.Serializable;
020import java.util.AbstractList;
021import java.util.AbstractSet;
022import java.util.Collection;
023import java.util.Iterator;
024import java.util.Map;
025import java.util.NoSuchElementException;
026import java.util.Set;
027
028/**
029 * A fixed size map implementation. Holds an array of keys and array of values which correspond by
030 * index. Null key entries are available for use. This means that null is not a valid key.
031 *
032 * @author Jonathan Locke
033 * @param <K>
034 *            Key type
035 * @param <V>
036 *            Value type
037 */
038public class MiniMap<K, V> implements Map<K, V>, Serializable
039{
040        private static final long serialVersionUID = 1L;
041
042        /** The array of keys. Keys that are null are not used. */
043        private final K[] keys;
044
045        /** The array of values which correspond by index with the keys array. */
046        private final V[] values;
047
048        /** The number of valid entries */
049        private int size;
050
051        /** The last search index. This makes putting and getting more efficient. */
052        private int lastSearchIndex;
053
054        /**
055         * Constructor
056         *
057         * @param maxEntries
058         *            The maximum number of entries this map can hold
059         */
060        @SuppressWarnings("unchecked")
061        public MiniMap(final int maxEntries)
062        {
063                keys = (K[])new Object[maxEntries];
064                values = (V[])new Object[maxEntries];
065        }
066
067        /**
068         * Constructor
069         *
070         * @param map
071         *            The map
072         * @param maxEntries
073         *            The maximum number of entries this map can hold
074         */
075        public MiniMap(final Map<? extends K, ? extends V> map, final int maxEntries)
076        {
077                this(maxEntries);
078                putAll(map);
079        }
080
081        /**
082         * @return True if this MiniMap is full
083         */
084        public boolean isFull()
085        {
086                return size == keys.length;
087        }
088
089        /**
090         * @see java.util.Map#size()
091         */
092        @Override
093        public int size()
094        {
095                return size;
096        }
097
098        /**
099         * @see java.util.Map#isEmpty()
100         */
101        @Override
102        public boolean isEmpty()
103        {
104                return size == 0;
105        }
106
107        /**
108         * @see java.util.Map#containsKey(java.lang.Object)
109         */
110        @Override
111        public boolean containsKey(final Object key)
112        {
113                return findKey(0, key) != -1;
114        }
115
116        /**
117         * @see java.util.Map#containsValue(java.lang.Object)
118         */
119        @Override
120        public boolean containsValue(final Object value)
121        {
122                return findValue(0, value) != -1;
123        }
124
125        /**
126         * @see java.util.Map#get(java.lang.Object)
127         */
128        @Override
129        public V get(final Object key)
130        {
131                // Search for key
132                final int index = findKey(key);
133
134                if (index != -1)
135                {
136                        // Return value
137                        return values[index];
138                }
139
140                // Failed to find key
141                return null;
142        }
143
144        /**
145         * @see java.util.Map#put(java.lang.Object, java.lang.Object)
146         */
147        @Override
148        public V put(final K key, final V value)
149        {
150                // Search for key
151                final int index = findKey(key);
152
153                if (index != -1)
154                {
155                        // Replace existing value
156                        final V oldValue = values[index];
157                        values[index] = value;
158                        return oldValue;
159                }
160
161                // Is there room for a new entry?
162                if (size < keys.length)
163                {
164                        // Store at first null index and continue searching after null index
165                        // next time
166                        final int nullIndex = nextNullKey(lastSearchIndex);
167                        lastSearchIndex = nextIndex(nullIndex);
168                        keys[nullIndex] = key;
169                        values[nullIndex] = value;
170                        size++;
171
172                        return null;
173                }
174                else
175                {
176                        throw new IllegalStateException("Map full");
177                }
178        }
179
180        /**
181         * @see java.util.Map#remove(java.lang.Object)
182         */
183        @Override
184        public V remove(final Object key)
185        {
186                // Search for key
187                final int index = findKey(key);
188
189                if (index != -1)
190                {
191                        // Store value
192                        final V oldValue = values[index];
193
194                        keys[index] = null;
195                        values[index] = null;
196                        size--;
197
198                        return oldValue;
199                }
200
201                return null;
202        }
203
204        /**
205         * @see java.util.Map#putAll(java.util.Map)
206         */
207        @Override
208        public void putAll(final Map<? extends K, ? extends V> map)
209        {
210                for (final Entry<? extends K, ? extends V> entry : map.entrySet())
211                {
212                        put(entry.getKey(), entry.getValue());
213                }
214        }
215
216        /**
217         * @see java.util.Map#clear()
218         */
219        @Override
220        public void clear()
221        {
222                for (int i = 0; i < keys.length; i++)
223                {
224                        keys[i] = null;
225                        values[i] = null;
226                }
227
228                size = 0;
229        }
230
231        /**
232         * @see java.util.Map#keySet()
233         */
234        @Override
235        public Set<K> keySet()
236        {
237                return new AbstractSet<K>()
238                {
239                        @Override
240                        public Iterator<K> iterator()
241                        {
242                                return new Iterator<K>()
243                                {
244                                        @Override
245                                        public boolean hasNext()
246                                        {
247                                                return i < size - 1;
248                                        }
249
250                                        @Override
251                                        public K next()
252                                        {
253                                                // Just in case... (WICKET-428)
254                                                if (!hasNext())
255                                                {
256                                                        throw new NoSuchElementException();
257                                                }
258
259                                                // Find next key
260                                                i = nextKey(nextIndex(i));
261
262                                                // Get key
263                                                return keys[i];
264                                        }
265
266                                        @Override
267                                        public void remove()
268                                        {
269                                                keys[i] = null;
270                                                values[i] = null;
271                                                size--;
272                                        }
273
274                                        int i = -1;
275                                };
276                        }
277
278                        @Override
279                        public int size()
280                        {
281                                return size;
282                        }
283                };
284        }
285
286        /**
287         * @see java.util.Map#values()
288         */
289        @Override
290        public Collection<V> values()
291        {
292                return new AbstractList<V>()
293                {
294                        @Override
295                        public V get(final int index)
296                        {
297                                if (index > size - 1)
298                                {
299                                        throw new IndexOutOfBoundsException();
300                                }
301                                int keyIndex = nextKey(0);
302
303                                for (int i = 0; i < index; i++)
304                                {
305                                        keyIndex = nextKey(keyIndex + 1);
306                                }
307
308                                return values[keyIndex];
309                        }
310
311                        @Override
312                        public int size()
313                        {
314                                return size;
315                        }
316                };
317        }
318
319        /**
320         * @see java.util.Map#entrySet()
321         */
322        @Override
323        public Set<Entry<K, V>> entrySet()
324        {
325                return new AbstractSet<Entry<K, V>>()
326                {
327                        @Override
328                        public Iterator<Entry<K, V>> iterator()
329                        {
330                                return new Iterator<Entry<K, V>>()
331                                {
332                                        @Override
333                                        public boolean hasNext()
334                                        {
335                                                return index < size;
336                                        }
337
338                                        @Override
339                                        public Entry<K, V> next()
340                                        {
341                                                if (!hasNext())
342                                                {
343                                                        throw new NoSuchElementException();
344                                                }
345
346                                                keyIndex = nextKey(nextIndex(keyIndex));
347
348                                                index++;
349
350                                                return new Map.Entry<K, V>()
351                                                {
352                                                        @Override
353                                                        public K getKey()
354                                                        {
355                                                                return keys[keyIndex];
356                                                        }
357
358                                                        @Override
359                                                        public V getValue()
360                                                        {
361                                                                return values[keyIndex];
362                                                        }
363
364                                                        @Override
365                                                        public V setValue(final V value)
366                                                        {
367                                                                final V oldValue = values[keyIndex];
368
369                                                                values[keyIndex] = value;
370
371                                                                return oldValue;
372                                                        }
373                                                };
374                                        }
375
376                                        @Override
377                                        public void remove()
378                                        {
379                                                keys[keyIndex] = null;
380                                                values[keyIndex] = null;
381                                        }
382
383                                        int keyIndex = -1;
384
385                                        int index = 0;
386                                };
387                        }
388
389                        @Override
390                        public int size()
391                        {
392                                return size;
393                        }
394                };
395        }
396
397        /**
398         * Computes the next index in the key or value array (both are the same length)
399         *
400         * @param index
401         *            The index
402         * @return The next index, taking into account wraparound
403         */
404        private int nextIndex(final int index)
405        {
406                return (index + 1) % keys.length;
407        }
408
409        /**
410         * Finds the index of the next non-null key. If the map is empty, -1 will be returned.
411         *
412         * @param start
413         *            Index to start at
414         * @return Index of next non-null key
415         */
416        private int nextKey(final int start)
417        {
418                int i = start;
419
420                do
421                {
422                        if (keys[i] != null)
423                        {
424                                return i;
425                        }
426
427                        i = nextIndex(i);
428                }
429                while (i != start);
430
431                return -1;
432        }
433
434        /**
435         * Finds the index of the next null key. If no null key can be found, the map is full and -1
436         * will be returned.
437         *
438         * @param start
439         *            Index to start at
440         * @return Index of next null key
441         */
442        private int nextNullKey(final int start)
443        {
444                int i = start;
445
446                do
447                {
448                        if (keys[i] == null)
449                        {
450                                return i;
451                        }
452
453                        i = nextIndex(i);
454                }
455                while (i != start);
456
457                return -1;
458        }
459
460        /**
461         * Finds a key by starting at lastSearchIndex and searching from there. If the key is found,
462         * lastSearchIndex is advanced so the next key search can find the next key in the array, which
463         * is the most likely to be retrieved.
464         *
465         * @param key
466         *            Key to find in map
467         * @return Index of matching key or -1 if not found
468         */
469        private int findKey(final Object key)
470        {
471                if (size > 0)
472                {
473                        // Find key starting at search index
474                        final int index = findKey(lastSearchIndex, key);
475
476                        // Found match?
477                        if (index != -1)
478                        {
479                                // Start search at the next index next time
480                                lastSearchIndex = nextIndex(index);
481
482                                // Return index of key
483                                return index;
484                        }
485                }
486
487                return -1;
488        }
489
490        /**
491         * Searches for a key from a given starting index.
492         *
493         * @param key
494         *            The key to find in this map
495         * @param start
496         *            Index to start at
497         * @return Index of matching key or -1 if not found
498         */
499        private int findKey(final int start, final Object key)
500        {
501                int i = start;
502
503                do
504                {
505                        if (key.equals(keys[i]))
506                        {
507                                return i;
508                        }
509
510                        i = nextIndex(i);
511                }
512                while (i != start);
513
514                return -1;
515        }
516
517        /**
518         * Searches for a value from a given starting index.
519         *
520         * @param start
521         *            Index to start at
522         * @param value
523         *            The value to find in this map
524         * @return Index of matching value or -1 if not found
525         */
526        private int findValue(final int start, final Object value)
527        {
528                int i = start;
529
530                do
531                {
532                        if (value.equals(values[i]))
533                        {
534                                return i;
535                        }
536
537                        i = nextIndex(i);
538                }
539                while (i != start);
540
541                return -1;
542        }
543}