1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.common.util;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.io.Reader;
23 import java.io.Writer;
24 import java.nio.charset.Charset;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.Enumeration;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Properties;
32 import java.util.Set;
33 import java.util.TreeSet;
34
35 import org.apache.commons.io.FileUtils;
36 import org.apache.commons.io.IOUtils;
37 import org.apache.commons.lang3.StringUtils;
38 import org.jasypt.util.text.TextEncryptor;
39 import org.kuali.common.util.property.Constants;
40 import org.kuali.common.util.property.GlobalPropertiesMode;
41 import org.kuali.common.util.property.ProjectProperties;
42 import org.kuali.common.util.property.PropertiesContext;
43 import org.kuali.common.util.property.processor.AddPropertiesProcessor;
44 import org.kuali.common.util.property.processor.PropertyProcessor;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.springframework.util.Assert;
48 import org.springframework.util.PropertyPlaceholderHelper;
49
50
51
52
53
54
55 public class PropertyUtils {
56
57 private static final Logger logger = LoggerFactory.getLogger(PropertyUtils.class);
58
59 private static final String XML_EXTENSION = ".xml";
60 private static final String ENV_PREFIX = "env";
61 private static final String DEFAULT_ENCODING = Charset.defaultCharset().name();
62 private static final String DEFAULT_XML_ENCODING = "UTF-8";
63
64 public static void appendToOrSetProperty(Properties properties, String key, String value) {
65 Assert.hasText(value);
66 String existingValue = properties.getProperty(key);
67 if (existingValue == null) {
68 existingValue = "";
69 }
70 String newValue = existingValue + value;
71 properties.setProperty(key, newValue);
72 }
73
74 public static Properties load(List<ProjectProperties> pps) {
75
76
77 Properties properties = new Properties();
78
79
80 for (ProjectProperties pp : pps) {
81
82
83 PropertiesContext ctx = pp.getPropertiesContext();
84
85
86 Properties combined = PropertyUtils.combine(properties, ctx.getProperties());
87
88
89 ctx.setProperties(combined);
90
91
92 Properties loaded = load(ctx);
93
94
95 properties.putAll(loaded);
96 }
97
98
99 return properties;
100 }
101
102 public static Properties load(PropertiesContext context) {
103
104 if (CollectionUtils.isEmpty(context.getLocations())) {
105 return PropertyUtils.toEmpty(context.getProperties());
106 }
107
108
109 Assert.notNull(context.getHelper(), "helper is null");
110 Assert.notNull(context.getLocations(), "locations are null");
111 Assert.notNull(context.getEncoding(), "encoding is null");
112 Assert.notNull(context.getMissingLocationsMode(), "missingLocationsMode is null");
113
114
115 Properties global = PropertyUtils.getGlobalProperties();
116
117
118 context.setProperties(PropertyUtils.toEmpty(context.getProperties()));
119
120
121 Properties result = new Properties();
122
123
124 result.putAll(PropertyUtils.toEmpty(context.getProperties()));
125
126
127 for (String location : context.getLocations()) {
128
129
130 Properties resolverProperties = PropertyUtils.combine(context.getProperties(), result, global);
131
132
133 String resolvedLocation = context.getHelper().replacePlaceholders(location, resolverProperties);
134
135
136 if (LocationUtils.exists(resolvedLocation)) {
137
138
139 Properties properties = PropertyUtils.load(resolvedLocation, context.getEncoding());
140
141
142 result.putAll(properties);
143 } else {
144
145
146 ModeUtils.validate(context.getMissingLocationsMode(), "Non-existent location [" + resolvedLocation + "]");
147 }
148 }
149
150
151 return result;
152 }
153
154
155
156
157
158
159
160
161 public static void decrypt(Properties properties, TextEncryptor encryptor) {
162 decrypt(properties, encryptor, null, null);
163 }
164
165
166
167
168
169
170
171
172 public static Properties getEncryptedProperties(Properties properties) {
173 List<String> keys = getSortedKeys(properties);
174 Properties encrypted = new Properties();
175 for (String key : keys) {
176 String value = properties.getProperty(key);
177 if (isEncryptedPropertyValue(value)) {
178 encrypted.setProperty(key, value);
179 }
180 }
181 return encrypted;
182 }
183
184
185
186
187
188
189
190
191 public static void decrypt(Properties properties, TextEncryptor encryptor, List<String> includes, List<String> excludes) {
192 List<String> keys = getSortedKeys(properties, includes, excludes);
193 for (String key : keys) {
194 String value = properties.getProperty(key);
195 if (isEncryptedPropertyValue(value)) {
196 String decryptedValue = decryptPropertyValue(encryptor, value);
197 properties.setProperty(key, decryptedValue);
198 }
199 }
200 }
201
202
203
204
205 public static boolean isEncryptedPropertyValue(String value) {
206 return StringUtils.startsWith(value, Constants.ENCRYPTION_PREFIX) && StringUtils.endsWith(value, Constants.ENCRYPTION_SUFFIX);
207 }
208
209
210
211
212
213
214
215
216 public static void encrypt(Properties properties, TextEncryptor encryptor) {
217 encrypt(properties, encryptor, null, null);
218 }
219
220
221
222
223
224
225
226
227 public static void encrypt(Properties properties, TextEncryptor encryptor, List<String> includes, List<String> excludes) {
228 List<String> keys = getSortedKeys(properties, includes, excludes);
229 for (String key : keys) {
230 String originalValue = properties.getProperty(key);
231 String encryptedValue = encryptPropertyValue(encryptor, originalValue);
232 properties.setProperty(key, encryptedValue);
233 }
234 }
235
236
237
238
239
240
241
242
243 public static String decryptPropertyValue(TextEncryptor encryptor, String value) {
244
245 Assert.isTrue(StringUtils.startsWith(value, Constants.ENCRYPTION_PREFIX), "value does not start with " + Constants.ENCRYPTION_PREFIX);
246 Assert.isTrue(StringUtils.endsWith(value, Constants.ENCRYPTION_SUFFIX), "value does not end with " + Constants.ENCRYPTION_SUFFIX);
247
248
249 int start = Constants.ENCRYPTION_PREFIX.length();
250 int end = StringUtils.length(value) - Constants.ENCRYPTION_SUFFIX.length();
251 String unwrapped = StringUtils.substring(value, start, end);
252
253
254 return encryptor.decrypt(unwrapped);
255 }
256
257
258
259
260
261
262
263
264 public static String encryptPropertyValue(TextEncryptor encryptor, String value) {
265 String encryptedValue = encryptor.encrypt(value);
266 StringBuilder sb = new StringBuilder();
267 sb.append(Constants.ENCRYPTION_PREFIX);
268 sb.append(encryptedValue);
269 sb.append(Constants.ENCRYPTION_SUFFIX);
270 return sb.toString();
271 }
272
273 public static void overrideWithGlobalValues(Properties properties, GlobalPropertiesMode mode) {
274 List<String> keys = PropertyUtils.getSortedKeys(properties);
275 Properties global = PropertyUtils.getProperties(mode);
276 for (String key : keys) {
277 String globalValue = global.getProperty(key);
278 if (!StringUtils.isBlank(globalValue)) {
279 properties.setProperty(key, globalValue);
280 }
281 }
282 }
283
284 public static final Properties combine(List<Properties> properties) {
285 Properties combined = new Properties();
286 for (Properties p : properties) {
287 combined.putAll(PropertyUtils.toEmpty(p));
288 }
289 return combined;
290 }
291
292 public static final Properties combine(Properties... properties) {
293 return combine(Arrays.asList(properties));
294 }
295
296 public static final void process(Properties properties, PropertyProcessor processor) {
297 process(properties, Collections.singletonList(processor));
298 }
299
300 public static final void process(Properties properties, List<PropertyProcessor> processors) {
301 for (PropertyProcessor processor : CollectionUtils.toEmptyList(processors)) {
302 processor.process(properties);
303 }
304 }
305
306 public static final Properties toEmpty(Properties properties) {
307 return properties == null ? new Properties() : properties;
308 }
309
310 public static final boolean isSingleUnresolvedPlaceholder(String string) {
311 return isSingleUnresolvedPlaceholder(string, Constants.DEFAULT_PLACEHOLDER_PREFIX, Constants.DEFAULT_PLACEHOLDER_SUFFIX);
312 }
313
314 public static final boolean isSingleUnresolvedPlaceholder(String string, String prefix, String suffix) {
315 int prefixMatches = StringUtils.countMatches(string, prefix);
316 int suffixMatches = StringUtils.countMatches(string, suffix);
317 boolean startsWith = StringUtils.startsWith(string, prefix);
318 boolean endsWith = StringUtils.endsWith(string, suffix);
319 return prefixMatches == 1 && suffixMatches == 1 && startsWith && endsWith;
320 }
321
322 public static final boolean containsUnresolvedPlaceholder(String string) {
323 return containsUnresolvedPlaceholder(string, Constants.DEFAULT_PLACEHOLDER_PREFIX, Constants.DEFAULT_PLACEHOLDER_SUFFIX);
324 }
325
326 public static final boolean containsUnresolvedPlaceholder(String string, String prefix, String suffix) {
327 int beginIndex = StringUtils.indexOf(string, prefix);
328 if (beginIndex == -1) {
329 return false;
330 }
331 return StringUtils.indexOf(string, suffix) != -1;
332 }
333
334
335
336
337
338 public static final Properties getResolvedProperties(Properties properties) {
339 return getResolvedProperties(properties, Constants.DEFAULT_PROPERTY_PLACEHOLDER_HELPER, Constants.DEFAULT_GLOBAL_PROPERTIES_MODE);
340 }
341
342
343
344
345
346 public static final Properties getResolvedProperties(Properties properties, GlobalPropertiesMode globalPropertiesMode) {
347 return getResolvedProperties(properties, Constants.DEFAULT_PROPERTY_PLACEHOLDER_HELPER, globalPropertiesMode);
348 }
349
350
351
352
353
354 public static final Properties getResolvedProperties(Properties properties, PropertyPlaceholderHelper helper) {
355 return getResolvedProperties(properties, helper, Constants.DEFAULT_GLOBAL_PROPERTIES_MODE);
356 }
357
358
359
360
361
362 public static final Properties getResolvedProperties(Properties properties, PropertyPlaceholderHelper helper, GlobalPropertiesMode globalPropertiesMode) {
363 Properties global = PropertyUtils.getProperties(properties, globalPropertiesMode);
364 List<String> keys = PropertyUtils.getSortedKeys(properties);
365 Properties newProperties = new Properties();
366 for (String key : keys) {
367 String originalValue = properties.getProperty(key);
368 String resolvedValue = helper.replacePlaceholders(originalValue, global);
369 if (!resolvedValue.equals(originalValue)) {
370 logger.debug("Resolved property '" + key + "' [{}] -> [{}]", Str.flatten(originalValue), Str.flatten(resolvedValue));
371 newProperties.setProperty(key, resolvedValue);
372 }
373 }
374 return newProperties;
375 }
376
377
378
379
380 public static final List<String> getValues(Properties properties, List<String> keys) {
381 List<String> values = new ArrayList<String>();
382 for (String key : keys) {
383 values.add(properties.getProperty(key));
384 }
385 return values;
386 }
387
388
389
390
391 public static final List<String> getEndsWithKeys(Properties properties, String suffix) {
392 List<String> keys = getSortedKeys(properties);
393 List<String> matches = new ArrayList<String>();
394 for (String key : keys) {
395 if (StringUtils.endsWith(key, suffix)) {
396 matches.add(key);
397 }
398 }
399 return matches;
400 }
401
402
403
404
405 public static final void trim(Properties properties, String includesCSV, String excludesCSV) {
406 List<String> includes = CollectionUtils.getTrimmedListFromCSV(includesCSV);
407 List<String> excludes = CollectionUtils.getTrimmedListFromCSV(excludesCSV);
408 trim(properties, includes, excludes);
409 }
410
411
412
413
414 public static final void trim(Properties properties, List<String> includes, List<String> excludes) {
415 List<String> keys = getSortedKeys(properties);
416 for (String key : keys) {
417 if (!include(key, includes, excludes)) {
418 logger.debug("Removing [{}]", key);
419 properties.remove(key);
420 }
421 }
422 }
423
424
425
426
427
428
429
430
431
432 public static final boolean include(String value, List<String> includes, List<String> excludes) {
433 if (isSingleWildcardMatch(value, excludes)) {
434
435 return false;
436 } else {
437
438 return CollectionUtils.isEmpty(includes) || isSingleWildcardMatch(value, includes);
439 }
440 }
441
442 public static final boolean isSingleWildcardMatch(String s, List<String> patterns) {
443 for (String pattern : CollectionUtils.toEmptyList(patterns)) {
444 if (isSingleWildcardMatch(s, pattern)) {
445 return true;
446 }
447 }
448 return false;
449 }
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469 public static final boolean isSingleWildcardMatch(String value, String pattern) {
470 if (value == null && pattern == null) {
471
472 return true;
473 } else if (value != null && pattern == null || value == null && pattern != null) {
474
475 return false;
476 } else if (pattern.equals(Constants.WILDCARD)) {
477
478 return true;
479 } else if (StringUtils.countMatches(pattern, Constants.WILDCARD) > 1) {
480
481 throw new IllegalArgumentException("Pattern [" + pattern + "] is not supported. Only one wildcard is allowed in the pattern");
482 } else if (!StringUtils.contains(pattern, Constants.WILDCARD)) {
483
484 return StringUtils.equals(value, pattern);
485 } else {
486
487
488
489 int pos = StringUtils.indexOf(pattern, Constants.WILDCARD);
490 int suffixPos = pos + Constants.WILDCARD.length();
491 boolean nullPrefix = pos == 0;
492 boolean nullSuffix = suffixPos >= pattern.length();
493 String prefix = nullPrefix ? null : StringUtils.substring(pattern, 0, pos);
494 String suffix = nullSuffix ? null : StringUtils.substring(pattern, suffixPos);
495 boolean prefixMatch = nullPrefix || StringUtils.startsWith(value, prefix);
496 boolean suffixMatch = nullSuffix || StringUtils.endsWith(value, suffix);
497 return prefixMatch && suffixMatch;
498 }
499 }
500
501
502
503
504 public static final Properties getProperties(Properties properties, String include, String exclude) {
505 List<String> keys = getSortedKeys(properties, include, exclude);
506 Properties newProperties = new Properties();
507 for (String key : keys) {
508 String value = properties.getProperty(key);
509 newProperties.setProperty(key, value);
510 }
511 return newProperties;
512 }
513
514
515
516
517 public static final List<String> getSortedKeys(Properties properties, String include, String exclude) {
518 return getSortedKeys(properties, CollectionUtils.toEmptyList(include), CollectionUtils.toEmptyList(exclude));
519 }
520
521
522
523
524 public static final List<String> getSortedKeys(Properties properties, List<String> includes, List<String> excludes) {
525 List<String> keys = getSortedKeys(properties);
526 List<String> includedKeys = new ArrayList<String>();
527 for (String key : keys) {
528 if (include(key, includes, excludes)) {
529 includedKeys.add(key);
530 }
531 }
532 return includedKeys;
533 }
534
535
536
537
538 public static final List<String> getStartsWithKeys(Properties properties, String prefix) {
539 List<String> keys = getSortedKeys(properties);
540 List<String> matches = new ArrayList<String>();
541 for (String key : keys) {
542 if (StringUtils.startsWith(key, prefix)) {
543 matches.add(key);
544 }
545 }
546 return matches;
547 }
548
549
550
551
552 public static final List<String> getSortedKeys(Properties properties) {
553 List<String> keys = new ArrayList<String>(properties.stringPropertyNames());
554 Collections.sort(keys);
555 return keys;
556 }
557
558 public static final String toString(Properties properties) {
559 List<String> keys = getSortedKeys(properties);
560 StringBuilder sb = new StringBuilder();
561 for (String key : keys) {
562 String value = Str.flatten(properties.getProperty(key));
563 sb.append(key + "=" + value + "\n");
564 }
565 return sb.toString();
566 }
567
568 public static final void info(Properties properties) {
569 properties = toEmpty(properties);
570 logger.info("--- Displaying {} properties ---\n\n{}", properties.size(), toString(properties));
571 }
572
573 public static final void debug(Properties properties) {
574 properties = toEmpty(properties);
575 logger.debug("--- Displaying {} properties ---\n\n{}", properties.size(), toString(properties));
576 }
577
578
579
580
581 public static final void store(Properties properties, File file) {
582 store(properties, file, null);
583 }
584
585
586
587
588 public static final void store(Properties properties, File file, String encoding) {
589 store(properties, file, encoding, null);
590 }
591
592
593
594
595 public static final void store(Properties properties, File file, String encoding, String comment) {
596 OutputStream out = null;
597 Writer writer = null;
598 try {
599 out = FileUtils.openOutputStream(file);
600 String path = file.getCanonicalPath();
601 boolean xml = isXml(path);
602 Properties sorted = getSortedProperties(properties);
603 comment = getComment(encoding, comment, xml);
604 if (xml) {
605 logger.info("Storing XML properties - [{}] encoding={}", path, StringUtils.defaultIfBlank(encoding, DEFAULT_ENCODING));
606 if (encoding == null) {
607 sorted.storeToXML(out, comment);
608 } else {
609 sorted.storeToXML(out, comment, encoding);
610 }
611 } else {
612 writer = LocationUtils.getWriter(out, encoding);
613 logger.info("Storing properties - [{}] encoding={}", path, StringUtils.defaultIfBlank(encoding, DEFAULT_ENCODING));
614 sorted.store(writer, comment);
615 }
616 } catch (IOException e) {
617 throw new IllegalStateException("Unexpected IO error", e);
618 } finally {
619 IOUtils.closeQuietly(writer);
620 IOUtils.closeQuietly(out);
621 }
622 }
623
624
625
626
627
628 public static final Properties getGlobalProperties() {
629 return getProperties(Constants.DEFAULT_GLOBAL_PROPERTIES_MODE);
630 }
631
632
633
634
635
636 public static final Properties getGlobalProperties(Properties properties) {
637 return getProperties(properties, Constants.DEFAULT_GLOBAL_PROPERTIES_MODE);
638 }
639
640
641
642
643
644
645
646 public static final Properties getProperties(Properties properties, GlobalPropertiesMode mode) {
647 Properties newProperties = duplicate(properties);
648 List<PropertyProcessor> modifiers = getPropertyProcessors(mode);
649 for (PropertyProcessor modifier : modifiers) {
650 modifier.process(newProperties);
651 }
652 return newProperties;
653 }
654
655
656
657
658
659
660
661 public static final Properties getProperties(GlobalPropertiesMode mode) {
662 return getProperties(new Properties(), mode);
663 }
664
665
666
667
668 public static final String getProperty(String key, GlobalPropertiesMode mode) {
669 return getProperty(key, new Properties(), mode);
670 }
671
672
673
674
675
676 public static final String getProperty(String key, Properties properties, GlobalPropertiesMode mode) {
677 return getProperties(properties, mode).getProperty(key);
678 }
679
680
681
682
683 public static final List<PropertyProcessor> getPropertyProcessors(GlobalPropertiesMode mode) {
684 List<PropertyProcessor> processors = new ArrayList<PropertyProcessor>();
685 switch (mode) {
686 case NONE:
687 return processors;
688 case ENVIRONMENT:
689 processors.add(new AddPropertiesProcessor(getEnvAsProperties()));
690 return processors;
691 case SYSTEM:
692 processors.add(new AddPropertiesProcessor(System.getProperties()));
693 return processors;
694 case BOTH:
695 processors.add(new AddPropertiesProcessor(getEnvAsProperties()));
696 processors.add(new AddPropertiesProcessor(System.getProperties()));
697 return processors;
698 default:
699 throw new IllegalStateException(mode + " is unknown");
700 }
701 }
702
703
704
705
706 public static final Properties convert(Map<String, String> map) {
707 Properties props = new Properties();
708 for (String key : map.keySet()) {
709 String value = map.get(key);
710 props.setProperty(key, value);
711 }
712 return props;
713 }
714
715
716
717
718 public static final Properties duplicate(Properties properties) {
719 Properties newProperties = new Properties();
720 newProperties.putAll(properties);
721 return newProperties;
722 }
723
724
725
726
727 public static Properties getEnvAsProperties() {
728 return getEnvAsProperties(ENV_PREFIX);
729 }
730
731
732
733
734 public static Properties getEnvAsProperties(String prefix) {
735 Properties properties = convert(System.getenv());
736 return getPrefixedProperties(properties, prefix);
737 }
738
739
740
741
742 public static final boolean isXml(String location) {
743 return StringUtils.endsWithIgnoreCase(location, XML_EXTENSION);
744 }
745
746
747
748
749 public static final Properties load(File file) {
750 return load(file, null);
751 }
752
753
754
755
756 public static final Properties load(File file, String encoding) {
757 String location = LocationUtils.getCanonicalPath(file);
758 return load(location, encoding);
759 }
760
761
762
763
764 public static final Properties load(String location) {
765 return load(location, null);
766 }
767
768
769
770
771 public static final Properties load(String location, String encoding) {
772 InputStream in = null;
773 Reader reader = null;
774 try {
775 Properties properties = new Properties();
776 boolean xml = isXml(location);
777 location = getCanonicalLocation(location);
778 if (xml) {
779 in = LocationUtils.getInputStream(location);
780 logger.info("Loading XML properties - [{}]", location);
781 properties.loadFromXML(in);
782 } else {
783 logger.info("Loading properties - [{}] encoding={}", location, StringUtils.defaultIfBlank(encoding, DEFAULT_ENCODING));
784 reader = LocationUtils.getBufferedReader(location, encoding);
785 properties.load(reader);
786 }
787 return properties;
788 } catch (IOException e) {
789 throw new IllegalStateException("Unexpected IO error", e);
790 } finally {
791 IOUtils.closeQuietly(in);
792 IOUtils.closeQuietly(reader);
793 }
794 }
795
796 protected static String getCanonicalLocation(String location) {
797 if (LocationUtils.isExistingFile(location)) {
798 return LocationUtils.getCanonicalPath(new File(location));
799 } else {
800 return location;
801 }
802 }
803
804
805
806
807
808 public static final Properties getPrefixedProperties(Properties properties, String prefix) {
809 if (StringUtils.isBlank(prefix)) {
810 return duplicate(properties);
811 }
812 Properties newProperties = new Properties();
813 for (String key : properties.stringPropertyNames()) {
814 String value = properties.getProperty(key);
815 String newKey = StringUtils.startsWith(key, prefix + ".") ? key : prefix + "." + key;
816 newProperties.setProperty(newKey, value);
817 }
818 return newProperties;
819 }
820
821
822
823
824 public static final Properties reformatKeysAsEnvVars(Properties properties) {
825 Properties newProperties = new Properties();
826 for (String key : properties.stringPropertyNames()) {
827 String value = properties.getProperty(key);
828 String newKey = StringUtils.upperCase(StringUtils.replace(key, ".", "-"));
829 newProperties.setProperty(newKey, value);
830 }
831 return newProperties;
832 }
833
834
835
836
837
838 public static final void addOrOverrideProperty(Properties properties, String key, String newValue, Mode propertyOverwriteMode) {
839 String oldValue = properties.getProperty(key);
840 if (StringUtils.equals(newValue, oldValue)) {
841
842 return;
843 }
844 boolean overwrite = !StringUtils.isBlank(oldValue);
845
846
847 String logNewValue = newValue;
848 String logOldValue = oldValue;
849 if (obscure(key)) {
850 logNewValue = "PROTECTED";
851 logOldValue = "PROTECTED";
852 }
853
854 if (overwrite) {
855
856
857 Object[] args = new Object[] { key, Str.flatten(logNewValue), Str.flatten(logOldValue) };
858 ModeUtils.validate(propertyOverwriteMode, "Overriding [{}={}] was [{}]", args, "Override of existing property [" + key + "] is not allowed.");
859 } else {
860
861 logger.info("Adding [{}={}]", key, Str.flatten(logNewValue));
862 }
863 properties.setProperty(key, newValue);
864 }
865
866 protected static boolean obscure(String key) {
867 if (StringUtils.containsIgnoreCase(key, ".password")) {
868 return true;
869 }
870 if (StringUtils.containsIgnoreCase(key, ".secret")) {
871 return true;
872 }
873 if (StringUtils.containsIgnoreCase(key, ".private")) {
874 return true;
875 }
876 return false;
877 }
878
879 private static final String getDefaultComment(String encoding, boolean xml) {
880 if (encoding == null) {
881 if (xml) {
882
883 return "encoding.default=" + DEFAULT_XML_ENCODING;
884 } else {
885
886 return "encoding.default=" + DEFAULT_ENCODING;
887 }
888 } else {
889 return "encoding.specified=" + encoding;
890 }
891 }
892
893 private static final String getComment(String encoding, String comment, boolean xml) {
894 if (StringUtils.isBlank(comment)) {
895 return getDefaultComment(encoding, xml);
896 } else {
897 return comment + "\n#" + getDefaultComment(encoding, xml);
898 }
899 }
900
901
902
903
904 private static final SortedProperties getSortedProperties(Properties properties) {
905 SortedProperties sp = new PropertyUtils().new SortedProperties();
906 sp.putAll(properties);
907 return sp;
908 }
909
910
911
912
913 private class SortedProperties extends Properties {
914
915 private static final long serialVersionUID = 1330825236411537386L;
916
917
918
919
920 @Override
921 public Set<Object> keySet() {
922 return Collections.unmodifiableSet(new TreeSet<Object>(super.keySet()));
923 }
924
925
926
927
928 @Override
929 public synchronized Enumeration<Object> keys() {
930 return Collections.enumeration(new TreeSet<Object>(super.keySet()));
931 }
932 }
933
934
935
936
937
938
939
940
941
942
943
944
945 public static final void addListComparisonProperties(Properties properties, ComparisonResults listComparison, List<String> propertyNames) {
946
947 Assert.isTrue(propertyNames.size() == 3);
948
949 properties.setProperty(propertyNames.get(0), CollectionUtils.getCSV(listComparison.getAdded()));
950 properties.setProperty(propertyNames.get(1), CollectionUtils.getCSV(listComparison.getSame()));
951 properties.setProperty(propertyNames.get(2), CollectionUtils.getCSV(listComparison.getDeleted()));
952 }
953
954 }