    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 *      https://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.lang.reflect.Array;
    020import java.util.Collection;
    021import java.util.Iterator;
    022import java.util.Map;
    023import java.util.Set;
    024import java.util.function.Predicate;
    025
    026import org.apache.commons.collections4.Unmodifiable;
    027import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
    028import org.apache.commons.collections4.keyvalue.AbstractMapEntryDecorator;
    029import org.apache.commons.collections4.set.AbstractSetDecorator;
    030
    031/**
    032 * Decorates a map entry {@code Set} to ensure it can't be altered.
    033 * <p>
    034 * Attempts to modify it will result in an UnsupportedOperationException.
    035 * </p>
    036 *
    037 * @param <K> The type of the keys in the map
    038 * @param <V> The type of the values in the map
    039 * @since 3.0
    040 */
    041public final class UnmodifiableEntrySet<K, V>
    042        extends AbstractSetDecorator<Map.Entry<K, V>> implements Unmodifiable {
    043
    044    /**
    045     * Implements a map entry that is unmodifiable.
    046     */
    047    private final class UnmodifiableEntry extends AbstractMapEntryDecorator<K, V> {
    048
    049        protected UnmodifiableEntry(final Map.Entry<K, V> entry) {
    050            super(entry);
    051        }
    052
    053        /**
    054         * Always throws {@link UnsupportedOperationException}.
    055         *
    056         * @param value Ignored.
    057         * @throws UnsupportedOperationException Always thrown.
    058         */
    059        @Override
    060        public V setValue(final V value) {
    061            throw new UnsupportedOperationException();
    062        }
    063    }
    064
    065    /**
    066     * Implements an entry set iterator.
    067     */
    068    private final class UnmodifiableEntrySetIterator extends AbstractIteratorDecorator<Map.Entry<K, V>> {
    069
    070        protected UnmodifiableEntrySetIterator(final Iterator<Map.Entry<K, V>> iterator) {
    071            super(iterator);
    072        }
    073
    074        /**
    075         * Always throws {@link UnsupportedOperationException}.
    076         *
    077         * @throws UnsupportedOperationException Always thrown.
    078         */
    079        @Override
    080        public Map.Entry<K, V> next() {
    081            return new UnmodifiableEntry(getIterator().next());
    082        }
    083
    084        /**
    085         * Always throws {@link UnsupportedOperationException}.
    086         *
    087         * @throws UnsupportedOperationException Always thrown.
    088         */
    089        @Override
    090        public void remove() {
    091            throw new UnsupportedOperationException();
    092        }
    093    }
    094
    095    /** Serialization version */
    096    private static final long serialVersionUID = 1678353579659253473L;
    097
    098    /**
    099     * Factory method to create an unmodifiable set of Map Entry objects.
    100     *
    101     * @param <K>  the key type
    102     * @param <V>  the value type
    103     * @param set  The set to decorate, must not be null
    104     * @return A new unmodifiable entry set
    105     * @throws NullPointerException if set is null
    106     * @since 4.0
    107     */
    108    public static <K, V> Set<Map.Entry<K, V>> unmodifiableEntrySet(final Set<Map.Entry<K, V>> set) {
    109        if (set instanceof Unmodifiable) {
    110            return set;
    111        }
    112        return new UnmodifiableEntrySet<>(set);
    113    }
    114
    115    /**
    116     * Constructor that wraps (not copies).
    117     *
    118     * @param set  The set to decorate, must not be null
    119     * @throws NullPointerException if set is null
    120     */
    121    private UnmodifiableEntrySet(final Set<Map.Entry<K, V>> set) {
    122        super(set);
    123    }
    124
    125    /**
    126     * Always throws {@link UnsupportedOperationException}.
    127     *
    128     * @param object Ignored.
    129     * @throws UnsupportedOperationException Always thrown.
    130     */
    131    @Override
    132    public boolean add(final Map.Entry<K, V> object) {
    133        throw new UnsupportedOperationException();
    134    }
    135
    136    /**
    137     * Always throws {@link UnsupportedOperationException}.
    138     *
    139     * @param coll Ignored.
    140     * @throws UnsupportedOperationException Always thrown.
    141     */
    142    @Override
    143    public boolean addAll(final Collection<? extends Map.Entry<K, V>> coll) {
    144        throw new UnsupportedOperationException();
    145    }
    146
    147    /**
    148     * Always throws {@link UnsupportedOperationException}.
    149     *
    150     * @throws UnsupportedOperationException Always thrown.
    151     */
    152    @Override
    153    public void clear() {
    154        throw new UnsupportedOperationException();
    155    }
    156
    157    @Override
    158    public Iterator<Map.Entry<K, V>> iterator() {
    159        return new UnmodifiableEntrySetIterator(decorated().iterator());
    160    }
    161
    162    /**
    163     * Always throws {@link UnsupportedOperationException}.
    164     *
    165     * @param object Ignored.
    166     * @throws UnsupportedOperationException Always thrown.
    167     */
    168    @Override
    169    public boolean remove(final Object object) {
    170        throw new UnsupportedOperationException();
    171    }
    172
    173    /**
    174     * Always throws {@link UnsupportedOperationException}.
    175     *
    176     * @param coll Ignored.
    177     * @throws UnsupportedOperationException Always thrown.
    178     */
    179    @Override
    180    public boolean removeAll(final Collection<?> coll) {
    181        throw new UnsupportedOperationException();
    182    }
    183
    184    /**
    185     * Always throws {@link UnsupportedOperationException}.
    186     *
    187     * @param filter Ignored.
    188     * @throws UnsupportedOperationException Always thrown.
    189     * @since 4.4
    190     */
    191    @Override
    192    public boolean removeIf(final Predicate<? super Map.Entry<K, V>> filter) {
    193        throw new UnsupportedOperationException();
    194    }
    195
    196    /**
    197     * Always throws {@link UnsupportedOperationException}.
    198     *
    199     * @param coll Ignored.
    200     * @throws UnsupportedOperationException Always thrown.
    201     */
    202    @Override
    203    public boolean retainAll(final Collection<?> coll) {
    204        throw new UnsupportedOperationException();
    205    }
    206
    207    @Override
    208    @SuppressWarnings("unchecked")
    209    public Object[] toArray() {
    210        final Object[] array = decorated().toArray();
    211        for (int i = 0; i < array.length; i++) {
    212            array[i] = new UnmodifiableEntry((Map.Entry<K, V>) array[i]);
    213        }
    214        return array;
    215    }
    216
    217    @Override
    218    @SuppressWarnings("unchecked")
    219    public <T> T[] toArray(final T[] array) {
    220        Object[] result = array;
    221        if (array.length > 0) {
    222            // we must create a new array to handle multithreaded situations
    223            // where another thread could access data before we decorate it
    224            result = (Object[]) Array.newInstance(array.getClass().getComponentType(), 0);
    225        }
    226        result = decorated().toArray(result);
    227        for (int i = 0; i < result.length; i++) {
    228            result[i] = new UnmodifiableEntry((Map.Entry<K, V>) result[i]);
    229        }
    230
    231        // check to see if result should be returned straight
    232        if (result.length > array.length) {
    233            return (T[]) result;
    234        }
    235
    236        // copy back into input array to fulfill the method contract
    237        System.arraycopy(result, 0, array, 0, result.length);
    238        if (array.length > result.length) {
    239            array[result.length] = null;
    240        }
    241        return array;
    242    }
    243
    244}