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.commons.collections4.map;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.ObjectOutputStream;
022import java.io.Serializable;
023import java.util.Collection;
024import java.util.Map;
025import java.util.Set;
026import java.util.SortedMap;
027
028import org.apache.commons.collections4.set.UnmodifiableSet;
029import org.apache.commons.collections4.BoundedMap;
030import org.apache.commons.collections4.CollectionUtils;
031import org.apache.commons.collections4.collection.UnmodifiableCollection;
032
033/**
034 * Decorates another <code>SortedMap</code> to fix the size blocking add/remove.
035 * <p>
036 * Any action that would change the size of the map is disallowed.
037 * The put method is allowed to change the value associated with an existing
038 * key however.
039 * <p>
040 * If trying to remove or clear the map, an UnsupportedOperationException is
041 * thrown. If trying to put a new mapping into the map, an
042 * IllegalArgumentException is thrown. This is because the put method can
043 * succeed if the mapping's key already exists in the map, so the put method
044 * is not always unsupported.
045 * <p>
046 * <strong>Note that FixedSizeSortedMap is not synchronized and is not thread-safe.</strong>
047 * If you wish to use this map from multiple threads concurrently, you must use
048 * appropriate synchronization. The simplest approach is to wrap this map
049 * using {@link java.util.Collections#synchronizedSortedMap}. This class may throw
050 * exceptions when accessed by concurrent threads without synchronization.
051 * <p>
052 * This class is Serializable from Commons Collections 3.1.
053 *
054 * @since 3.0
055 * @version $Id: FixedSizeSortedMap.html 972421 2015-11-14 20:00:04Z tn $
056 */
057public class FixedSizeSortedMap<K, V>
058        extends AbstractSortedMapDecorator<K, V>
059        implements BoundedMap<K, V>, Serializable {
060
061    /** Serialization version */
062    private static final long serialVersionUID = 3126019624511683653L;
063
064    /**
065     * Factory method to create a fixed size sorted map.
066     *
067     * @param <K>  the key type
068     * @param <V>  the value type
069     * @param map  the map to decorate, must not be null
070     * @return a new fixed size sorted map
071     * @throws IllegalArgumentException if map is null
072     * @since 4.0
073     */
074    public static <K, V> FixedSizeSortedMap<K, V> fixedSizeSortedMap(final SortedMap<K, V> map) {
075        return new FixedSizeSortedMap<K, V>(map);
076    }
077
078    //-----------------------------------------------------------------------
079    /**
080     * Constructor that wraps (not copies).
081     *
082     * @param map  the map to decorate, must not be null
083     * @throws IllegalArgumentException if map is null
084     */
085    protected FixedSizeSortedMap(final SortedMap<K, V> map) {
086        super(map);
087    }
088
089    /**
090     * Gets the map being decorated.
091     *
092     * @return the decorated map
093     */
094    protected SortedMap<K, V> getSortedMap() {
095        return (SortedMap<K, V>) map;
096    }
097
098    //-----------------------------------------------------------------------
099    /**
100     * Write the map out using a custom routine.
101     */
102    private void writeObject(final ObjectOutputStream out) throws IOException {
103        out.defaultWriteObject();
104        out.writeObject(map);
105    }
106
107    /**
108     * Read the map in using a custom routine.
109     */
110    @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect
111    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
112        in.defaultReadObject();
113        map = (Map<K, V>) in.readObject(); // (1)
114    }
115
116    //-----------------------------------------------------------------------
117    @Override
118    public V put(final K key, final V value) {
119        if (map.containsKey(key) == false) {
120            throw new IllegalArgumentException("Cannot put new key/value pair - Map is fixed size");
121        }
122        return map.put(key, value);
123    }
124
125    @Override
126    public void putAll(final Map<? extends K, ? extends V> mapToCopy) {
127        if (CollectionUtils.isSubCollection(mapToCopy.keySet(), keySet())) {
128            throw new IllegalArgumentException("Cannot put new key/value pair - Map is fixed size");
129        }
130        map.putAll(mapToCopy);
131    }
132
133    @Override
134    public void clear() {
135        throw new UnsupportedOperationException("Map is fixed size");
136    }
137
138    @Override
139    public V remove(final Object key) {
140        throw new UnsupportedOperationException("Map is fixed size");
141    }
142
143    @Override
144    public Set<Map.Entry<K, V>> entrySet() {
145        return UnmodifiableSet.unmodifiableSet(map.entrySet());
146    }
147
148    @Override
149    public Set<K> keySet() {
150        return UnmodifiableSet.unmodifiableSet(map.keySet());
151    }
152
153    @Override
154    public Collection<V> values() {
155        return UnmodifiableCollection.unmodifiableCollection(map.values());
156    }
157
158    //-----------------------------------------------------------------------
159    @Override
160    public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
161        return new FixedSizeSortedMap<K, V>(getSortedMap().subMap(fromKey, toKey));
162    }
163
164    @Override
165    public SortedMap<K, V> headMap(final K toKey) {
166        return new FixedSizeSortedMap<K, V>(getSortedMap().headMap(toKey));
167    }
168
169    @Override
170    public SortedMap<K, V> tailMap(final K fromKey) {
171        return new FixedSizeSortedMap<K, V>(getSortedMap().tailMap(fromKey));
172    }
173
174    public boolean isFull() {
175        return true;
176    }
177
178    public int maxSize() {
179        return size();
180    }
181
182}