1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration2;
18
19 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertFalse;
22 import static org.junit.jupiter.api.Assertions.assertNotEquals;
23 import static org.junit.jupiter.api.Assertions.assertNull;
24 import static org.junit.jupiter.api.Assertions.assertSame;
25 import static org.junit.jupiter.api.Assertions.assertThrows;
26 import static org.junit.jupiter.api.Assertions.assertTrue;
27
28 import java.io.Reader;
29 import java.io.StringReader;
30 import java.io.StringWriter;
31 import java.net.URL;
32 import java.util.Deque;
33 import java.util.Iterator;
34
35 import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
36 import org.apache.commons.configuration2.convert.LegacyListDelimiterHandler;
37 import org.apache.commons.configuration2.event.ConfigurationEvent;
38 import org.apache.commons.configuration2.event.EventListener;
39 import org.apache.commons.configuration2.ex.ConfigurationException;
40 import org.junit.jupiter.api.BeforeEach;
41 import org.junit.jupiter.api.Test;
42
43
44
45
46 public class TestPropertiesConfigurationLayout {
47
48
49
50
51 static class LayoutTestConfiguration extends PropertiesConfiguration {
52
53
54 private PropertiesBuilder builder;
55
56
57
58
59 @Override
60 boolean propertyLoaded(final String key, final String value, final Deque<URL> seenStack) throws ConfigurationException {
61 if (builder == null) {
62 return super.propertyLoaded(key, value, seenStack);
63 }
64 if (PropertiesConfiguration.getInclude().equals(key)) {
65 getLayout().load(this, builder.getReader());
66 return false;
67 }
68 return true;
69 }
70 }
71
72
73
74
75 static class PropertiesBuilder {
76
77
78 private final StringBuilder buf = new StringBuilder();
79
80
81 private int commentCounter;
82
83
84
85
86
87
88 public void addComment(final String s) {
89 if (s != null) {
90 if (commentCounter % 2 == 0) {
91 buf.append("# ");
92 } else {
93 buf.append("! ");
94 }
95 buf.append(s);
96 commentCounter++;
97 }
98 buf.append(CR);
99 }
100
101
102
103
104
105
106 public void addLine(final String s) {
107 buf.append(s).append(CR);
108 }
109
110
111
112
113
114
115
116 public void addProperty(final String key, final String value) {
117 buf.append(key).append(" = ").append(value).append(CR);
118 }
119
120
121
122
123
124
125 public Reader getReader() {
126 return new StringReader(buf.toString());
127 }
128
129
130
131
132
133
134 @Override
135 public String toString() {
136 return buf.toString();
137 }
138 }
139
140
141 private static final String CR = System.lineSeparator();
142
143
144 private static final String CRNORM = "\n";
145
146
147 private static final String TEST_KEY = "myProperty";
148
149
150 private static final String TEST_COMMENT = "A comment for my test property";
151
152
153 private static final String TEST_VALUE = "myPropertyValue";
154
155
156 private PropertiesConfigurationLayout layout;
157
158
159 private LayoutTestConfiguration config;
160
161
162 private PropertiesBuilder builder;
163
164
165
166
167
168
169
170 private void checkLayoutString(final String expected) throws ConfigurationException {
171 assertEquals(expected, getLayoutString());
172 }
173
174
175
176
177 private void fillLayout() {
178 builder.addComment("A header comment");
179 builder.addComment(null);
180 builder.addProperty("prop", "value");
181 builder.addComment(TEST_COMMENT);
182 builder.addProperty(TEST_KEY, TEST_VALUE);
183 builder.addProperty("anotherProp", "anotherValue");
184 builder.addComment("A footer comment");
185 assertDoesNotThrow(() -> layout.load(config, builder.getReader()));
186 }
187
188
189
190
191
192
193
194 private String getLayoutString() throws ConfigurationException {
195 final StringWriter out = new StringWriter();
196 layout.save(config, out);
197 return out.toString();
198 }
199
200 @BeforeEach
201 public void setUp() throws Exception {
202 config = new LayoutTestConfiguration();
203 config.setListDelimiterHandler(new LegacyListDelimiterHandler(','));
204 layout = new PropertiesConfigurationLayout();
205 config.setLayout(layout);
206 builder = new PropertiesBuilder();
207 }
208
209
210
211
212 @Test
213 void testBlankLines() throws ConfigurationException {
214 builder.addProperty("prop", "value");
215 builder.addComment(null);
216 builder.addComment(null);
217 builder.addComment(TEST_COMMENT);
218 builder.addComment(null);
219 builder.addProperty(TEST_KEY, TEST_VALUE);
220 layout.load(config, builder.getReader());
221 assertEquals(2, layout.getBlankLinesBefore(TEST_KEY));
222 assertEquals(TEST_COMMENT + CRNORM, layout.getCanonicalComment(TEST_KEY, false));
223 assertEquals(TEST_VALUE, config.getString(TEST_KEY));
224 }
225
226
227
228
229 @Test
230 void testBlankLinesWithHeaderComment() throws ConfigurationException {
231 builder.addComment(TEST_COMMENT);
232 builder.addComment(null);
233 builder.addComment(null);
234 builder.addComment(TEST_COMMENT);
235 builder.addProperty(TEST_KEY, TEST_VALUE);
236 layout.load(config, builder.getReader());
237 assertEquals(2, layout.getBlankLinesBefore(TEST_KEY));
238 assertEquals(TEST_COMMENT, layout.getCanonicalComment(TEST_KEY, false));
239 assertEquals(TEST_VALUE, config.getString(TEST_KEY));
240 }
241
242
243
244
245 @Test
246 void testCombineComments() throws ConfigurationException {
247 builder.addComment(TEST_COMMENT);
248 builder.addProperty(TEST_KEY, TEST_VALUE);
249 builder.addComment(null);
250 builder.addComment(TEST_COMMENT);
251 builder.addProperty(TEST_KEY, TEST_VALUE + "2");
252 layout.load(config, builder.getReader());
253 assertEquals(TEST_COMMENT + CRNORM + TEST_COMMENT, layout.getCanonicalComment(TEST_KEY, false));
254 assertEquals(0, layout.getBlankLinesBefore(TEST_KEY));
255 }
256
257
258
259
260 @Test
261 void testEventAdd() {
262 final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.ADD_PROPERTY, TEST_KEY, TEST_VALUE, false);
263 layout.onEvent(event);
264 assertTrue(layout.getKeys().contains(TEST_KEY));
265 assertEquals(0, layout.getBlankLinesBefore(TEST_KEY));
266 assertTrue(layout.isSingleLine(TEST_KEY));
267 assertEquals(" = ", layout.getSeparator(TEST_KEY));
268 }
269
270
271
272
273 @Test
274 void testEventAddBefore() {
275 final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.ADD_PROPERTY, TEST_KEY, TEST_VALUE, true);
276 layout.onEvent(event);
277 assertFalse(layout.getKeys().contains(TEST_KEY));
278 }
279
280
281
282
283 @Test
284 void testEventAddExisting() throws ConfigurationException {
285 builder.addComment(TEST_COMMENT);
286 builder.addProperty(TEST_KEY, TEST_VALUE);
287 layout.load(config, builder.getReader());
288 final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.ADD_PROPERTY, TEST_KEY, TEST_VALUE, false);
289 layout.onEvent(event);
290 assertFalse(layout.isSingleLine(TEST_KEY));
291 assertEquals(TEST_COMMENT, layout.getCanonicalComment(TEST_KEY, false));
292 }
293
294
295
296
297 @Test
298 void testEventAddMultiple() {
299 final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.ADD_PROPERTY, TEST_KEY, TEST_VALUE, false);
300 layout.onEvent(event);
301 layout.onEvent(event);
302 assertFalse(layout.isSingleLine(TEST_KEY));
303 }
304
305
306
307
308 @Test
309 void testEventClearConfig() throws Exception {
310 fillLayout();
311 final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.CLEAR, null, null, false);
312 layout.onEvent(event);
313 assertTrue(layout.getKeys().isEmpty());
314 assertNull(layout.getHeaderComment());
315 }
316
317
318
319
320 @Test
321 void testEventDelete() {
322 ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.ADD_PROPERTY, TEST_KEY, TEST_VALUE, false);
323 layout.onEvent(event);
324 event = new ConfigurationEvent(this, ConfigurationEvent.CLEAR_PROPERTY, TEST_KEY, null, false);
325 layout.onEvent(event);
326 assertFalse(layout.getKeys().contains(TEST_KEY));
327 }
328
329
330
331
332 @Test
333 void testEventSetNonExisting() {
334 final ConfigurationEvent event = new ConfigurationEvent(this, ConfigurationEvent.SET_PROPERTY, TEST_KEY, TEST_VALUE, false);
335 layout.onEvent(event);
336 assertTrue(layout.getKeys().contains(TEST_KEY));
337 }
338
339
340
341
342 @Test
343 void testGetNonExistingLayouData() {
344 assertNull(layout.getComment("unknown"));
345 assertTrue(layout.isSingleLine("unknown"));
346 assertEquals(0, layout.getBlankLinesBefore("unknown"));
347 }
348
349
350
351
352 @Test
353 void testGetNullLayouttData() {
354 assertThrows(IllegalArgumentException.class, () -> layout.setComment(null, TEST_COMMENT));
355 }
356
357
358
359
360 @Test
361 void testHeaderComment() throws ConfigurationException {
362 builder.addComment(TEST_COMMENT);
363 builder.addComment(null);
364 builder.addProperty(TEST_KEY, TEST_VALUE);
365 layout.load(config, builder.getReader());
366 assertEquals(TEST_COMMENT, layout.getCanonicalHeaderComment(false));
367 assertNull(layout.getCanonicalComment(TEST_KEY, false));
368 }
369
370
371
372
373 @Test
374 void testHeaderCommentNull() {
375 assertNull(layout.getCanonicalHeaderComment(true));
376 assertNull(layout.getCanonicalHeaderComment(false));
377 }
378
379
380
381
382 @Test
383 void testHeaderCommentWithBlanks() throws ConfigurationException {
384 builder.addComment(TEST_COMMENT);
385 builder.addComment(null);
386 builder.addComment(TEST_COMMENT);
387 builder.addComment(null);
388 builder.addProperty(TEST_KEY, TEST_VALUE);
389 layout.load(config, builder.getReader());
390 assertEquals(TEST_COMMENT + CRNORM + CRNORM + TEST_COMMENT, layout.getCanonicalHeaderComment(false));
391 assertNull(layout.getComment(TEST_KEY));
392 }
393
394
395
396
397
398 @Test
399 void testHeaderCommentWithBlanksAndPresetHeaderComment() throws ConfigurationException {
400 final String presetHeaderComment = "preset" + TEST_COMMENT + CRNORM + CRNORM + TEST_COMMENT;
401 builder.addComment(TEST_COMMENT);
402 builder.addComment(null);
403 builder.addComment(TEST_COMMENT);
404 builder.addComment(null);
405 builder.addProperty(TEST_KEY, TEST_VALUE);
406 layout.setHeaderComment(presetHeaderComment);
407 layout.load(config, builder.getReader());
408 assertEquals(presetHeaderComment, layout.getCanonicalHeaderComment(false));
409 assertNull(layout.getComment(TEST_KEY));
410 }
411
412
413
414
415
416 @Test
417 void testHeaderCommentWithBlanksAndPropComment() throws ConfigurationException {
418 builder.addComment(TEST_COMMENT);
419 builder.addComment(null);
420 builder.addComment(TEST_COMMENT);
421 builder.addComment(null);
422 builder.addComment(TEST_COMMENT);
423 builder.addProperty(TEST_KEY, TEST_VALUE);
424 layout.load(config, builder.getReader());
425 assertEquals(TEST_COMMENT + CRNORM + CRNORM + TEST_COMMENT, layout.getCanonicalHeaderComment(false));
426 assertEquals(TEST_COMMENT, layout.getCanonicalComment(TEST_KEY, false));
427 }
428
429
430
431
432 @Test
433 void testInit() {
434 assertTrue(layout.getKeys().isEmpty());
435 assertNull(layout.getHeaderComment());
436 final Iterator<EventListener<? super ConfigurationEvent>> it = config.getEventListeners(ConfigurationEvent.ANY).iterator();
437 assertTrue(it.hasNext());
438 assertSame(layout, it.next());
439 assertFalse(it.hasNext());
440 assertFalse(layout.isForceSingleLine());
441 assertNull(layout.getGlobalSeparator());
442 }
443
444
445
446
447 @Test
448 void testInitCopy() {
449 fillLayout();
450 final PropertiesConfigurationLayout l2 = new PropertiesConfigurationLayout(layout);
451 assertEquals(l2.getKeys(), layout.getKeys());
452 assertEquals(layout.getHeaderComment(), l2.getHeaderComment());
453 assertEquals(layout.getFooterComment(), l2.getFooterComment());
454 }
455
456
457
458
459 @Test
460 void testInitCopyModify() {
461 fillLayout();
462 final PropertiesConfigurationLayout l2 = new PropertiesConfigurationLayout(layout);
463 assertEquals(layout.getComment(TEST_KEY), l2.getComment(TEST_KEY));
464 layout.setComment(TEST_KEY, "A new comment");
465 assertEquals(TEST_COMMENT, l2.getCanonicalComment(TEST_KEY, false));
466 l2.setBlankLinesBefore(TEST_KEY, l2.getBlankLinesBefore(TEST_KEY) + 1);
467 assertNotEquals(layout.getBlankLinesBefore(TEST_KEY), l2.getBlankLinesBefore(TEST_KEY));
468 }
469
470
471
472
473 @Test
474 void testInitNull() {
475 layout = new PropertiesConfigurationLayout(null);
476 assertTrue(layout.getKeys().isEmpty());
477 }
478
479
480
481
482 @Test
483 void testIsSingleLine() throws ConfigurationException {
484 builder.addProperty(TEST_KEY, TEST_VALUE + "," + TEST_VALUE + "2");
485 layout.load(config, builder.getReader());
486 assertTrue(layout.isSingleLine(TEST_KEY));
487 assertEquals(2, config.getList(TEST_KEY).size());
488 }
489
490
491
492
493 @Test
494 void testIsSingleLineMulti() throws ConfigurationException {
495 builder.addProperty(TEST_KEY, TEST_VALUE);
496 builder.addProperty("anotherProp", "a value");
497 builder.addProperty(TEST_KEY, TEST_VALUE + "2");
498 layout.load(config, builder.getReader());
499 assertFalse(layout.isSingleLine(TEST_KEY));
500 assertEquals(2, config.getList(TEST_KEY).size());
501 }
502
503
504
505
506 @Test
507 void testLineWithBlank() throws ConfigurationException {
508 builder.addComment(TEST_COMMENT);
509 builder.addLine(" ");
510 builder.addProperty(TEST_KEY, TEST_VALUE);
511 layout.load(config, builder.getReader());
512 assertEquals(TEST_COMMENT + CRNORM + " ", layout.getCanonicalComment(TEST_KEY, false));
513 }
514
515
516
517
518 @Test
519 void testReadAndWrite() throws ConfigurationException {
520 builder.addComment("This is my test properties file,");
521 builder.addComment("which contains a header comment.");
522 builder.addComment(null);
523 builder.addComment(null);
524 builder.addComment(TEST_COMMENT);
525 builder.addProperty(TEST_KEY, TEST_COMMENT);
526 builder.addComment(null);
527 builder.addComment(null);
528 builder.addComment("Another comment");
529 builder.addProperty("property", "and a value");
530 layout.load(config, builder.getReader());
531 checkLayoutString(builder.toString());
532 }
533
534
535
536
537 @Test
538 void testReadSimple() throws ConfigurationException {
539 builder.addComment(TEST_COMMENT);
540 builder.addProperty(TEST_KEY, TEST_VALUE);
541 layout.load(config, builder.getReader());
542 assertNull(layout.getHeaderComment());
543 assertEquals(1, layout.getKeys().size());
544 assertTrue(layout.getKeys().contains(TEST_KEY));
545 assertEquals(TEST_COMMENT, layout.getCanonicalComment(TEST_KEY, false));
546 assertEquals(0, layout.getBlankLinesBefore(TEST_KEY));
547 assertTrue(layout.isSingleLine(TEST_KEY));
548 assertEquals(TEST_VALUE, config.getString(TEST_KEY));
549 }
550
551
552
553
554 @Test
555 void testRecursiveLoadCall() throws ConfigurationException {
556 final PropertiesBuilder b = new PropertiesBuilder();
557 b.addComment("A nested header comment.");
558 b.addComment("With multiple lines");
559 b.addComment(null);
560 b.addComment("Second comment");
561 b.addProperty(TEST_KEY, TEST_VALUE);
562 b.addProperty(TEST_KEY + "2", TEST_VALUE + "2");
563 config.builder = b;
564
565 builder.addComment("Header comment");
566 builder.addComment(null);
567 builder.addComment(TEST_COMMENT);
568 builder.addProperty(TEST_KEY, TEST_VALUE);
569 builder.addComment("Include file");
570 builder.addProperty(PropertiesConfiguration.getInclude(), "test");
571
572 layout.load(config, builder.getReader());
573
574 assertEquals("Header comment", layout.getCanonicalHeaderComment(false));
575 assertFalse(layout.getKeys().contains(PropertiesConfiguration.getInclude()));
576 assertEquals(TEST_COMMENT + CRNORM + "A nested header comment." + CRNORM + "With multiple lines" + CRNORM + CRNORM + "Second comment",
577 layout.getCanonicalComment(TEST_KEY, false));
578 }
579
580
581
582
583 @Test
584 void testSave() throws ConfigurationException {
585 config.addProperty(TEST_KEY, TEST_VALUE);
586 layout.setComment(TEST_KEY, TEST_COMMENT);
587 config.addProperty(TEST_KEY, TEST_VALUE + "2");
588 config.addProperty("AnotherProperty", "AnotherValue");
589 config.addProperty("AnotherProperty", "3rdValue");
590 layout.setComment("AnotherProperty", "AnotherComment");
591 layout.setBlankLinesBefore("AnotherProperty", 2);
592 layout.setSingleLine("AnotherProperty", true);
593 layout.setHeaderComment("A header comment" + CRNORM + "for my properties");
594 checkLayoutString("# A header comment" + CR + "# for my properties" + CR + CR + "# " + TEST_COMMENT + CR + TEST_KEY + " = " + TEST_VALUE + CR + TEST_KEY
595 + " = " + TEST_VALUE + "2" + CR + CR + CR + "# AnotherComment" + CR + "AnotherProperty = AnotherValue,3rdValue" + CR);
596 }
597
598
599
600
601
602 @Test
603 void testSaveCommentForUnexistingProperty() throws ConfigurationException {
604 fillLayout();
605 layout.setComment("NonExistingKey", "NonExistingComment");
606 final String output = getLayoutString();
607 assertFalse(output.contains("NonExistingKey"));
608 assertFalse(output.contains("NonExistingComment"));
609 }
610
611
612
613
614 @Test
615 void testSaveEmptyLayout() throws ConfigurationException {
616 checkLayoutString("");
617 }
618
619
620
621
622 @Test
623 void testSaveForceSingleLine() throws ConfigurationException {
624 config.setListDelimiterHandler(new DefaultListDelimiterHandler(';'));
625 config.addProperty(TEST_KEY, TEST_VALUE);
626 config.addProperty(TEST_KEY, TEST_VALUE + "2");
627 config.addProperty("AnotherProperty", "value1;value2;value3");
628 layout.setComment(TEST_KEY, TEST_COMMENT);
629 layout.setForceSingleLine(true);
630 checkLayoutString(
631 "# " + TEST_COMMENT + CR + TEST_KEY + " = " + TEST_VALUE + ';' + TEST_VALUE + "2" + CR + "AnotherProperty = value1;value2;value3" + CR);
632 }
633
634
635
636
637 @Test
638 void testSetGlobalSeparator() throws ConfigurationException {
639 final String sep = "=";
640 config.addProperty(TEST_KEY, TEST_VALUE);
641 config.addProperty("key2", "value2");
642 layout.setSeparator(TEST_KEY, " : ");
643 layout.setGlobalSeparator(sep);
644 checkLayoutString(TEST_KEY + sep + TEST_VALUE + CR + "key2" + sep + "value2" + CR);
645 }
646
647
648
649
650 @Test
651 void testSetLineSeparator() throws ConfigurationException {
652 final String lf = CR + CR;
653 config.addProperty(TEST_KEY, TEST_VALUE);
654 layout.setBlankLinesBefore(TEST_KEY, 2);
655 layout.setComment(TEST_KEY, TEST_COMMENT);
656 layout.setHeaderComment("Header comment");
657 layout.setLineSeparator(lf);
658 checkLayoutString("# Header comment" + lf + lf + lf + "# " + TEST_COMMENT + lf + TEST_KEY + " = " + TEST_VALUE + lf);
659 }
660
661
662
663
664 @Test
665 void testSetLineSeparatorInComments() throws ConfigurationException {
666 final String lf = "<-\n";
667 config.addProperty(TEST_KEY, TEST_VALUE);
668 layout.setComment(TEST_KEY, TEST_COMMENT + "\nMore comment");
669 layout.setHeaderComment("Header\ncomment");
670 layout.setLineSeparator(lf);
671 checkLayoutString("# Header" + lf + "# comment" + lf + lf + "# " + TEST_COMMENT + lf + "# More comment" + lf + TEST_KEY + " = " + TEST_VALUE + lf);
672 }
673
674
675
676
677 @Test
678 void testSetNullComment() {
679 fillLayout();
680 layout.setComment(TEST_KEY, null);
681 assertNull(layout.getComment(TEST_KEY));
682 }
683
684
685
686
687 @Test
688 void testSetSeparator() throws ConfigurationException {
689 config.addProperty(TEST_KEY, TEST_VALUE);
690 layout.setSeparator(TEST_KEY, ":");
691 checkLayoutString(TEST_KEY + ":" + TEST_VALUE + CR);
692 }
693
694
695
696
697 @Test
698 void testTrimComment() {
699 assertEquals("This is a comment" + CR + "that spans multiple" + CR + "lines in a" + CR + " complex way.",
700 PropertiesConfigurationLayout.trimComment(
701 " # This is a comment" + CR + "that spans multiple" + CR + "!lines in a" + CR + " complex way.",
702 false
703 ));
704 }
705
706
707
708
709 @Test
710 void testTrimCommentFalse() {
711 assertEquals("# Comment with" + CR + " ! some mixed " + CR + "#comment" + CR + "# lines",
712 PropertiesConfigurationLayout.trimComment("Comment with" + CR + " ! some mixed " + CR + "#comment" + CR + "lines", true));
713 }
714
715
716
717
718 @Test
719 void testTrimCommentTrainlingCR() {
720 assertEquals("Comment with" + CR + "trailing CR" + CR,
721 PropertiesConfigurationLayout.trimComment("Comment with" + CR + "! trailing CR" + CR, false));
722 }
723 }