    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.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        @Override
    054        public V setValue(final V value) {
    055            throw new UnsupportedOperationException();
    056        }
    057    }
    058
    059    /**
    060     * Implements an entry set iterator.
    061     */
    062    private final class UnmodifiableEntrySetIterator extends AbstractIteratorDecorator<Map.Entry<K, V>> {
    063
    064        protected UnmodifiableEntrySetIterator(final Iterator<Map.Entry<K, V>> iterator) {
    065            super(iterator);
    066        }
    067
    068        @Override
    069        public Map.Entry<K, V> next() {
    070            return new UnmodifiableEntry(getIterator().next());
    071        }
    072
    073        @Override
    074        public void remove() {
    075            throw new UnsupportedOperationException();
    076        }
    077    }
    078
    079    /** Serialization version */
    080    private static final long serialVersionUID = 1678353579659253473L;
    081
    082    /**
    083     * Factory method to create an unmodifiable set of Map Entry objects.
    084     *
    085     * @param <K>  the key type
    086     * @param <V>  the value type
    087     * @param set  the set to decorate, must not be null
    088     * @return a new unmodifiable entry set
    089     * @throws NullPointerException if set is null
    090     * @since 4.0
    091     */
    092    public static <K, V> Set<Map.Entry<K, V>> unmodifiableEntrySet(final Set<Map.Entry<K, V>> set) {
    093        if (set instanceof Unmodifiable) {
    094            return set;
    095        }
    096        return new UnmodifiableEntrySet<>(set);
    097    }
    098
    099    /**
    100     * Constructor that wraps (not copies).
    101     *
    102     * @param set  the set to decorate, must not be null
    103     * @throws NullPointerException if set is null
    104     */
    105    private UnmodifiableEntrySet(final Set<Map.Entry<K, V>> set) {
    106        super(set);
    107    }
    108
    109    @Override
    110    public boolean add(final Map.Entry<K, V> object) {
    111        throw new UnsupportedOperationException();
    112    }
    113
    114    @Override
    115    public boolean addAll(final Collection<? extends Map.Entry<K, V>> coll) {
    116        throw new UnsupportedOperationException();
    117    }
    118
    119    @Override
    120    public void clear() {
    121        throw new UnsupportedOperationException();
    122    }
    123
    124    @Override
    125    public Iterator<Map.Entry<K, V>> iterator() {
    126        return new UnmodifiableEntrySetIterator(decorated().iterator());
    127    }
    128
    129    @Override
    130    public boolean remove(final Object object) {
    131        throw new UnsupportedOperationException();
    132    }
    133
    134    @Override
    135    public boolean removeAll(final Collection<?> coll) {
    136        throw new UnsupportedOperationException();
    137    }
    138
    139    /**
    140     * @since 4.4
    141     */
    142    @Override
    143    public boolean removeIf(final Predicate<? super Map.Entry<K, V>> filter) {
    144        throw new UnsupportedOperationException();
    145    }
    146
    147    @Override
    148    public boolean retainAll(final Collection<?> coll) {
    149        throw new UnsupportedOperationException();
    150    }
    151
    152    @Override
    153    @SuppressWarnings("unchecked")
    154    public Object[] toArray() {
    155        final Object[] array = decorated().toArray();
    156        for (int i = 0; i < array.length; i++) {
    157            array[i] = new UnmodifiableEntry((Map.Entry<K, V>) array[i]);
    158        }
    159        return array;
    160    }
    161
    162    @Override
    163    @SuppressWarnings("unchecked")
    164    public <T> T[] toArray(final T[] array) {
    165        Object[] result = array;
    166        if (array.length > 0) {
    167            // we must create a new array to handle multithreaded situations
    168            // where another thread could access data before we decorate it
    169            result = (Object[]) Array.newInstance(array.getClass().getComponentType(), 0);
    170        }
    171        result = decorated().toArray(result);
    172        for (int i = 0; i < result.length; i++) {
    173            result[i] = new UnmodifiableEntry((Map.Entry<K, V>) result[i]);
    174        }
    175
    176        // check to see if result should be returned straight
    177        if (result.length > array.length) {
    178            return (T[]) result;
    179        }
    180
    181        // copy back into input array to fulfill the method contract
    182        System.arraycopy(result, 0, array, 0, result.length);
    183        if (array.length > result.length) {
    184            array[result.length] = null;
    185        }
    186        return array;
    187    }
    188
    189}