View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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 }