From 68f112c4ac0c510da868b3e22bdfc4c74bb48b86 Mon Sep 17 00:00:00 2001 From: Rafael Chaves <> Date: Sat, 29 Dec 2007 08:54:03 +0000 Subject: [PATCH] added settings project --- umlgraph_ng/org.umlgraph.settings/.classpath | 7 + umlgraph_ng/org.umlgraph.settings/.project | 28 ++ .../META-INF/MANIFEST.MF | 8 + .../org.umlgraph.settings/build.properties | 4 + .../umlgraph/settings/SettingDefinitions.java | 14 + .../src/org/umlgraph/settings/SettingKey.java | 103 +++++++ .../src/org/umlgraph/settings/Settings.java | 264 ++++++++++++++++++ 7 files changed, 428 insertions(+) create mode 100644 umlgraph_ng/org.umlgraph.settings/.classpath create mode 100644 umlgraph_ng/org.umlgraph.settings/.project create mode 100644 umlgraph_ng/org.umlgraph.settings/META-INF/MANIFEST.MF create mode 100644 umlgraph_ng/org.umlgraph.settings/build.properties create mode 100644 umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/SettingDefinitions.java create mode 100644 umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/SettingKey.java create mode 100644 umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/Settings.java diff --git a/umlgraph_ng/org.umlgraph.settings/.classpath b/umlgraph_ng/org.umlgraph.settings/.classpath new file mode 100644 index 0000000..751c8f2 --- /dev/null +++ b/umlgraph_ng/org.umlgraph.settings/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/umlgraph_ng/org.umlgraph.settings/.project b/umlgraph_ng/org.umlgraph.settings/.project new file mode 100644 index 0000000..2cfb3d0 --- /dev/null +++ b/umlgraph_ng/org.umlgraph.settings/.project @@ -0,0 +1,28 @@ + + + org.umlgraph.settings + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/umlgraph_ng/org.umlgraph.settings/META-INF/MANIFEST.MF b/umlgraph_ng/org.umlgraph.settings/META-INF/MANIFEST.MF new file mode 100644 index 0000000..fad2443 --- /dev/null +++ b/umlgraph_ng/org.umlgraph.settings/META-INF/MANIFEST.MF @@ -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 diff --git a/umlgraph_ng/org.umlgraph.settings/build.properties b/umlgraph_ng/org.umlgraph.settings/build.properties new file mode 100644 index 0000000..34d2e4d --- /dev/null +++ b/umlgraph_ng/org.umlgraph.settings/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/SettingDefinitions.java b/umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/SettingDefinitions.java new file mode 100644 index 0000000..8c23c00 --- /dev/null +++ b/umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/SettingDefinitions.java @@ -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 { + +} diff --git a/umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/SettingKey.java b/umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/SettingKey.java new file mode 100644 index 0000000..398976a --- /dev/null +++ b/umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/SettingKey.java @@ -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(); + } +} \ No newline at end of file diff --git a/umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/Settings.java b/umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/Settings.java new file mode 100644 index 0000000..d32a3d7 --- /dev/null +++ b/umlgraph_ng/org.umlgraph.settings/src/org/umlgraph/settings/Settings.java @@ -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: + * + * Non-features: + * + */ +public class Settings { + + private Class[] definitions; + + /* + * The child setting nodes. + */ + private Map children = new HashMap(); + /* + * The setting values defined for this setting node. + */ + private Map values = new HashMap(); + + private Settings parent; + + /** + * Constructs a root settings object. + * + * @param definitions + * the setting definition classes + */ + public Settings(Class... 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 null + */ + 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 true and a value for the option does not + * exist, returns the default value. Otherwise, returns null. + * + * @param key + * the option key + * @param acceptDefault + * whether a default value should be returned + * @return the value for the given key, or null + */ + public 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 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 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 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 true and a value for the option does not + * exist, returns the default value. Otherwise, returns null. + * 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 null + */ + + public T get(String path, boolean acceptDefault) { + return get(new SettingKey(path), acceptDefault); + } + + public 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 null + */ + public 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 void set(String path, T value) { + set(new SettingKey(path), value); + } + + public 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 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 null if this is a root + * setting. + * + * @return the parent settings, or null + */ + public Settings getParent() { + return parent; + } + + public static void registerContext( + Class contextType) { + // TODO Auto-generated method stub + } +}