|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectbb.util.Properties2
public class Properties2
This class is what Properties
ought to have been, namely,
a Map<String, String>
implementation with additional constraints and convenience methods.
Properties has issues:
Hashtable
(it should have used composition instead of inheritance,
that is, have a field of some Map type that it delegates to)
SortedMap
and ConcurrentMap
interfaces
(so it supports all that functionality, but is not commited to a particular implementation),
plus it constrains keys
and values
.
This class also offers much additional functionality that is not present in Properties.
First, it offers several convenience constructors for initializing name/value pairs from
Files
, Maps
, and Strings
.
Second, it adds many convenience getXXX
methods.
With these methods, the user specifies whether mappings are mandatory or are optional.
Specificly, every getXXX
method comes in one-arg and two-arg versions.
The one-arg versions take only a String key param, and have mandatory mapping semantics:
each will throw an IllegalStateException if key is not mapped to a value
(e.g. getProperty(key)
, getDouble(key)
, etc).
The two-arg versions take both a String key param and a String default value param, and have optional mapping semantics:
each will return the default value if key is not mapped to a value
(e.g. getProperty(key, valueDefault)
, getDouble(key, valueDefault)
, etc).
Note that the default value is never constrained, except for type correctness (e.g. it may be null).
Third, this class supports valid key checking
.
Fourth, this class supports the notion of effectively only a key being present;
see isKeyOnlyPresent
and putKeyOnly
.
This is useful for dealing with command line switches where only the key part is present;
see the String[]-arg constructor
.
Fifth, this class supports auditing.
It is not unusual in a large project for a Properties instance to be mutated at multiple points in the codebase,
and this mutation includes not only adding new key/value pairs, but also overriding existing key/value pairs.
If some piece of this configuration code is buggy, it can be very difficult to find the offender.
(This same issue, by the way, affects the System properties
.)
To ease this type of debugging, this class logs whenever its properties are mutated.
It not only logs the event and the state that changed, but it also logs the full stack trace of the calling thread.
Sixth, this class supports freezing its state
.
This allows initialization code to populate an instance, and then make it effectively immutable.
As with auditing, this can be invaluable in large codebases.
Be aware that freezing an instance has some subtle consequences.
In particular, after freezing, entrySet
, keySet
, and values
can no loner be called because all are potentially mutative
(if their results are mutated, the change is propogated to this class).
Users who need a view of the keys and values of this class after freezing should call copy
.
Since this class is not an instance of Properties, it offers interoperability support.
For converting Properties into Properties2, the toSortedMap
method
will convert a Properties instance into a Map instance
which can then be passed to the Map-arg constructor
or to putAll
.
This technique is used by the File[]-arg constructor
to support legacy properties files.
For converting Properties2 into Properties, the toProperties
method may be called.
This is useful when interacting with old APIs that require Properties instances.
Here is an example of a subclass which relaxes this class's restriction on values being non-blank. The subclass below allows values to be any non-null String (i.e. empty or all whitespace Strings are additionally allowed):
private static class Properties3 extends Properties2 {
// probably want to have more than the default no-arg constructor...
protected String checkValue(Object obj) throws IllegalArgumentException {
Check.arg().notNull(obj);
if (!(obj instanceof String)) throw new IllegalArgumentException("arg " + obj.toString() + " is of type " + obj.getClass().getName() + " which is not an instance of String");
return (String) obj;
}
}
This class is multithread safe: every public method is synchronized.
Nested Class Summary | |
---|---|
static class |
Properties2.UnitTest
See the Overview page of the project's javadocs for a general description of this unit test class. |
Nested classes/interfaces inherited from interface java.util.Map |
---|
Map.Entry<K,V> |
Field Summary | |
---|---|
private int |
auditDepth
|
private boolean |
frozen
|
private long |
instanceId
This instance's Id. |
private static AtomicLong |
instanceIdNext
The next Properties2 instance's instanceId field. |
private Logger2 |
logger2
|
private SortedMap<String,String> |
sortedMap
Holds all this instance's property data. |
private static String |
valueUndefined
|
Constructor Summary | |
---|---|
Properties2()
Constructs a new instance which contains no key/value pairs. |
|
Properties2(File... files)
Constructs a new instance with key/value pairs parsed from files. |
|
Properties2(Map<String,String> m)
Constructs a new instance with initial key/value pairs drawn from m. |
|
Properties2(String[] args)
Constructs a new instance with initial key/value pairs parsed from args. |
Method Summary | |
---|---|
private void |
audit(Level level,
String methodName,
String msg)
|
protected String |
checkKey(Object obj)
Checks that obj is a valid key. |
Properties2 |
checkKeys(Collection<String> keysLegal)
Checks if this instance contains any keys which are not elements of keysLegal. |
private void |
checkMutate(String msg)
|
protected String |
checkValue(Object obj)
Checks that obj is a valid value. |
void |
clear()
|
Comparator<? super String> |
comparator()
|
boolean |
containsKey(Object key)
|
boolean |
containsValue(Object value)
|
ConcurrentNavigableMap<String,String> |
copy()
Copies this instance into a new ConcurrentNavigableMap, which is then returned. |
Set<Map.Entry<String,String>> |
entrySet()
|
boolean |
equals(Object obj)
|
String |
firstKey()
|
void |
freeze()
Freezes this instances state to its present values. |
String |
get(Object key)
Returns the String value to which key is mapped, or null if this instance contains no mapping for key. |
boolean |
getBoolean(String key)
Returns Boolean.parseBoolean( . |
boolean |
getBoolean(String key,
boolean valueDefault)
If key has an associated value, then returns Boolean.parseBoolean(value) . |
byte |
getByte(String key)
Returns Byte.parseByte( . |
byte |
getByte(String key,
byte valueDefault)
If key has an associated value, then returns Byte.parseByte(value) . |
double |
getDouble(String key)
Returns Double.parseDouble( . |
double |
getDouble(String key,
double valueDefault)
If key has an associated value, then returns Double.parseDouble(value) . |
File |
getFile(String key)
Returns new File( . |
File |
getFile(String key,
File valueDefault)
If key has an associated value, then returns new File(value) . |
float |
getFloat(String key)
Returns Float.parseFloat( . |
float |
getFloat(String key,
float valueDefault)
If key has an associated value, then returns Float.parseFloat(value) . |
int |
getInt(String key)
Returns Integer.parseInt( . |
int |
getInt(String key,
int valueDefault)
If key has an associated value, then returns Integer.parseInt(value) . |
long |
getLong(String key)
Returns Long.parseLong( . |
long |
getLong(String key,
long valueDefault)
If key has an associated value, then returns Long.parseLong(value) . |
String |
getProperty(String key)
Returns the value associated with key. |
String |
getProperty(String key,
String valueDefault)
If key has an associated value, then returns that value. |
short |
getShort(String key)
Returns Short.parseShort( . |
short |
getShort(String key,
short valueDefault)
If key has an associated value, then returns Short.parseShort(value) . |
protected String |
getValueUndefined()
Returns the value associated with every key added by putKeyOnly . |
int |
hashCode()
|
SortedMap<String,String> |
headMap(String toKey)
|
boolean |
isEmpty()
|
boolean |
isFrozen()
Reports whether or not this instance's state has been frozen . |
boolean |
isKeyOnlyPresent(String key)
Reports whether or not only key is effectively in this instance (i.e. it has no substantial associated value). |
protected boolean |
isSwitchKey(String s)
Determines whether or not s is a key for a command line switch. |
Set<String> |
keySet()
|
String |
lastKey()
|
static Properties |
parseProperties(File file)
Parses a new Properties instance from the contents of file. |
String |
put(String key,
String value)
Associates key with value, replacing any previous mapping of key. |
void |
putAll(Map<? extends String,? extends String> m)
Copies all of the mappings from m to this instance. |
String |
putIfAbsent(String key,
String value)
|
String |
putKeyOnly(String key)
Associates key with the result of getValueUndefined . |
String |
remove(Object key)
|
boolean |
remove(Object key,
Object value)
|
String |
replace(String key,
String value)
|
boolean |
replace(String key,
String valueOld,
String valueNew)
|
int |
size()
|
SortedMap<String,String> |
subMap(String fromKey,
String toKey)
|
SortedMap<String,String> |
tailMap(String fromKey)
|
Properties |
toProperties()
Converts this instance into a new Properties instance, which is then returned. |
static SortedMap<String,String> |
toSortedMap(Properties properties)
Converts properties into a SortedMap . |
Collection<String> |
values()
|
Methods inherited from class java.lang.Object |
---|
clone, finalize, getClass, notify, notifyAll, toString, wait, wait, wait |
Field Detail |
---|
private static final AtomicLong instanceIdNext
instanceId
field.
Contract: is initialized to 0, and if this field has that value, it means that no instances have been created.
private static final String valueUndefined
getValueUndefined
,
Constant Field Valuesprivate final long instanceId
private final SortedMap<String,String> sortedMap
private final Logger2 logger2
private int auditDepth
private boolean frozen
Constructor Detail |
---|
public Properties2(File... files) throws IllegalArgumentException, SecurityException, RuntimeException
Each element of files is processed in sequence as follows:
parsed
into a new Properties
instanceconverted
into a SortedMap
putAll
This method never throws a checked Exception in order to support convenient one-line field assignments like
private final int port = new Properties2(file).getInt("Port", 7462);
RuntimeException
- (or some subclass) if any error occurs; this may merely wrap some other underlying Throwable
IllegalArgumentException
SecurityException
public Properties2(Map<String,String> m) throws IllegalArgumentException
IllegalArgumentException
- if m is null;
m contains a key which fails checkKey
;
m contains a value which fails checkValue
public Properties2(String[] args) throws IllegalArgumentException
The key/value pairs in args must be a series of command line switches (since args is typically supplied on the command line to a program's main). The precise format is:
isSwitchKey
putKeyOnly(java.lang.String)
A common use case for this constructor is to parse the command line arguments passed to some class's main
method.
Here is an example which illustrates how this class can parse, verify, and exploit command line arguments:
public class Operations
private static final String operation_key = "-operation";
private static final String n_key = "-n";
private static final List keysLegal = Arrays.asList( operation_key, numberLoops_key );
public static void main(String[] args) {
// may also want to use bb.util.Execute.XXX here...
Check.arg().notEmpty(args);
Properties2 properties = new Properties2(args).checkKeys(keysLegal); // parses args into name/value pairs and verifies that only legal keys present
String operation = properties.getProperty(operation_key); // gets a mandatory property
int n = properties.getInt(n_key, 10); // gets an optional property as an int, and uses a default value if absent
...
}
}
This class might be executed on a command line like
java Operations -operation add -a 1 -b 2
IllegalArgumentException
- if args is null or violates a format rulepublic Properties2()
Method Detail |
---|
public static Properties parseProperties(File file) throws RuntimeException
Properties
instance from the contents of file.
The format of file must be compatible with Properties.load
.
RuntimeException
- (or some subclass) if any error occurs; this may merely wrap some other underlying Throwablepublic static SortedMap<String,String> toSortedMap(Properties properties) throws IllegalArgumentException
SortedMap
.
The keys are obtained by calling Properties.stringPropertyNames()
.
So, only those key/value pairs where both are String instances are in the result.
Furthermore, in addition to the String keys immediately in properties,
the chain of default properties
is drawn on as well.
IllegalArgumentException
- if properties is nullprotected boolean isSwitchKey(String s)
protected String checkKey(Object obj) throws IllegalArgumentException
This class requires keys to be String instances which are not blank. Subclasses may override to implement their own key constraints.
IllegalArgumentException
- if obj is null, is not an instance of String, or is blank
protected String checkValue(Object obj) throws IllegalArgumentException
This class imposes the same requirements on values as keys,
so the implementation here simply calls
.
Subclasses may override to implement their own value constraints.
checkKey
(obj)
IllegalArgumentException
- if obj is null, is not an instance of String, or is blank
public Properties2 checkKeys(Collection<String> keysLegal) throws IllegalArgumentException, IllegalStateException
A common use for this method is verifying command line arguments,
as illustrated in the String[]-arg constructor
javadocs.
IllegalArgumentException
- if keysLegal is null
IllegalStateException
- if this instance contains any keys which are not elements of keysLegalprivate void audit(Level level, String methodName, String msg)
public void freeze()
public boolean isFrozen()
frozen
.
private void checkMutate(String msg) throws IllegalStateException
IllegalStateException
public void clear() throws IllegalStateException
clear
in interface Map<String,String>
IllegalStateException
- if isFrozen
returns truepublic boolean containsKey(Object key) throws IllegalArgumentException
containsKey
in interface Map<String,String>
IllegalArgumentException
- if key fails checkKey
public boolean containsValue(Object value) throws IllegalArgumentException
containsValue
in interface Map<String,String>
IllegalArgumentException
- if value fails checkValue
public Set<Map.Entry<String,String>> entrySet() throws IllegalStateException
entrySet
in interface Map<String,String>
entrySet
in interface SortedMap<String,String>
IllegalStateException
- if isFrozen
returns truepublic boolean equals(Object obj)
equals
in interface Map<String,String>
equals
in class Object
public String get(Object key) throws IllegalArgumentException, IllegalStateException
Contract: the result is only null if key has no mapping, since this class does not permit null keys or values.
Furthermore, if non-null, then the result is guaranteed to pass checkValue
.
get
in interface Map<String,String>
IllegalArgumentException
- if key fails checkKey
IllegalStateException
getProperty(String)
,
getProperty(String, String)
public int hashCode()
hashCode
in interface Map<String,String>
hashCode
in class Object
public boolean isEmpty()
isEmpty
in interface Map<String,String>
public Set<String> keySet() throws IllegalStateException
keySet
in interface Map<String,String>
keySet
in interface SortedMap<String,String>
IllegalStateException
- if isFrozen
returns truepublic String put(String key, String value) throws IllegalStateException, IllegalArgumentException
put
in interface Map<String,String>
IllegalStateException
- if isFrozen
returns true
IllegalArgumentException
- if key fails checkKey
; value fails checkValue
public void putAll(Map<? extends String,? extends String> m) throws IllegalStateException, IllegalArgumentException
putAll
in interface Map<String,String>
IllegalStateException
- if isFrozen
returns true
IllegalArgumentException
- if m is null;
m contains a key which fails checkKey
;
m contains a value which fails checkValue
public String remove(Object key) throws IllegalStateException
remove
in interface Map<String,String>
IllegalStateException
- if isFrozen
returns truepublic int size()
size
in interface Map<String,String>
public Collection<String> values() throws IllegalStateException
values
in interface Map<String,String>
values
in interface SortedMap<String,String>
IllegalStateException
- if isFrozen
returns truepublic Comparator<? super String> comparator()
comparator
in interface SortedMap<String,String>
public SortedMap<String,String> subMap(String fromKey, String toKey) throws IllegalStateException
subMap
in interface SortedMap<String,String>
IllegalStateException
- if isFrozen
returns truepublic SortedMap<String,String> headMap(String toKey) throws IllegalStateException
headMap
in interface SortedMap<String,String>
IllegalStateException
- if isFrozen
returns truepublic SortedMap<String,String> tailMap(String fromKey) throws IllegalStateException
tailMap
in interface SortedMap<String,String>
IllegalStateException
- if isFrozen
returns truepublic String firstKey()
firstKey
in interface SortedMap<String,String>
public String lastKey()
lastKey
in interface SortedMap<String,String>
public String putIfAbsent(String key, String value) throws IllegalStateException, IllegalArgumentException
putIfAbsent
in interface ConcurrentMap<String,String>
IllegalStateException
- if isFrozen
returns true
IllegalArgumentException
- if key fails checkKey
; value fails checkValue
public boolean remove(Object key, Object value) throws IllegalStateException, IllegalArgumentException
remove
in interface ConcurrentMap<String,String>
IllegalStateException
- if isFrozen
returns true
IllegalArgumentException
- if key fails checkKey
; value fails checkValue
public String replace(String key, String value) throws IllegalStateException, IllegalArgumentException
replace
in interface ConcurrentMap<String,String>
IllegalStateException
- if isFrozen
returns true
IllegalArgumentException
- if key fails checkKey
; value fails checkValue
public boolean replace(String key, String valueOld, String valueNew) throws IllegalStateException, IllegalArgumentException
replace
in interface ConcurrentMap<String,String>
IllegalStateException
- if isFrozen
returns true
IllegalArgumentException
- if key fails checkKey
; valueOld or valueNew fail checkValue
public String getProperty(String key) throws IllegalArgumentException, IllegalStateException
Is identical to get
except that null is never returned (an IllegalStateException is thrown instead).
IllegalArgumentException
- if key fails checkKey
IllegalStateException
- if key is not associated with any valuepublic boolean getBoolean(String key) throws IllegalArgumentException, IllegalStateException
Boolean.parseBoolean( getProperty
(key) )
.
IllegalArgumentException
- if key fails checkKey
IllegalStateException
- if key is not associated with any valuepublic byte getByte(String key) throws IllegalArgumentException, IllegalStateException, NumberFormatException
Byte.parseByte( getProperty
(key) )
.
IllegalArgumentException
- if key fails checkKey
IllegalStateException
- if key is not associated with any value
NumberFormatException
- if the value is not a parsable bytepublic double getDouble(String key) throws IllegalArgumentException, IllegalStateException, NumberFormatException
Double.parseDouble( getProperty
(key) )
.
IllegalArgumentException
- if key fails checkKey
IllegalStateException
- if key is not associated with any value
NumberFormatException
- if the value is not a parsable doublepublic File getFile(String key) throws IllegalArgumentException, IllegalStateException
new File( getProperty
(key) )
.
IllegalArgumentException
- if key fails checkKey
IllegalStateException
- if key is not associated with any valuepublic float getFloat(String key) throws IllegalArgumentException, IllegalStateException, NumberFormatException
Float.parseFloat( getProperty
(key) )
.
IllegalArgumentException
- if key fails checkKey
IllegalStateException
- if key is not associated with any value
NumberFormatException
- if the value is not a parsable floatpublic int getInt(String key) throws IllegalArgumentException, IllegalStateException, NumberFormatException
Integer.parseInt( getProperty
(key) )
.
IllegalArgumentException
- if key fails checkKey
IllegalStateException
- if key is not associated with any value
NumberFormatException
- if the value is not a parsable intpublic long getLong(String key) throws IllegalArgumentException, IllegalStateException, NumberFormatException
Long.parseLong( getProperty
(key) )
.
IllegalArgumentException
- if key fails checkKey
IllegalStateException
- if key is not associated with any value
NumberFormatException
- if the value is not a parsable longpublic short getShort(String key) throws IllegalArgumentException, IllegalStateException, NumberFormatException
Short.parseShort( getProperty
(key) )
.
IllegalArgumentException
- if key fails checkKey
IllegalStateException
- if key is not associated with any value
NumberFormatException
- if the value is not a parsable shortpublic String getProperty(String key, String valueDefault) throws IllegalArgumentException
Is identical to get
except that null is never returned (valueDefault is returned instead).
IllegalArgumentException
- if key fails checkKey
public boolean getBoolean(String key, boolean valueDefault) throws IllegalArgumentException
Boolean.parseBoolean(value)
.
Otherwise, returns valueDefault.
IllegalArgumentException
- if key fails checkKey
public byte getByte(String key, byte valueDefault) throws IllegalArgumentException, NumberFormatException
Byte.parseByte(value)
.
Otherwise, returns valueDefault.
IllegalArgumentException
- if key fails checkKey
NumberFormatException
- if the value is not a parsable bytepublic double getDouble(String key, double valueDefault) throws IllegalArgumentException, NumberFormatException
Double.parseDouble(value)
.
Otherwise, returns valueDefault.
IllegalArgumentException
- if key fails checkKey
NumberFormatException
- if the value is not a parsable doublepublic File getFile(String key, File valueDefault) throws IllegalArgumentException, NumberFormatException
new File(value)
.
Otherwise, returns valueDefault.
IllegalArgumentException
- if key fails checkKey
NumberFormatException
public float getFloat(String key, float valueDefault) throws IllegalArgumentException, NumberFormatException
Float.parseFloat(value)
.
Otherwise, returns valueDefault.
IllegalArgumentException
- if key fails checkKey
NumberFormatException
- if the value is not a parsable floatpublic int getInt(String key, int valueDefault) throws IllegalArgumentException, NumberFormatException
Integer.parseInt(value)
.
Otherwise, returns valueDefault.
IllegalArgumentException
- if key fails checkKey
NumberFormatException
- if the value is not a parsable intpublic long getLong(String key, long valueDefault) throws IllegalArgumentException, NumberFormatException
Long.parseLong(value)
.
Otherwise, returns valueDefault.
IllegalArgumentException
- if key fails checkKey
NumberFormatException
- if the value is not a parsable longpublic short getShort(String key, short valueDefault) throws IllegalArgumentException, NumberFormatException
Short.parseShort(value)
.
Otherwise, returns valueDefault.
IllegalArgumentException
- if key fails checkKey
NumberFormatException
- if the value is not a parsable shortpublic boolean isKeyOnlyPresent(String key) throws IllegalArgumentException
In principle, this method should only return true if
was the last mapping done on key.
So, this method is implemented as putKeyOnly
(key)return
(note that the getValueUndefined()
== get
(key)==
operator, not the equals
method, is used).
IllegalArgumentException
- if key fails checkKey
public String putKeyOnly(String key) throws IllegalStateException, IllegalArgumentException
getValueUndefined
.
This special value is meant for situations in which only the presence of key in this instance matters
and its associated value is irrelevant.
For example, if this instance represents a set of command line switches,
it is often the case that many of the switches just have a key part and no associated value.
IllegalStateException
- if isFrozen
returns true
IllegalArgumentException
- if key fails checkKey
protected String getValueUndefined()
putKeyOnly
.
Contract: the result must be the same String instance for a given Properties2 instance.
(This class satisfies this by returning a static constant, however, subclasses may behave differently.)
Furthermore, the result must pass checkValue
.
public ConcurrentNavigableMap<String,String> copy()
clone
.
The result is not backed by this instance (so mutating the result does not affect this instance).
Motivation: because instances can be frozen
,
this method was written to get a view of this instance whose keys and values can always be interrogated.
Recall that entrySet
, keySet
, and values
are potentially mutative methods, and so they will throw an IllegalStateException if called after an instance has been frozen.
public Properties toProperties()
System.setProperties
).
|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |