mirror of https://github.com/dspinellis/UMLGraph
added settings project
This commit is contained in:
parent
a133341bd7
commit
68f112c4ac
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.umlgraph.settings</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Settings
|
||||
Bundle-SymbolicName: org.umlgraph.settings
|
||||
Bundle-Version: 5.1.0.qualifier
|
||||
Bundle-Vendor: umlgraph.org
|
||||
Export-Package: org.umlgraph.settings
|
||||
Bundle-RequiredExecutionEnvironment: J2SE-1.5
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package org.umlgraph.settings;
|
||||
|
||||
|
||||
/**
|
||||
* A tag interface for classes defining settings.
|
||||
* Classes implementing tis interface are expected to
|
||||
* define possible settings as public fields. Names of fields
|
||||
* are expected to be unique across different definition classes.
|
||||
* In case a default value is desired, Java's field default value
|
||||
* mechanism can be used.
|
||||
*/
|
||||
public interface SettingDefinitions {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package org.umlgraph.settings;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* A key for accessing a specific setting.
|
||||
*
|
||||
*/
|
||||
public class SettingKey {
|
||||
private static final String PATH_DELIMITER = "/";
|
||||
private static final String OPTION_DELIMITER = "@";
|
||||
private static final String[] EMPTY_CONTEXT_PATH = {};
|
||||
|
||||
private String[] nodePath;
|
||||
private String key;
|
||||
|
||||
public SettingKey(String[] nodePath, String key) {
|
||||
this.nodePath = nodePath;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public SettingKey(String stringKey) {
|
||||
int atIndex = stringKey.indexOf(OPTION_DELIMITER);
|
||||
if (atIndex >= 0) {
|
||||
if (atIndex == stringKey.length() - 1)
|
||||
throw new IllegalArgumentException("argument: " + stringKey);
|
||||
key = stringKey.substring(atIndex + 1);
|
||||
if (atIndex == 0) {
|
||||
nodePath = EMPTY_CONTEXT_PATH;
|
||||
return;
|
||||
}
|
||||
}
|
||||
String nodePathStr = atIndex < 0 ? stringKey : stringKey.substring(0,
|
||||
atIndex);
|
||||
StringTokenizer pathSegmenter = new StringTokenizer(nodePathStr,
|
||||
PATH_DELIMITER);
|
||||
nodePath = new String[pathSegmenter.countTokens()];
|
||||
int i = 0;
|
||||
while (pathSegmenter.hasMoreTokens())
|
||||
nodePath[i++] = pathSegmenter.nextToken();
|
||||
}
|
||||
|
||||
public String[] getNodePath() {
|
||||
return nodePath;
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return nodePath.length == 0;
|
||||
}
|
||||
|
||||
public boolean hasKey() {
|
||||
return key != null;
|
||||
}
|
||||
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((key == null) ? 0 : key.hashCode());
|
||||
result = prime * result + Arrays.hashCode(nodePath);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
final SettingKey other = (SettingKey) obj;
|
||||
if (key == null) {
|
||||
if (other.key != null)
|
||||
return false;
|
||||
} else if (!key.equals(other.key))
|
||||
return false;
|
||||
if (!Arrays.equals(nodePath, other.nodePath))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer result = new StringBuffer(PATH_DELIMITER);
|
||||
for (int i = 0; i < nodePath.length; i++) {
|
||||
result.append(nodePath[i]);
|
||||
result.append(PATH_DELIMITER);
|
||||
}
|
||||
if (hasKey()) {
|
||||
result.append(OPTION_DELIMITER);
|
||||
result.append(key);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
package org.umlgraph.settings;
|
||||
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A typed hierarchic preference mechanism that takes advantage of generics for
|
||||
* providing a simpler protocol.
|
||||
*
|
||||
* Features:
|
||||
* <ul>
|
||||
* <li>default values</li>
|
||||
* <li>contexts are hierarchic, option value lookup falls back to parent if
|
||||
* option is not found at the current level</li>
|
||||
* <li> options are typed, so an attempt at setting them with the wrong value
|
||||
* type causes an exception </li>
|
||||
* <li> option contexts are statically typed, meaning that set of available
|
||||
* options for a context is defined at compile time and trying to set an
|
||||
* unexpected option value causes an exception. </li>
|
||||
* </ul>
|
||||
* Non-features:
|
||||
* <ul>
|
||||
* <li> persistence </li>
|
||||
* </ul>
|
||||
*/
|
||||
public class Settings {
|
||||
|
||||
private Class<? extends SettingDefinitions>[] definitions;
|
||||
|
||||
/*
|
||||
* The child setting nodes.
|
||||
*/
|
||||
private Map<String, Settings> children = new HashMap<String, Settings>();
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* @param definitions
|
||||
* the setting definition classes
|
||||
*/
|
||||
public Settings(Class<? extends SettingDefinitions>... definitions) {
|
||||
if (definitions.length == 0)
|
||||
throw new IllegalArgumentException("no definitions provided");
|
||||
this.definitions = definitions;
|
||||
}
|
||||
|
||||
private Settings(Settings parent) {
|
||||
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);
|
||||
}
|
||||
|
||||
public Settings node(String path) {
|
||||
return node(new SettingKey(path).getNodePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a setting node under the given path, or returns the existing one.
|
||||
*/
|
||||
private Settings createNode(String[] path, int start) {
|
||||
if (path.length == start)
|
||||
return this;
|
||||
Settings found = children.get(path[start]);
|
||||
if (found == null)
|
||||
children.put(path[start], found = new Settings(this));
|
||||
if (start == path.length - 1)
|
||||
return found;
|
||||
return found.createNode(path, start + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given node exists.
|
||||
*/
|
||||
public boolean nodeExists(String[] path) {
|
||||
return existingNode(path) != null;
|
||||
}
|
||||
|
||||
public boolean nodeExists(String path) {
|
||||
return nodeExists(new SettingKey(path).getNodePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an existing setting node corresponding to the given path.
|
||||
*
|
||||
* @param path
|
||||
* the setting path
|
||||
* @return the settings object, or <code>null</code>
|
||||
*/
|
||||
public Settings existingNode(String[] segments) {
|
||||
Settings current = this;
|
||||
for (String segment : segments) {
|
||||
current = current.children.get(segment);
|
||||
if (current == null)
|
||||
return null;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
public Settings existingNode(String path) {
|
||||
return existingNode(new SettingKey(path).getNodePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the option corresponding to the given key. If
|
||||
* acceptDefault is <code>true</code> and a value for the option does not
|
||||
* exist, returns the default value. Otherwise, returns <code>null</code>.
|
||||
*
|
||||
* @param key
|
||||
* the option key
|
||||
* @param acceptDefault
|
||||
* whether a default value should be returned
|
||||
* @return the value for the given key, or <code>null</code>
|
||||
*/
|
||||
public <T> T get(SettingKey key, boolean acceptDefault) {
|
||||
if (!acceptDefault) {
|
||||
Settings node = existingNode(key.getNodePath());
|
||||
return node == null ? null : (T) node.values.get(key.getKey());
|
||||
}
|
||||
Settings node = lastExistingNode(key.getNodePath(), 0);
|
||||
return node.searchValue(key.getKey());
|
||||
}
|
||||
|
||||
private <T> T searchValue(String key) {
|
||||
if (!isDefined(key))
|
||||
throw new IllegalArgumentException("unknown setting: " + key);
|
||||
T ownValue = (T) values.get(key);
|
||||
if (ownValue != null)
|
||||
return ownValue;
|
||||
return (T) (parent == null ? getDefaultValue(key) : parent.searchValue(key));
|
||||
}
|
||||
|
||||
private boolean isDefined(String key) {
|
||||
return findField(key) != null;
|
||||
}
|
||||
|
||||
private <T> T getDefaultValue(String key) {
|
||||
Field field = findField(key);
|
||||
if (field == null)
|
||||
throw new IllegalArgumentException(
|
||||
"unknown setting or undefined default: " + key);
|
||||
try {
|
||||
return (T) field.get(null);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalArgumentException("unknown setting: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
private Field findField(String key) {
|
||||
if (parent != null)
|
||||
return parent.findField(key);
|
||||
for (Class<? extends SettingDefinitions> definitionClass : definitions)
|
||||
try {
|
||||
Field field = definitionClass.getDeclaredField(key);
|
||||
if (!field.getType().isPrimitive() && Modifier.isStatic(field.getModifiers()))
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
// keep looking...
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Settings lastExistingNode(String[] nodePath, int start) {
|
||||
if (start == nodePath.length)
|
||||
return this;
|
||||
Settings immediateChild = children.get(nodePath[start]);
|
||||
return immediateChild == null ? this : immediateChild.lastExistingNode(
|
||||
nodePath, start + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 path
|
||||
* the option path
|
||||
* @param acceptDefault
|
||||
* whether a default value should be returned
|
||||
* @return the value for the given path, or <code>null</code>
|
||||
*/
|
||||
|
||||
public <T> T get(String path, boolean acceptDefault) {
|
||||
return get(new SettingKey(path), acceptDefault);
|
||||
}
|
||||
|
||||
public <T> T get(String path) {
|
||||
return get(new SettingKey(path), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the given option.
|
||||
*
|
||||
* @param key
|
||||
* the option key
|
||||
* @param value
|
||||
* the new value, or <code>null</code>
|
||||
*/
|
||||
public <T> void set(SettingKey key, T value) {
|
||||
if (value == null) {
|
||||
Settings node = existingNode(key.getNodePath());
|
||||
if (node != null)
|
||||
node.values.remove(key.getKey());
|
||||
} else {
|
||||
Settings node = createNode(key.getNodePath(), 0);
|
||||
node.values.put(key.getKey(), value);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void set(String path, T value) {
|
||||
set(new SettingKey(path), value);
|
||||
}
|
||||
|
||||
public <T> void setToDefault(String path) {
|
||||
setToDefault(new SettingKey(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given option to its current default value.
|
||||
*
|
||||
* @param settingKey
|
||||
* the key to set to default
|
||||
*/
|
||||
public <T> void setToDefault(SettingKey key) {
|
||||
Object defaultValue = getDefaultValue(key.getKey());
|
||||
if (defaultValue == null)
|
||||
throw new IllegalStateException("no default value for " + key);
|
||||
set(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
public static void registerContext(
|
||||
Class<? extends SettingDefinitions> contextType) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue