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    *      http://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          @Override
54          public V setValue(final V value) {
55              throw new UnsupportedOperationException();
56          }
57      }
58  
59      /**
60       * Implements an entry set iterator.
61       */
62      private final class UnmodifiableEntrySetIterator extends AbstractIteratorDecorator<Map.Entry<K, V>> {
63  
64          protected UnmodifiableEntrySetIterator(final Iterator<Map.Entry<K, V>> iterator) {
65              super(iterator);
66          }
67  
68          @Override
69          public Map.Entry<K, V> next() {
70              return new UnmodifiableEntry(getIterator().next());
71          }
72  
73          @Override
74          public void remove() {
75              throw new UnsupportedOperationException();
76          }
77      }
78  
79      /** Serialization version */
80      private static final long serialVersionUID = 1678353579659253473L;
81  
82      /**
83       * Factory method to create an unmodifiable set of Map Entry objects.
84       *
85       * @param <K>  the key type
86       * @param <V>  the value type
87       * @param set  the set to decorate, must not be null
88       * @return a new unmodifiable entry set
89       * @throws NullPointerException if set is null
90       * @since 4.0
91       */
92      public static <K, V> Set<Map.Entry<K, V>> unmodifiableEntrySet(final Set<Map.Entry<K, V>> set) {
93          if (set instanceof Unmodifiable) {
94              return set;
95          }
96          return new UnmodifiableEntrySet<>(set);
97      }
98  
99      /**
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 }