1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.collections4.properties;
18
19 import java.util.AbstractCollection;
20 import java.util.AbstractMap.SimpleEntry;
21 import java.util.AbstractSet;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Enumeration;
25 import java.util.Iterator;
26 import java.util.LinkedHashSet;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.Properties;
30 import java.util.Set;
31 import java.util.function.BiConsumer;
32 import java.util.function.BiFunction;
33 import java.util.function.Function;
34 import java.util.stream.Collectors;
35
36
37
38
39
40
41
42
43
44
45 public class OrderedProperties extends Properties {
46
47
48
49
50 private final class KeySet extends AbstractSet<Object> {
51
52 @Override
53 public void clear() {
54 OrderedProperties.this.clear();
55 }
56
57 @Override
58 public boolean contains(final Object key) {
59 return containsKey(key);
60 }
61
62 @Override
63 public Iterator<Object> iterator() {
64 return orderedKeysIterator();
65 }
66
67 @Override
68 public boolean remove(final Object key) {
69 return OrderedProperties.this.remove(key) != null;
70 }
71
72 @Override
73 public int size() {
74 return OrderedProperties.this.size();
75 }
76 }
77
78
79
80
81 private final class Values extends AbstractCollection<Object> {
82
83 @Override
84 public void clear() {
85 OrderedProperties.this.clear();
86 }
87
88 @Override
89 public boolean contains(final Object value) {
90 return containsValue(value);
91 }
92
93 @Override
94 public Iterator<Object> iterator() {
95 final Iterator<Object> keys = orderedKeysIterator();
96 return new Iterator<Object>() {
97
98 @Override
99 public boolean hasNext() {
100 return keys.hasNext();
101 }
102
103 @Override
104 public Object next() {
105 return get(keys.next());
106 }
107
108 @Override
109 public void remove() {
110 keys.remove();
111 }
112 };
113 }
114
115 @Override
116 public int size() {
117 return OrderedProperties.this.size();
118 }
119 }
120
121 private static final long serialVersionUID = 1L;
122
123
124
125
126 private final LinkedHashSet<Object> orderedKeys = new LinkedHashSet<>();
127
128
129
130
131 public OrderedProperties() {
132
133 }
134
135 @Override
136 public synchronized void clear() {
137 orderedKeys.clear();
138 super.clear();
139 }
140
141 @Override
142 public synchronized Object compute(final Object key, final BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
143 final Object compute = super.compute(key, remappingFunction);
144 if (compute != null) {
145 orderedKeys.add(key);
146 } else {
147 orderedKeys.remove(key);
148 }
149 return compute;
150 }
151
152 @Override
153 public synchronized Object computeIfAbsent(final Object key, final Function<? super Object, ? extends Object> mappingFunction) {
154 final Object computeIfAbsent = super.computeIfAbsent(key, mappingFunction);
155 if (computeIfAbsent != null) {
156 orderedKeys.add(key);
157 }
158 return computeIfAbsent;
159 }
160
161 @Override
162 public Set<Map.Entry<Object, Object>> entrySet() {
163 return orderedKeys.stream().map(k -> new SimpleEntry<>(k, get(k))).collect(Collectors.toCollection(LinkedHashSet::new));
164 }
165
166
167
168
169
170
171
172 private synchronized LinkedHashSet<String> enumerateStringProperties(final LinkedHashSet<String> result) {
173 if (defaults != null) {
174 result.addAll(defaults.stringPropertyNames());
175 }
176 for (final Enumeration<?> e = keys(); e.hasMoreElements();) {
177 final Object k = e.nextElement();
178 final Object v = get(k);
179 if (k instanceof String && v instanceof String) {
180 result.add((String) k);
181 }
182 }
183 return result;
184 }
185
186 @Override
187 public synchronized void forEach(final BiConsumer<? super Object, ? super Object> action) {
188 Objects.requireNonNull(action, "action");
189 orderedKeys.forEach(k -> action.accept(k, get(k)));
190 }
191
192 @Override
193 public synchronized Enumeration<Object> keys() {
194 return Collections.enumeration(orderedKeys);
195 }
196
197 @Override
198 public Set<Object> keySet() {
199 return new KeySet();
200 }
201
202 @Override
203 public synchronized Object merge(final Object key, final Object value,
204 final BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
205 final Object merge = super.merge(key, value, remappingFunction);
206 if (merge != null) {
207 orderedKeys.add(key);
208 } else {
209 orderedKeys.remove(key);
210 }
211 return merge;
212 }
213
214
215
216
217
218
219 private Iterator<Object> orderedKeysIterator() {
220 final Iterator<Object> iterator = orderedKeys.iterator();
221 return new Iterator<Object>() {
222
223 private Object last;
224
225 @Override
226 public boolean hasNext() {
227 return iterator.hasNext();
228 }
229
230 @Override
231 public Object next() {
232 last = iterator.next();
233 return last;
234 }
235
236 @Override
237 public void remove() {
238
239 synchronized (OrderedProperties.this) {
240
241 iterator.remove();
242 OrderedProperties.super.remove(last);
243 }
244 }
245 };
246 }
247
248 @Override
249 public Enumeration<?> propertyNames() {
250 return Collections.enumeration(stringPropertyNames());
251 }
252
253 @Override
254 public synchronized Object put(final Object key, final Object value) {
255 final Object put = super.put(key, value);
256 if (put == null) {
257 orderedKeys.add(key);
258 }
259 return put;
260 }
261
262 @Override
263 public synchronized void putAll(final Map<? extends Object, ? extends Object> t) {
264 orderedKeys.addAll(t.keySet());
265 super.putAll(t);
266 }
267
268 @Override
269 public synchronized Object putIfAbsent(final Object key, final Object value) {
270 final Object putIfAbsent = super.putIfAbsent(key, value);
271 if (putIfAbsent == null) {
272 orderedKeys.add(key);
273 }
274 return putIfAbsent;
275 }
276
277 @Override
278 public synchronized Object remove(final Object key) {
279 final Object remove = super.remove(key);
280 if (remove != null) {
281 orderedKeys.remove(key);
282 }
283 return remove;
284 }
285
286 @Override
287 public synchronized boolean remove(final Object key, final Object value) {
288 final boolean remove = super.remove(key, value);
289 if (remove) {
290 orderedKeys.remove(key);
291 }
292 return remove;
293 }
294
295 @Override
296 public Set<String> stringPropertyNames() {
297 return enumerateStringProperties(new LinkedHashSet<>());
298 }
299
300 @Override
301 public synchronized String toString() {
302
303 final int max = size() - 1;
304 if (max == -1) {
305 return "{}";
306 }
307 final StringBuilder sb = new StringBuilder();
308 final Iterator<Map.Entry<Object, Object>> it = entrySet().iterator();
309 sb.append('{');
310 for (int i = 0;; i++) {
311 final Map.Entry<Object, Object> e = it.next();
312 final Object key = e.getKey();
313 final Object value = e.getValue();
314 sb.append(key == this ? "(this Map)" : key.toString());
315 sb.append('=');
316 sb.append(value == this ? "(this Map)" : value.toString());
317 if (i == max) {
318 return sb.append('}').toString();
319 }
320 sb.append(", ");
321 }
322 }
323
324 @Override
325 public Collection<Object> values() {
326 return new Values();
327 }
328 }