View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections4.map;
18  
19  import java.lang.reflect.Array;
20  import java.util.Collection;
21  import java.util.Iterator;
22  import java.util.Map;
23  import java.util.Set;
24  import java.util.function.Predicate;
25  
26  import org.apache.commons.collections4.Unmodifiable;
27  import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
28  import org.apache.commons.collections4.keyvalue.AbstractMapEntryDecorator;
29  import org.apache.commons.collections4.set.AbstractSetDecorator;
30  
31  /**
32   * Decorates a map entry {@code Set} to ensure it can't be altered.
33   * <p>
34   * Attempts to modify it will result in an UnsupportedOperationException.
35   * </p>
36   *
37   * @param <K> The type of the keys in the map
38   * @param <V> The type of the values in the map
39   * @since 3.0
40   */
41  public final class UnmodifiableEntrySet<K, V>
42          extends AbstractSetDecorator<Map.Entry<K, V>> implements Unmodifiable {
43  
44      /**
45       * Implements a map entry that is unmodifiable.
46       */
47      private final class UnmodifiableEntry extends AbstractMapEntryDecorator<K, V> {
48  
49          protected UnmodifiableEntry(final Map.Entry<K, V> entry) {
50              super(entry);
51          }
52  
53          /**
54           * Always throws {@link UnsupportedOperationException}.
55           *
56           * @param value Ignored.
57           * @throws UnsupportedOperationException Always thrown.
58           */
59          @Override
60          public V setValue(final V value) {
61              throw new UnsupportedOperationException();
62          }
63      }
64  
65      /**
66       * Implements an entry set iterator.
67       */
68      private final class UnmodifiableEntrySetIterator extends AbstractIteratorDecorator<Map.Entry<K, V>> {
69  
70          protected UnmodifiableEntrySetIterator(final Iterator<Map.Entry<K, V>> iterator) {
71              super(iterator);
72          }
73  
74          /**
75           * Always throws {@link UnsupportedOperationException}.
76           *
77           * @throws UnsupportedOperationException Always thrown.
78           */
79          @Override
80          public Map.Entry<K, V> next() {
81              return new UnmodifiableEntry(getIterator().next());
82          }
83  
84          /**
85           * Always throws {@link UnsupportedOperationException}.
86           *
87           * @throws UnsupportedOperationException Always thrown.
88           */
89          @Override
90          public void remove() {
91              throw new UnsupportedOperationException();
92          }
93      }
94  
95      /** Serialization version */
96      private static final long serialVersionUID = 1678353579659253473L;
97  
98      /**
99       * 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 }