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