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