Solution working also for java > 8
Despite the fact that the question does not explicitly mention about store(OutputStream out, String comments)
functionality, I find this (and load(InputStream inStream)
respectively) to be the most interesting part of Properties
.
If we were not interested in storing/loading properties we could just replace Properties
with some Map
or SortedMap
implementation.
The difference in Properties
compared to Map
is the existence of store()
/load()
methods which offer some specific serialization rules. These rules have not (and will not) change across java versions. e.g.
- Storing pairs like
prop=value
- Escaping rules for unicode and special characters.
- Adding optional comment on top
Unfortunately the above 3 parts of the functionality are hidden inside private methods, and cannot be easily re-used inside extended or alternative implementations that wish to use different internal data-structures (e.g. for keeping properties sorted).
So what remains is to keep the same internal structures and override the two store()
methods.
Note: For getting sorted properties, some time ago I followed danisupr4 implementation, but on java9 it broke.
TL;DR
The code below maintains the full functionality of Properties
by overriding only store(OutputStream out, String comments)
method, and is tested with all versions from java5
- java12
.
I believe that filtering the output of this public method makes the implementation less fragile to future code changes in java.util.Properties
class.
class SortedStoreProperties extends Properties {
@Override
public void store(OutputStream out, String comments) throws IOException {
Properties sortedProps = new Properties() {
@Override
public Set<Map.Entry<Object, Object>> entrySet() {
/*
* Using comparator to avoid the following exception on jdk >=9:
* java.lang.ClassCastException: java.base/java.util.concurrent.ConcurrentHashMap$MapEntry cannot be cast to java.base/java.lang.Comparable
*/
Set<Map.Entry<Object, Object>> sortedSet = new TreeSet<Map.Entry<Object, Object>>(new Comparator<Map.Entry<Object, Object>>() {
@Override
public int compare(Map.Entry<Object, Object> o1, Map.Entry<Object, Object> o2) {
return o1.getKey().toString().compareTo(o2.getKey().toString());
}
}
);
sortedSet.addAll(super.entrySet());
return sortedSet;
}
@Override
public Set<Object> keySet() {
return new TreeSet<Object>(super.keySet());
}
@Override
public synchronized Enumeration<Object> keys() {
return Collections.enumeration(new TreeSet<Object>(super.keySet()));
}
};
sortedProps.putAll(this);
sortedProps.store(out, comments);
}
}
Note: Depending on who calls the store()
, it might be even better, to not override the existing method but create a new one: e.g. storeSorted()
.
NOTE:
For simplicity I override only one of the two store()
methods but the same concept applies to both.