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}