1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.settings;
20
21 import java.io.IOError;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.io.PrintStream;
26 import java.io.PrintWriter;
27 import java.io.Reader;
28 import java.io.Writer;
29 import java.util.AbstractSet;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.Enumeration;
34 import java.util.InvalidPropertiesFormatException;
35 import java.util.Iterator;
36 import java.util.LinkedHashSet;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Objects;
40 import java.util.Properties;
41 import java.util.Set;
42 import java.util.concurrent.CopyOnWriteArrayList;
43 import java.util.function.BiConsumer;
44 import java.util.function.BiFunction;
45 import java.util.function.Consumer;
46 import java.util.function.Function;
47 import java.util.function.Supplier;
48
49 class WrapperProperties extends Properties {
50
51 final Supplier<Map<String, String>> getter;
52 final Consumer<Properties> setter;
53
54 WrapperProperties(Supplier<Map<String, String>> getter, Consumer<Properties> setter) {
55 this.getter = getter;
56 this.setter = setter;
57 }
58
59 @Override
60 public String getProperty(String key) {
61 return getter.get().get(key);
62 }
63
64 @Override
65 public String getProperty(String key, String defaultValue) {
66 return getter.get().getOrDefault(key, defaultValue);
67 }
68
69 @Override
70 public Enumeration<?> propertyNames() {
71 return Collections.enumeration(getter.get().keySet());
72 }
73
74 @Override
75 public Set<String> stringPropertyNames() {
76 return getter.get().keySet();
77 }
78
79 @Override
80 public void list(PrintStream out) {
81 throw new UnsupportedOperationException();
82 }
83
84 @Override
85 public void list(PrintWriter out) {
86 throw new UnsupportedOperationException();
87 }
88
89 @Override
90 public int size() {
91 return getter.get().size();
92 }
93
94 @Override
95 public boolean isEmpty() {
96 return getter.get().isEmpty();
97 }
98
99 @Override
100 public Enumeration<Object> keys() {
101 return Collections.enumeration((Set) getter.get().keySet());
102 }
103
104 @Override
105 public Enumeration<Object> elements() {
106 return Collections.enumeration((Collection) getter.get().values());
107 }
108
109 @Override
110 public boolean contains(Object value) {
111 return getter.get().containsKey(value != null ? value.toString() : null);
112 }
113
114 @Override
115 public boolean containsValue(Object value) {
116 return getter.get().containsValue(value);
117 }
118
119 @Override
120 public boolean containsKey(Object key) {
121 return getter.get().containsKey(key);
122 }
123
124 @Override
125 public Object get(Object key) {
126 return getter.get().get(key);
127 }
128
129 @Override
130 public synchronized String toString() {
131 return getter.get().toString();
132 }
133
134 @Override
135 public Set<Object> keySet() {
136 return new OrderedProperties(getter.get()).keySet();
137 }
138
139 @Override
140 public Collection<Object> values() {
141 return new OrderedProperties(getter.get()).values();
142 }
143
144 @Override
145 public Set<Map.Entry<Object, Object>> entrySet() {
146 return new OrderedProperties(getter.get()).entrySet();
147 }
148
149 @Override
150 public synchronized boolean equals(Object o) {
151 if (o instanceof WrapperProperties wrapperProperties) {
152 o = wrapperProperties.getter.get();
153 }
154 return getter.get().equals(o);
155 }
156
157 @Override
158 public synchronized int hashCode() {
159 return getter.get().hashCode();
160 }
161
162 @Override
163 public Object getOrDefault(Object key, Object defaultValue) {
164 if (key instanceof String str && defaultValue instanceof String def) {
165 return getter.get().getOrDefault(key, def);
166 } else {
167 return defaultValue;
168 }
169 }
170
171 @Override
172 public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) {
173 getter.get().forEach(action);
174 }
175
176 interface WriteOp<T> {
177 T perform(Properties props);
178 }
179
180 interface WriteOpVoid {
181 void perform(Properties props);
182 }
183
184 private <T> T writeOperation(WriteOp<T> runner) {
185 OrderedProperties props = new OrderedProperties(getter.get());
186 T ret = runner.perform(props);
187 if (!props.equals(getter.get())) {
188 setter.accept(props);
189 }
190 return ret;
191 }
192
193 private void writeOperationVoid(WriteOpVoid runner) {
194 writeOperation(properties -> {
195 runner.perform(properties);
196 return null;
197 });
198 }
199
200 @Override
201 public synchronized Object setProperty(String key, String value) {
202 return writeOperation(p -> p.setProperty(key, value));
203 }
204
205 @Override
206 public synchronized Object put(Object key, Object value) {
207 return writeOperation(p -> p.put(key, value));
208 }
209
210 @Override
211 public synchronized Object remove(Object key) {
212 return writeOperation(p -> p.remove(key));
213 }
214
215 @Override
216 public synchronized void putAll(Map<?, ?> t) {
217 writeOperationVoid(p -> p.putAll(t));
218 }
219
220 @Override
221 public synchronized void clear() {
222 writeOperationVoid(Properties::clear);
223 }
224
225 @Override
226 public synchronized void replaceAll(BiFunction<? super Object, ? super Object, ?> function) {
227 writeOperationVoid(p -> p.replaceAll(function));
228 }
229
230 @Override
231 public synchronized Object putIfAbsent(Object key, Object value) {
232 return writeOperation(p -> p.putIfAbsent(key, value));
233 }
234
235 @Override
236 public synchronized boolean remove(Object key, Object value) {
237 return writeOperation(p -> p.remove(key, value));
238 }
239
240 @Override
241 public synchronized boolean replace(Object key, Object oldValue, Object newValue) {
242 return writeOperation(p -> p.replace(key, oldValue, newValue));
243 }
244
245 @Override
246 public synchronized Object replace(Object key, Object value) {
247 return writeOperation(p -> p.replace(key, value));
248 }
249
250 @Override
251 public synchronized Object computeIfAbsent(Object key, Function<? super Object, ?> mappingFunction) {
252 return writeOperation(p -> p.computeIfAbsent(key, mappingFunction));
253 }
254
255 @Override
256 public synchronized Object computeIfPresent(
257 Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) {
258 return writeOperation(p -> p.computeIfPresent(key, remappingFunction));
259 }
260
261 @Override
262 public synchronized Object compute(Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) {
263 return writeOperation(p -> p.compute(key, remappingFunction));
264 }
265
266 @Override
267 public synchronized Object merge(
268 Object key, Object value, BiFunction<? super Object, ? super Object, ?> remappingFunction) {
269 return writeOperation(p -> p.merge(key, value, remappingFunction));
270 }
271
272 @Override
273 public synchronized void load(Reader reader) throws IOException {
274 try {
275 writeOperationVoid(p -> {
276 try {
277 p.load(reader);
278 } catch (IOException e) {
279 throw new IOError(e);
280 }
281 });
282 } catch (IOError e) {
283 throw (IOException) e.getCause();
284 }
285 }
286
287 @Override
288 public synchronized void load(InputStream inStream) throws IOException {
289 try {
290 writeOperationVoid(p -> {
291 try {
292 p.load(inStream);
293 } catch (IOException e) {
294 throw new IOError(e);
295 }
296 });
297 } catch (IOError e) {
298 throw (IOException) e.getCause();
299 }
300 }
301
302 @Override
303 public void save(OutputStream out, String comments) {
304 new OrderedProperties(getter.get()).save(out, comments);
305 }
306
307 @Override
308 public void store(Writer writer, String comments) throws IOException {
309 new OrderedProperties(getter.get()).store(writer, comments);
310 }
311
312 @Override
313 public void store(OutputStream out, String comments) throws IOException {
314 new OrderedProperties(getter.get()).store(out, comments);
315 }
316
317 @Override
318 public synchronized void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException {
319 throw new UnsupportedOperationException();
320 }
321
322 @Override
323 public void storeToXML(OutputStream os, String comment) throws IOException {
324 new OrderedProperties(getter.get()).storeToXML(os, comment);
325 }
326
327 @Override
328 public void storeToXML(OutputStream os, String comment, String encoding) throws IOException {
329 new OrderedProperties(getter.get()).storeToXML(os, comment, encoding);
330 }
331
332
333 private Object writeReplace() throws java.io.ObjectStreamException {
334 return new OrderedProperties(getter.get());
335 }
336
337 private class OrderedProperties extends Properties {
338 private final List<Object> keyOrder = new CopyOnWriteArrayList<>();
339
340 OrderedProperties(Map<?, ?> map) {
341 putAll(map);
342 }
343
344 @Override
345 public synchronized void putAll(Map<?, ?> t) {
346 t.forEach(this::put);
347 }
348
349 @Override
350 public Set<Object> keySet() {
351 return new KeySet();
352 }
353
354 @Override
355 public Set<Map.Entry<Object, Object>> entrySet() {
356 return new EntrySet();
357 }
358
359 @Override
360 public synchronized Object put(Object key, Object value) {
361 if (!keyOrder.contains(key)) {
362 keyOrder.add(key);
363 }
364 return super.put(key, value);
365 }
366
367 @Override
368 public synchronized Object setProperty(String key, String value) {
369 if (!keyOrder.contains(key)) {
370 keyOrder.add(key);
371 }
372 return super.setProperty(key, value);
373 }
374
375 @Override
376 public synchronized Object remove(Object key) {
377 keyOrder.remove(key);
378 return super.remove(key);
379 }
380
381 @Override
382 public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) {
383 entrySet().forEach(e -> action.accept(e.getKey(), e.getValue()));
384 }
385
386 private class EntrySet extends AbstractSet<Map.Entry<Object, Object>> {
387 @Override
388 public Iterator<Map.Entry<Object, Object>> iterator() {
389 return new Iterator<Map.Entry<Object, Object>>() {
390 Iterator<Object> keyIterator = keyOrder.iterator();
391 @Override
392 public boolean hasNext() {
393 return keyIterator.hasNext();
394 }
395
396 @Override
397 public Map.Entry<Object, Object> next() {
398 Object key = keyIterator.next();
399 return new Map.Entry<>() {
400 @Override
401 public Object getKey() {
402 return key;
403 }
404
405 @Override
406 public Object getValue() {
407 return get(key);
408 }
409
410 @Override
411 public Object setValue(Object value) {
412 return WrapperProperties.this.put(key, value);
413 }
414 };
415 }
416 };
417 }
418
419 @Override
420 public int size() {
421 return keyOrder.size();
422 }
423 }
424
425 private class KeySet extends AbstractSet<Object> {
426 public Iterator<Object> iterator() {
427 final Iterator<Object> iter = keyOrder.iterator();
428 return new Iterator<Object>() {
429 Object lastRet = null;
430 @Override
431 public boolean hasNext() {
432 return iter.hasNext();
433 }
434
435 @Override
436 public Object next() {
437 lastRet = iter.next();
438 return lastRet;
439 }
440
441 @Override
442 public void remove() {
443 WrapperProperties.super.remove(lastRet);
444 }
445 };
446 }
447
448 public int size() {
449 return keyOrder.size();
450 }
451
452 public boolean contains(Object o) {
453 return containsKey(o);
454 }
455
456 public boolean remove(Object o) {
457 boolean b = WrapperProperties.this.containsKey(o);
458 WrapperProperties.this.remove(o);
459 return b;
460 }
461
462 public void clear() {
463 WrapperProperties.this.clear();
464 }
465 }
466 }
467 }