27
27
import java .util .stream .Collectors ;
28
28
import java .util .stream .IntStream ;
29
29
30
+ /**
31
+ * This class is a replacement for <code>java.util.Properties</code>, with the difference that it
32
+ * properly supports comments both for reading and writing. It also maintains an exact
33
+ * representation of the input, meaning that when an input is read and later written out again the
34
+ * output will match the input exactly. Methods exist for obtaining and setting comments on
35
+ * key-value pairs.
36
+ */
30
37
public class Properties extends AbstractMap <String , String > {
31
38
private final LinkedHashMap <String , String > values = new LinkedHashMap <>();
32
39
private final List <PropertiesParser .Token > tokens = new ArrayList <>();
@@ -64,13 +71,25 @@ public int size() {
64
71
};
65
72
}
66
73
74
+ /**
75
+ * Works like <code>keySet()</code> but returning the keys' raw values. Meaning that the keys
76
+ * haven't been unescaped before being returned.
77
+ *
78
+ * @return A set of raw key values
79
+ */
67
80
public Set <String > rawKeySet () {
68
81
return tokens .stream ()
69
82
.filter (t -> t .type == PropertiesParser .Type .KEY )
70
83
.map (PropertiesParser .Token ::getRaw )
71
84
.collect (Collectors .toCollection (LinkedHashSet ::new ));
72
85
}
73
86
87
+ /**
88
+ * Works like <code>values()</code> but returning the raw values. Meaning that the values have
89
+ * not been unescaped before being returned.
90
+ *
91
+ * @return a collection of raw values.
92
+ */
74
93
public Collection <String > rawValues () {
75
94
return IntStream .range (0 , tokens .size ())
76
95
.filter (idx -> tokens .get (idx ).type == PropertiesParser .Type .KEY )
@@ -83,6 +102,13 @@ public String get(Object key) {
83
102
return values .get (key );
84
103
}
85
104
105
+ /**
106
+ * Works like <code>get()</code> but returns the raw value associated with the given raw key.
107
+ * This means that the value won't be unescaped before being returned.
108
+ *
109
+ * @param rawKey The key too look up in raw format
110
+ * @return A raw value or <code>null</code> if the key wasn't found
111
+ */
86
112
public String getRaw (String rawKey ) {
87
113
int idx = indexOf (unescape (rawKey ));
88
114
if (idx >= 0 ) {
@@ -105,8 +131,8 @@ public String put(String key, String value) {
105
131
}
106
132
107
133
/**
108
- * Works like ` put()` but uses raw values for keys and values. This means these keys and values
109
- * will not be escaped before being serialized .
134
+ * Works like <code> put()</code> but uses raw values for keys and values. This means these keys
135
+ * and values will not be escaped before being stored .
110
136
*
111
137
* @param rawKey key with which the specified value is to be associated
112
138
* @param rawValue value to be associated with the specified key
@@ -155,7 +181,8 @@ public String remove(Object key) {
155
181
/**
156
182
* Gather all the comments directly before the given key and return them as a list. The list
157
183
* will only contain those lines that immediately follow one another, once a non-comment line is
158
- * encountered gathering will stop.
184
+ * encountered gathering will stop. The returned values will include the comment character that
185
+ * the line started with in the original input.
159
186
*
160
187
* @param key The key to look for
161
188
* @return A list of comment strings or an empty list if no comments lines were found or the key
@@ -172,10 +199,34 @@ private List<String> getComment(List<Integer> indices) {
172
199
.collect (Collectors .toList ()));
173
200
}
174
201
202
+ /**
203
+ * Adds the given comments to the item indicated by the given key. Each comment will be put on a
204
+ * separate line. Each comment should start with one of the valid comment symbols <code>#</code>
205
+ * or <code>!</code>, but if none is encountered the code will select one for you (it will look
206
+ * at any existing comments, or at symbols found on previous items and as a last result will use
207
+ * <code># </code>).
208
+ *
209
+ * @param key The key to look for
210
+ * @param comments The comments to add to the item
211
+ * @return The previous list of comments, if any
212
+ * @throws NoSuchElementException Thrown when they key couldn't be found
213
+ */
175
214
public List <String > setComment (String key , String ... comments ) {
176
215
return setComment (key , Arrays .asList (comments ));
177
216
}
178
217
218
+ /**
219
+ * Adds the list of comments to the item indicated by the given key. Each comment will be put on
220
+ * a separate line. Each comment should start with one of the valid comment symbols <code>#
221
+ * </code> or <code>!</code>, but if none is encountered the code will select one for you (it
222
+ * will look at any existing comments, or at symbols found on previous items and as a last
223
+ * result will use <code># </code>).
224
+ *
225
+ * @param key The key to look for
226
+ * @param comments The list of comments to add to the item
227
+ * @return The previous list of comments, if any
228
+ * @throws NoSuchElementException Thrown when they key couldn't be found
229
+ */
179
230
public List <String > setComment (String key , List <String > comments ) {
180
231
int idx = indexOf (key );
181
232
if (idx < 0 ) {
@@ -315,22 +366,49 @@ private static String replace(String input, Pattern regex, Function<Matcher, Str
315
366
return resultString .toString ();
316
367
}
317
368
369
+ /**
370
+ * Returns a <code>java.util.Properties</code> with the same contents as this object. The
371
+ * information is a copy, changes to one Properties object will not affect the other.
372
+ *
373
+ * @return a <code>java.util.Properties</code> object
374
+ */
318
375
public java .util .Properties asJUProperties () {
319
376
return asJUProperties (null );
320
377
}
321
378
379
+ /**
380
+ * Returns a <code>java.util.Properties</code> with the same contents as this object and with
381
+ * the given <code>java.util.Properties</code> object as fallback. The information is a copy,
382
+ * changes to one Properties object will not affect the other.
383
+ *
384
+ * @return a <code>java.util.Properties</code> object
385
+ */
322
386
public java .util .Properties asJUProperties (java .util .Properties defaults ) {
323
387
java .util .Properties p = new java .util .Properties (defaults );
324
388
p .putAll (this );
325
389
return p ;
326
390
}
327
391
392
+ /**
393
+ * Loads the contents from the given file and stores it in this object. This includes not only
394
+ * key-value pairs but also all whitespace and any comments that are encountered.
395
+ *
396
+ * @param file a path to the file to load
397
+ * @throws IOException Thrown when any IO error occurs during loading
398
+ */
328
399
public void load (Path file ) throws IOException {
329
400
try (Reader br = Files .newBufferedReader (file )) {
330
401
load (br );
331
402
}
332
403
}
333
404
405
+ /**
406
+ * Loads the contents from the reader and stores it in this object. This includes not only
407
+ * key-value pairs but also all whitespace and any comments that are encountered.
408
+ *
409
+ * @param reader a <code>Reader</code> object
410
+ * @throws IOException Thrown when any IO error occurs during loading
411
+ */
334
412
public void load (Reader reader ) throws IOException {
335
413
tokens .clear ();
336
414
BufferedReader br =
@@ -349,24 +427,50 @@ public void load(Reader reader) throws IOException {
349
427
}
350
428
}
351
429
430
+ /**
431
+ * Returns a <code>Properties</code> with the contents read from the given file. This includes
432
+ * not only key-value pairs but also all whitespace and any comments that are encountered.
433
+ *
434
+ * @param file a path to the file to load
435
+ * @throws IOException Thrown when any IO error occurs during loading
436
+ */
352
437
public static Properties loadProperties (Path file ) throws IOException {
353
438
Properties props = new Properties ();
354
439
props .load (file );
355
440
return props ;
356
441
}
357
442
443
+ /**
444
+ * Returns a <code>Properties</code> with the contents read from the given file. This includes
445
+ * not only key-value pairs but also all whitespace and any comments that are encountered.
446
+ *
447
+ * @param reader a <code>Reader</code> object
448
+ * @throws IOException Thrown when any IO error occurs during loading
449
+ */
358
450
public static Properties loadProperties (Reader reader ) throws IOException {
359
451
Properties props = new Properties ();
360
452
props .load (reader );
361
453
return props ;
362
454
}
363
455
456
+ /**
457
+ * Stores the contents of this object to the given file.
458
+ *
459
+ * @param file a path to the file to write
460
+ * @throws IOException
461
+ */
364
462
public void store (Path file ) throws IOException {
365
463
try (Writer bw = Files .newBufferedWriter (file , StandardOpenOption .TRUNCATE_EXISTING )) {
366
464
store (bw );
367
465
}
368
466
}
369
467
468
+ /**
469
+ * Stores the contents of this object to the given file.
470
+ *
471
+ * @param writer a <code>Writer</code> object
472
+ * @throws IOException
473
+ */
370
474
public void store (Writer writer ) throws IOException {
371
475
for (PropertiesParser .Token token : tokens ) {
372
476
writer .write (token .getRaw ());
0 commit comments