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:
+ *
+ * - default values
+ * - contexts are hierarchic, option value lookup falls back to parent if
+ * option is not found at the current level
+ * - options are typed, so an attempt at setting them with the wrong value
+ * type causes an exception
+ * - 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.
+ *
+ * Non-features:
+ *
+ */
+public class Settings {
+
+ private Class extends SettingDefinitions>[] 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 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 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 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 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 extends SettingDefinitions> contextType) {
+ // TODO Auto-generated method stub
+ }
+}