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