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;
018
019import java.util.Collection;
020import java.util.Map;
021import java.util.Set;
022
023import org.apache.commons.collections4.set.UnmodifiableSet;
024import org.apache.commons.collections4.collection.UnmodifiableCollection;
025import org.apache.commons.collections4.iterators.UnmodifiableMapIterator;
026import org.apache.commons.collections4.map.EntrySetToMapIteratorAdapter;
027import org.apache.commons.collections4.map.UnmodifiableEntrySet;
028
029/**
030 * Utilities for working with "split maps:" objects that implement {@link Put}
031 * and/or {@link Get} but not {@link Map}.
032 *
033 * @since 4.0
034 * @version $Id: SplitMapUtils.html 972421 2015-11-14 20:00:04Z tn $
035 *
036 * @see Get
037 * @see Put
038 */
039public class SplitMapUtils {
040
041    /**
042     * <code>SplitMapUtils</code> should not normally be instantiated.
043     */
044    private SplitMapUtils() {}
045
046    //-----------------------------------------------------------------------
047
048    private static class WrappedGet<K, V> implements IterableMap<K, V>, Unmodifiable {
049        private final Get<K, V> get;
050
051        private WrappedGet(final Get<K, V> get) {
052            this.get = get;
053        }
054
055        public void clear() {
056            throw new UnsupportedOperationException();
057        }
058
059        public boolean containsKey(final Object key) {
060            return get.containsKey(key);
061        }
062
063        public boolean containsValue(final Object value) {
064            return get.containsValue(value);
065        }
066
067        public Set<Map.Entry<K, V>> entrySet() {
068            return UnmodifiableEntrySet.unmodifiableEntrySet(get.entrySet());
069        }
070
071        @Override
072        public boolean equals(final Object arg0) {
073            if (arg0 == this) {
074                return true;
075            }
076            return arg0 instanceof WrappedGet && ((WrappedGet<?, ?>) arg0).get.equals(this.get);
077        }
078
079        public V get(final Object key) {
080            return get.get(key);
081        }
082
083        @Override
084        public int hashCode() {
085            return ("WrappedGet".hashCode() << 4) | get.hashCode();
086        }
087
088        public boolean isEmpty() {
089            return get.isEmpty();
090        }
091
092        public Set<K> keySet() {
093            return UnmodifiableSet.unmodifiableSet(get.keySet());
094        }
095
096        public V put(final K key, final V value) {
097            throw new UnsupportedOperationException();
098        }
099
100        public void putAll(final Map<? extends K, ? extends V> t) {
101            throw new UnsupportedOperationException();
102        }
103
104        public V remove(final Object key) {
105            return get.remove(key);
106        }
107
108        public int size() {
109            return get.size();
110        }
111
112        public Collection<V> values() {
113            return UnmodifiableCollection.unmodifiableCollection(get.values());
114        }
115
116        public MapIterator<K, V> mapIterator() {
117            MapIterator<K, V> it;
118            if (get instanceof IterableGet) {
119                it = ((IterableGet<K, V>) get).mapIterator();
120            } else {
121                it = new EntrySetToMapIteratorAdapter<K, V>(get.entrySet());
122            }
123            return UnmodifiableMapIterator.unmodifiableMapIterator(it);
124        }
125    }
126
127    private static class WrappedPut<K, V> implements Map<K, V>, Put<K, V> {
128        private final Put<K, V> put;
129
130        private WrappedPut(final Put<K, V> put) {
131            this.put = put;
132        }
133
134        public void clear() {
135            put.clear();
136        }
137
138        public boolean containsKey(final Object key) {
139            throw new UnsupportedOperationException();
140        }
141
142        public boolean containsValue(final Object value) {
143            throw new UnsupportedOperationException();
144        }
145
146        public Set<Map.Entry<K, V>> entrySet() {
147            throw new UnsupportedOperationException();
148        }
149
150        @Override
151        public boolean equals(final Object obj) {
152            if (obj == this) {
153                return true;
154            }
155            return obj instanceof WrappedPut && ((WrappedPut<?, ?>) obj).put.equals(this.put);
156        }
157
158        public V get(final Object key) {
159            throw new UnsupportedOperationException();
160        }
161
162        @Override
163        public int hashCode() {
164            return ("WrappedPut".hashCode() << 4) | put.hashCode();
165        }
166
167        public boolean isEmpty() {
168            throw new UnsupportedOperationException();
169        }
170
171        public Set<K> keySet() {
172            throw new UnsupportedOperationException();
173        }
174
175        @SuppressWarnings("unchecked")
176        public V put(final K key, final V value) {
177            return (V) put.put(key, value);
178        }
179
180        public void putAll(final Map<? extends K, ? extends V> t) {
181            put.putAll(t);
182        }
183
184        public V remove(final Object key) {
185            throw new UnsupportedOperationException();
186        }
187
188        public int size() {
189            throw new UnsupportedOperationException();
190        }
191
192        public Collection<V> values() {
193            throw new UnsupportedOperationException();
194        }
195    }
196
197    //-----------------------------------------------------------------------
198
199    /**
200     * Get the specified {@link Get} as an instance of {@link IterableMap}.
201     * If <code>get</code> implements {@link IterableMap} directly, no conversion will take place.
202     * If <code>get</code> implements {@link Map} but not {@link IterableMap} it will be decorated.
203     * Otherwise an {@link Unmodifiable} {@link IterableMap} will be returned.
204     * @param <K> the key type
205     * @param <V> the value type
206     * @param get to wrap, must not be null
207     * @return {@link IterableMap}
208     */
209    @SuppressWarnings("unchecked")
210    public static <K, V> IterableMap<K, V> readableMap(final Get<K, V> get) {
211        if (get == null) {
212            throw new IllegalArgumentException("Get must not be null");
213        }
214        if (get instanceof Map) {
215            return get instanceof IterableMap ? ((IterableMap<K, V>) get) : MapUtils
216                    .iterableMap((Map<K, V>) get);
217        }
218        return new WrappedGet<K, V>(get);
219    }
220
221    /**
222     * Get the specified {@link Put} as an instanceof {@link Map}.
223     * If <code>put</code> implements {@link Map} directly, no conversion will take place.
224     * Otherwise a <em>write-only</em> {@link Map} will be returned.  On such a {@link Map}
225     * it is recommended that the result of #put(K, V) be discarded as it likely will not
226     * match <code>V</code> at runtime.
227     *
228     * @param <K> the key type
229     * @param <V> the element type
230     * @param put to wrap, must not be null
231     * @return {@link Map}
232     */
233    @SuppressWarnings("unchecked")
234    public static <K, V> Map<K, V> writableMap(final Put<K, V> put) {
235        if (put == null) {
236            throw new IllegalArgumentException("Put must not be null");
237        }
238        if (put instanceof Map) {
239            return (Map<K, V>) put;
240        }
241        return new WrappedPut<K, V>(put);
242    }
243
244}