1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix;
19
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.ConcurrentModificationException;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.NoSuchElementException;
26 import java.util.Objects;
27 import java.util.Set;
28
29 import org.eclipse.jdt.annotation.NonNull;
30
31
32
33
34
35
36
37
38
39
40
41 final class MapValueSet<T, V> implements Set<T> {
42 private final Map<T, V> map;
43 private final ValueSupplier<@NonNull V> valueSupplier;
44 private final V removedSentinel;
45
46 @SuppressWarnings("unchecked")
47 MapValueSet(Map<? extends T, V> map, ValueSupplier<@NonNull V> valueSupplier, V removedSentinel) {
48 this.valueSupplier = Objects.requireNonNull(valueSupplier);
49 this.removedSentinel = removedSentinel;
50 this.map = (Map<T, V>) map;
51 }
52
53 @FunctionalInterface
54 interface ValueSupplier<V> {
55 V supplyValue();
56 }
57
58
59
60
61
62
63
64
65
66
67 public void markRemoved(T elem) {
68 if (removedSentinel == null) {
69 map.remove(elem);
70 } else {
71 map.put(elem, removedSentinel);
72 }
73 }
74
75
76
77
78
79 public void markAllRemoved() {
80 if (removedSentinel == null) {
81 map.clear();
82 } else {
83 for (Map.Entry<T, V> en : map.entrySet()) {
84 en.setValue(removedSentinel);
85 }
86 }
87 }
88
89 private @NonNull V getValue() {
90 return Objects.requireNonNull(valueSupplier.supplyValue());
91 }
92
93 @Override
94 public int size() {
95 V val = getValue();
96 if (val.equals(removedSentinel)) {
97 return 0;
98 }
99
100 int size = 0;
101 for (Map.Entry<T, V> en : map.entrySet()) {
102 if (val.equals(en.getValue())) {
103 size++;
104 }
105 }
106 return size;
107 }
108
109 @Override
110 public boolean isEmpty() {
111 V val = getValue();
112 if (val.equals(removedSentinel)) {
113 return true;
114 }
115
116 for (Map.Entry<T, V> en : map.entrySet()) {
117 if (val.equals(en.getValue())) {
118 return false;
119 }
120 }
121 return true;
122 }
123
124 private boolean isDefinitelyEmpty() {
125 return getValue().equals(removedSentinel);
126 }
127
128 @Override
129 public boolean contains(Object o) {
130 if (isDefinitelyEmpty()) {
131 return false;
132 }
133 return getValue().equals(map.get(o));
134 }
135
136 @Override
137 public Iterator<T> iterator() {
138 if (isDefinitelyEmpty()) {
139 return Collections.emptyIterator();
140 }
141
142 Iterator<Map.Entry<T, V>> mapit = map.entrySet().iterator();
143
144 V val = getValue();
145
146 return new Iterator<T>() {
147 Map.Entry<T, V> nextObj = null;
148 Map.Entry<T, V> currentObj = null;
149
150 @Override
151 public boolean hasNext() {
152 if (nextObj != null) {
153 return true;
154 }
155 while (mapit.hasNext()) {
156 Map.Entry<T, V> en = mapit.next();
157 if (val.equals(en.getValue())) {
158 nextObj = en;
159 return true;
160 }
161 }
162 return false;
163 }
164
165 @Override
166 public T next() {
167 currentObj = null;
168 if (nextObj == null) {
169 if (!hasNext()) {
170 throw new NoSuchElementException();
171 }
172 }
173 T next = nextObj.getKey();
174 if (val.equals(nextObj.getValue())) {
175 currentObj = nextObj;
176 nextObj = null;
177 return next;
178 } else {
179 throw new ConcurrentModificationException();
180 }
181 }
182
183 @Override
184 public void remove() {
185 if (currentObj == null) {
186 throw new IllegalStateException();
187 }
188 markRemoved(currentObj.getKey());
189 currentObj = null;
190 }
191 };
192 }
193
194 @Override
195 @SuppressWarnings("PMD.OptimizableToArrayCall")
196 public Object[] toArray() {
197 return toArray(new Object[size()]);
198 }
199
200 @SuppressWarnings({"unchecked", "null"})
201 @Override
202 public <E> E[] toArray(E[] a) {
203 int size = size();
204
205 if (a.length < size) {
206 return toArray((E[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(),
207 size));
208 }
209
210 int i = 0;
211 for (T elem : this) {
212 a[i++] = (E) elem;
213 }
214 if (i < a.length) {
215 a[i] = null;
216 }
217
218 return a;
219 }
220
221
222
223
224
225
226
227 public boolean update(T e) {
228 if (map.containsKey(e)) {
229 map.put(e, getValue());
230 return true;
231 } else {
232 return false;
233 }
234 }
235
236
237
238
239 @Override
240 public boolean add(T e) {
241 if (contains(e)) {
242 return false;
243 } else if (update(e)) {
244 return true;
245 } else {
246 map.put(e, getValue());
247 return true;
248 }
249 }
250
251 @SuppressWarnings("unchecked")
252 @Override
253 public boolean remove(Object o) {
254 if (isDefinitelyEmpty() || !map.containsKey(o)) {
255 return false;
256 }
257
258 markRemoved((T) o);
259 return true;
260 }
261
262 @Override
263 public boolean containsAll(Collection<?> c) {
264 if (isDefinitelyEmpty()) {
265 return c.isEmpty();
266 }
267 for (Object obj : c) {
268 if (!contains(obj)) {
269 return false;
270 }
271 }
272 return true;
273 }
274
275 @Override
276 public boolean addAll(Collection<? extends T> c) {
277 boolean changed = false;
278 for (T elem : c) {
279 changed |= add(elem);
280 }
281 return changed;
282 }
283
284 @Override
285 public boolean retainAll(Collection<?> c) {
286 boolean changed = false;
287 for (Iterator<T> it = iterator(); it.hasNext();) {
288 T elem = it.next();
289 if (!c.contains(elem)) {
290 it.remove();
291 changed = true;
292 }
293 }
294 return changed;
295 }
296
297 @Override
298 public boolean removeAll(Collection<?> c) {
299 if (isDefinitelyEmpty()) {
300 return false;
301 }
302 boolean changed = false;
303 for (Object obj : c) {
304 changed |= remove(obj);
305 }
306 return changed;
307 }
308
309
310
311
312
313
314
315 @Override
316 public void clear() {
317 V val = getValue();
318 if (val.equals(removedSentinel)) {
319 return;
320 }
321
322 for (Map.Entry<T, V> en : map.entrySet()) {
323 if (val.equals(en.getValue())) {
324 markRemoved(en.getKey());
325 }
326 }
327 }
328 }