mirror of https://github.com/dspinellis/UMLGraph
support setting the value of an option using an external representation
This commit is contained in:
parent
7f785b94f8
commit
5827cbcdf9
|
|
@ -7,6 +7,8 @@ import junit.framework.TestSuite;
|
|||
import org.umlgraph.settings.SettingDefinitions;
|
||||
import org.umlgraph.settings.Settings;
|
||||
|
||||
import sun.management.snmp.jvminstr.JvmThreadInstanceEntryImpl.ThreadStateMap;
|
||||
|
||||
public class SettingTests extends TestCase {
|
||||
public static class TestBasicDefinitions implements SettingDefinitions {
|
||||
public static String option1;
|
||||
|
|
@ -239,4 +241,79 @@ public class SettingTests extends TestCase {
|
|||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestTypeCompatibility implements SettingDefinitions {
|
||||
public static String option1;
|
||||
public static Boolean option2;
|
||||
public static Integer option3;
|
||||
public static Thread.State option4;
|
||||
}
|
||||
|
||||
public void testTypeCompatibility() {
|
||||
Settings root = new Settings(TestTypeCompatibility.class);
|
||||
|
||||
root.set("@option1", "hello");
|
||||
try {
|
||||
root.set("@option1", Boolean.TRUE);
|
||||
fail("accepted a boolean value for a string setting");
|
||||
} catch (ClassCastException iae) {
|
||||
// expected
|
||||
}
|
||||
root.set("@option2", Boolean.TRUE);
|
||||
try {
|
||||
root.set("@option2", "hello");
|
||||
fail("accepted a string value for a boolean setting");
|
||||
} catch (ClassCastException iae) {
|
||||
// expected
|
||||
}
|
||||
root.set("@option3", 20);
|
||||
try {
|
||||
root.set("@option3", Boolean.TRUE);
|
||||
fail("accepted a boolean value for a int setting");
|
||||
} catch (ClassCastException iae) {
|
||||
// expected
|
||||
}
|
||||
root.set("@option4", Thread.State.RUNNABLE);
|
||||
try {
|
||||
root.set("@option4", Boolean.TRUE);
|
||||
fail("accepted a boolean value for a Thread.State setting");
|
||||
} catch (ClassCastException iae) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void testSettingFromExternal() {
|
||||
Settings root = new Settings(TestTypeCompatibility.class);
|
||||
|
||||
assertNull(root.get("@option2"));
|
||||
root.setFromExternal("@option2", Boolean.TRUE.toString());
|
||||
assertEquals(Boolean.TRUE, root.get("@option2"));
|
||||
|
||||
assertNull(root.get("@option3"));
|
||||
root.setFromExternal("@option3", "20");
|
||||
assertEquals(20, root.get("@option3"));
|
||||
|
||||
assertNull(root.get("@option4"));
|
||||
root.setFromExternal("@option4", Thread.State.RUNNABLE.toString());
|
||||
assertEquals(Thread.State.RUNNABLE, root.get("@option4"));
|
||||
}
|
||||
|
||||
public void testResettingFromExternal() {
|
||||
Settings root = new Settings(TestTypeCompatibility.class);
|
||||
|
||||
root.set("@option1", "Hello");
|
||||
root.set("@option2", Boolean.TRUE);
|
||||
root.set("@option3", 20);
|
||||
root.set("@option4", Thread.State.TERMINATED);
|
||||
|
||||
root.setFromExternal("@option1", "");
|
||||
root.setFromExternal("@option2", "");
|
||||
root.setFromExternal("@option3", "");
|
||||
root.setFromExternal("@option4", "");
|
||||
|
||||
assertNull(root.get("@option1"));
|
||||
assertNull(root.get("@option2"));
|
||||
assertNull(root.get("@option3"));
|
||||
assertNull(root.get("@option4"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
package org.umlgraph.settings;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
|
@ -42,6 +44,11 @@ import java.util.Map;
|
|||
*/
|
||||
public class Settings {
|
||||
|
||||
/*
|
||||
* The child setting nodes.
|
||||
*/
|
||||
private Map<String, Settings> children = new HashMap<String, Settings>();
|
||||
|
||||
/*
|
||||
* The setting definitions for this setting node hierarchy (only set for the
|
||||
* root node). The definitions only define the available options and the
|
||||
|
|
@ -49,18 +56,13 @@ public class Settings {
|
|||
*/
|
||||
private Class<? extends SettingDefinitions>[] definitions;
|
||||
|
||||
/*
|
||||
* The child setting nodes.
|
||||
*/
|
||||
private Map<String, Settings> children = new HashMap<String, Settings>();
|
||||
private Settings parent;
|
||||
|
||||
/*
|
||||
* The setting values defined for this setting node.
|
||||
*/
|
||||
private Map<String, Object> values = new HashMap<String, Object>();
|
||||
|
||||
private Settings parent;
|
||||
|
||||
/**
|
||||
* Constructs a root settings object.
|
||||
*
|
||||
|
|
@ -84,30 +86,6 @@ public class Settings {
|
|||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the setting node corresponding to the given path. If the node
|
||||
* does not exist, returns a new empty node.
|
||||
*
|
||||
* @param path
|
||||
* the setting node path
|
||||
* @return the setting node corresponding to the given path
|
||||
*/
|
||||
public Settings node(String[] path) {
|
||||
return createNode(path, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the setting node corresponding to the given path. If the node
|
||||
* does not exist, returns a new empty node.
|
||||
*
|
||||
* @param path
|
||||
* the setting node path
|
||||
* @return the setting node corresponding to the given path
|
||||
*/
|
||||
public Settings node(String path) {
|
||||
return node(new SettingKey(path).getNodePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a setting node under the given path, or returns the existing one.
|
||||
*/
|
||||
|
|
@ -123,17 +101,15 @@ public class Settings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given node exists.
|
||||
* Returns an existing node corresponding to the given path, or
|
||||
* <code>null</code> if none exists.
|
||||
*
|
||||
* @param path
|
||||
* the path of the node to look for
|
||||
* @return the node found, or <code>null</code>
|
||||
*/
|
||||
public boolean nodeExists(String[] path) {
|
||||
return existingNode(path) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given node exists.
|
||||
*/
|
||||
public boolean nodeExists(String path) {
|
||||
return nodeExists(new SettingKey(path).getNodePath());
|
||||
public Settings existingNode(String path) {
|
||||
return existingNode(new SettingKey(path).getNodePath());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -154,15 +130,24 @@ public class Settings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns an existing node corresponding to the given path, or
|
||||
* <code>null</code> if none exists.
|
||||
* Finds the field corresponding to the given option name.
|
||||
*
|
||||
* @param path
|
||||
* the path of the node to look for
|
||||
* @return the node found, or <code>null</code>
|
||||
* @param optionName
|
||||
* @return
|
||||
*/
|
||||
public Settings existingNode(String path) {
|
||||
return existingNode(new SettingKey(path).getNodePath());
|
||||
private Field findOptionField(String optionName) {
|
||||
if (parent != null)
|
||||
return parent.findOptionField(optionName);
|
||||
for (Class<? extends SettingDefinitions> definitionClass : definitions)
|
||||
try {
|
||||
Field field = definitionClass.getDeclaredField(optionName);
|
||||
if (!field.getType().isPrimitive()
|
||||
&& Modifier.isStatic(field.getModifiers()))
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
// keep looking...
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -190,32 +175,33 @@ public class Settings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the option with the given name. Falls back to the
|
||||
* default value, if one is defined.
|
||||
* Returns the value of the option corresponding to the given path (falls
|
||||
* back to a default value, if one is defined). This is just a convenience
|
||||
* method for {@link #get(SettingKey, boolean)}.
|
||||
*
|
||||
* @param optionName
|
||||
* the option to search a value for
|
||||
* @return the value for the given option, or <code>null</code>
|
||||
* @param path
|
||||
* the option path
|
||||
* @return the value for the given path, or <code>null</code>
|
||||
*/
|
||||
private <T> T searchValue(String optionName) {
|
||||
T ownValue = (T) values.get(optionName);
|
||||
if (ownValue != null)
|
||||
return ownValue;
|
||||
return (T) (parent == null ? getDefaultValue(optionName) : parent
|
||||
.searchValue(optionName));
|
||||
public <T> T get(String path) {
|
||||
return get(new SettingKey(path), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given option name is defined for this setting node
|
||||
* hierarchy.
|
||||
* Returns the value of the option corresponding to the given path. If
|
||||
* acceptDefault is <code>true</code> and a value for the option does not
|
||||
* exist, returns the default value. Otherwise, returns <code>null</code>.
|
||||
* This is just a convenience method for {@link #get(SettingKey, boolean)}.
|
||||
*
|
||||
* @param optionName
|
||||
* the option name
|
||||
* @return <code>true</code> if an option with the given name is defined,
|
||||
* <code>false</code> otherwise
|
||||
* @param path
|
||||
* the option path
|
||||
* @param acceptDefault
|
||||
* whether a default value should be returned
|
||||
* @return the value for the given path, or <code>null</code>
|
||||
*/
|
||||
private boolean isDefined(String optionName) {
|
||||
return findOptionField(optionName) != null;
|
||||
|
||||
public <T> T get(String path, boolean acceptDefault) {
|
||||
return get(new SettingKey(path), acceptDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -238,24 +224,26 @@ public class Settings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Finds the field corresponding to the given option name.
|
||||
* Returns the parent settings, or <code>null</code> if this is a root
|
||||
* setting.
|
||||
*
|
||||
* @return the parent settings, or <code>null</code>
|
||||
*/
|
||||
public Settings getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given option name is defined for this setting node
|
||||
* hierarchy.
|
||||
*
|
||||
* @param optionName
|
||||
* @return
|
||||
* the option name
|
||||
* @return <code>true</code> if an option with the given name is defined,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private Field findOptionField(String optionName) {
|
||||
if (parent != null)
|
||||
return parent.findOptionField(optionName);
|
||||
for (Class<? extends SettingDefinitions> definitionClass : definitions)
|
||||
try {
|
||||
Field field = definitionClass.getDeclaredField(optionName);
|
||||
if (!field.getType().isPrimitive()
|
||||
&& Modifier.isStatic(field.getModifiers()))
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
// keep looking...
|
||||
}
|
||||
return null;
|
||||
private boolean isDefined(String optionName) {
|
||||
return findOptionField(optionName) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -276,33 +264,57 @@ public class Settings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the option corresponding to the given path. If
|
||||
* acceptDefault is <code>true</code> and a value for the option does not
|
||||
* exist, returns the default value. Otherwise, returns <code>null</code>.
|
||||
* This is just a convenience method for {@link #get(SettingKey, boolean)}.
|
||||
* Returns the setting node corresponding to the given path. If the node
|
||||
* does not exist, returns a new empty node.
|
||||
*
|
||||
* @param path
|
||||
* the option path
|
||||
* @param acceptDefault
|
||||
* whether a default value should be returned
|
||||
* @return the value for the given path, or <code>null</code>
|
||||
* the setting node path
|
||||
* @return the setting node corresponding to the given path
|
||||
*/
|
||||
|
||||
public <T> T get(String path, boolean acceptDefault) {
|
||||
return get(new SettingKey(path), acceptDefault);
|
||||
public Settings node(String path) {
|
||||
return node(new SettingKey(path).getNodePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the option corresponding to the given path (falls
|
||||
* back to a default value, if one is defined). This is just a convenience
|
||||
* method for {@link #get(SettingKey, boolean)}.
|
||||
* Returns the setting node corresponding to the given path. If the node
|
||||
* does not exist, returns a new empty node.
|
||||
*
|
||||
* @param path
|
||||
* the option path
|
||||
* @return the value for the given path, or <code>null</code>
|
||||
* the setting node path
|
||||
* @return the setting node corresponding to the given path
|
||||
*/
|
||||
public <T> T get(String path) {
|
||||
return get(new SettingKey(path), true);
|
||||
public Settings node(String[] path) {
|
||||
return createNode(path, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given node exists.
|
||||
*/
|
||||
public boolean nodeExists(String path) {
|
||||
return nodeExists(new SettingKey(path).getNodePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given node exists.
|
||||
*/
|
||||
public boolean nodeExists(String[] path) {
|
||||
return existingNode(path) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the option with the given name. Falls back to the
|
||||
* default value, if one is defined.
|
||||
*
|
||||
* @param optionName
|
||||
* the option to search a value for
|
||||
* @return the value for the given option, or <code>null</code>
|
||||
*/
|
||||
private <T> T searchValue(String optionName) {
|
||||
T ownValue = (T) values.get(optionName);
|
||||
if (ownValue != null)
|
||||
return ownValue;
|
||||
return (T) (parent == null ? getDefaultValue(optionName) : parent
|
||||
.searchValue(optionName));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -342,13 +354,75 @@ public class Settings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the given option to the default value.
|
||||
* Sets the value of the given option (or clears it) using an external
|
||||
* string representation. To accept setting using an external representation,
|
||||
* the setting type must be String, or any type that has a static valueOf(String)
|
||||
* factory method (includes all wrappers and enumerations).
|
||||
*
|
||||
* An empty or blank string as external value corresponds to resetting the value of the
|
||||
* setting.
|
||||
*
|
||||
* @param path
|
||||
* the option key
|
||||
* @param externalValue
|
||||
* the new value, possibly an empty string, never
|
||||
* <code>null</code>
|
||||
*/
|
||||
public void setFromExternal(SettingKey key, String externalValue) {
|
||||
assert externalValue != null;
|
||||
Field field = findOptionField(key.getOptionName());
|
||||
if (field == null)
|
||||
throw new IllegalArgumentException("unknown setting: " + key);
|
||||
externalValue = externalValue.trim();
|
||||
if (externalValue.length() == 0) {
|
||||
// exceptional case - empty external value means resetting the
|
||||
// setting
|
||||
set(key, null);
|
||||
return;
|
||||
}
|
||||
if (field.getType() == String.class) {
|
||||
// exceptional case - for string settings, external value is taken
|
||||
// as is
|
||||
set(key, externalValue);
|
||||
return;
|
||||
}
|
||||
Method valueMethod;
|
||||
try {
|
||||
valueMethod = field.getType().getDeclaredMethod("valueOf",
|
||||
String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new UnsupportedOperationException(field.getType().getName()
|
||||
+ " does not support conversion from string");
|
||||
}
|
||||
try {
|
||||
set(key, valueMethod.invoke(null, externalValue));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new UnsupportedOperationException(
|
||||
field.getType().getName()
|
||||
+ " does not provide a public conversion method from string");
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new UnsupportedOperationException(field.getType().getName()
|
||||
+ " does not accept the given value: " + externalValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the given option (or clears it) using an external
|
||||
* string representation. To accept setting using an external representation,
|
||||
* the setting type must be String, or any type that has a static valueOf(String)
|
||||
* factory method (includes all wrappers and enumerations).
|
||||
*
|
||||
* An empty or blank string as external value corresponds to resetting the value of the
|
||||
* setting.
|
||||
*
|
||||
* @param path
|
||||
* the option path
|
||||
* @param value
|
||||
* the new value, possibly an empty string, never
|
||||
* <code>null</code>
|
||||
*/
|
||||
public <T> void setToDefault(String path) {
|
||||
setToDefault(new SettingKey(path));
|
||||
public void setFromExternal(String path, String value) {
|
||||
setFromExternal(new SettingKey(path), value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -365,12 +439,12 @@ public class Settings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the parent settings, or <code>null</code> if this is a root
|
||||
* setting.
|
||||
* Sets the given option to the default value.
|
||||
*
|
||||
* @return the parent settings, or <code>null</code>
|
||||
* @param path
|
||||
* the option path
|
||||
*/
|
||||
public Settings getParent() {
|
||||
return parent;
|
||||
public <T> void setToDefault(String path) {
|
||||
setToDefault(new SettingKey(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue