--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.browsing.ui.common;\r
+\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+import org.eclipse.core.runtime.IAdaptable;\r
+import org.simantics.browsing.ui.BuiltinKeys;\r
+import org.simantics.browsing.ui.NodeContext;\r
+import org.simantics.browsing.ui.NodeContext.ConstantKey;\r
+import org.simantics.browsing.ui.content.Viewpoint;\r
+import org.simantics.utils.datastructures.ArrayMap;\r
+\r
+/**\r
+ * Use this class in {@link Viewpoint}, and particularly\r
+ * {@link Viewpoint#getChildren()} implementations to construct new INodeContext\r
+ * instances.\r
+ * \r
+ * @author Antti Villberg\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public final class NodeContextBuilder {\r
+\r
+ private static Set<ConstantKey<?>> SINGLE_INPUT_KEYS =\r
+ Collections.<ConstantKey<?>> singleton(BuiltinKeys.INPUT);\r
+\r
+ MapNodeContext context;\r
+\r
+ private static class InputNodeContext implements NodeContext {\r
+\r
+ final Object input;\r
+\r
+ private InputNodeContext(Object input) {\r
+ this.input = input;\r
+ }\r
+\r
+ @SuppressWarnings({ "unchecked", "rawtypes" })\r
+ @Override\r
+ public Object getAdapter(Class adapter) {\r
+ if (NodeContext.class.equals(adapter))\r
+ return this;\r
+ if (input != null) {\r
+ if (adapter.isAssignableFrom(input.getClass()))\r
+ return input;\r
+ if (input instanceof IAdaptable)\r
+ return ((IAdaptable) input).getAdapter(adapter);\r
+ }\r
+ return null;\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T> T getConstant(ConstantKey<T> key) {\r
+ return key == BuiltinKeys.INPUT ? (T) input : null;\r
+ }\r
+\r
+ @Override\r
+ public Set<ConstantKey<?>> getKeys() {\r
+ return SINGLE_INPUT_KEYS;\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return ((input == null) ? 0 : input.hashCode());\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj)\r
+ return true;\r
+ if (obj == null)\r
+ return false;\r
+ if (getClass() != obj.getClass())\r
+ return false;\r
+ InputNodeContext other = (InputNodeContext) obj;\r
+ if (input == null) {\r
+ if (other.input != null)\r
+ return false;\r
+ } else if (!input.equals(other.input))\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "InputNodeContext(" + hashCode() + ") [" + input + "]";\r
+ }\r
+ }\r
+\r
+ public static class MapNodeContext implements NodeContext {\r
+\r
+ private Map<ConstantKey<?>, Object> data;\r
+ private int hash;\r
+\r
+ private MapNodeContext() {\r
+ }\r
+\r
+ void makeHash() {\r
+ this.hash = (data == null) ? 0 : data.hashCode();\r
+ }\r
+\r
+ @SuppressWarnings({ "unchecked", "rawtypes" })\r
+ @Override\r
+ public Object getAdapter(Class adapter) {\r
+ if (NodeContext.class.equals(adapter))\r
+ return this;\r
+ Object input = data.get(BuiltinKeys.INPUT);\r
+ if (input != null) {\r
+ if (adapter.isAssignableFrom(input.getClass()))\r
+ return input;\r
+ if (input instanceof IAdaptable)\r
+ return ((IAdaptable) input).getAdapter(adapter);\r
+ }\r
+ return null;\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T> T getConstant(ConstantKey<T> key) {\r
+ return data != null ? (T) data.get(key) : null;\r
+ }\r
+\r
+ @Override\r
+ public Set<ConstantKey<?>> getKeys() {\r
+ return data.keySet();\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return hash;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj)\r
+ return true;\r
+ if (obj == null)\r
+ return false;\r
+ if (getClass() != obj.getClass())\r
+ return false;\r
+ MapNodeContext other = (MapNodeContext) obj;\r
+ if (data == null) {\r
+ if (other.data != null)\r
+ return false;\r
+ } else if (!data.equals(other.data))\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "MapNodeContext(" + hashCode() + ") [" + (data != null ? data : "") + "]";\r
+ }\r
+ }\r
+\r
+ public NodeContextBuilder() {\r
+ this.context = new MapNodeContext();\r
+ }\r
+\r
+ public <T> NodeContextBuilder define(ConstantKey<T> key, T value) {\r
+ if (context.data == null)\r
+ context.data = new HashMap<ConstantKey<?>, Object>();\r
+ context.data.put(key, value);\r
+ return this;\r
+ }\r
+\r
+ public NodeContext createContext() {\r
+ context.makeHash();\r
+ return context;\r
+ }\r
+\r
+ /**\r
+ * A special case builder for an INodeContext that will only take one\r
+ * (key,value) pair and insert that into the built INodeContext.\r
+ * \r
+ * <p>\r
+ * This method works around the need to create NodeContextBuilder instances.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * Notice that your input key and value must properly implement\r
+ * <code>equals</code> and <code>hashCode</code> in order for\r
+ * GraphExplorer's automatic node expansion state tracking to work.\r
+ * </p>\r
+ * \r
+ * @param key\r
+ * @param value\r
+ * @return an INodeContext containing a single constant as specified by\r
+ * <code>key</code> and <code>value</code>\r
+ */\r
+ public static <T> NodeContext buildWithSingleData(ConstantKey<T> key, T value) {\r
+ MapNodeContext context = new MapNodeContext();\r
+ context.data = Collections.<ConstantKey<?>, Object>singletonMap(key, value);\r
+ context.makeHash();\r
+ return context;\r
+ }\r
+\r
+ /**\r
+ * A generic case builder for an INodeContext that will take as many\r
+ * (key,value) pairs as it is given and insert all of them into a map\r
+ * that is backing the built INodeContext implementation.\r
+ * \r
+ * <p>\r
+ * This method works around the need to create NodeContextBuilder instances.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * Notice that your input keys and values must properly implement\r
+ * <code>equals</code> and <code>hashCode</code> in order for\r
+ * GraphExplorer's automatic node expansion state tracking to work.\r
+ * </p>\r
+ * \r
+ * @param keyValuePairs\r
+ * @return an INodeContext containing the specified key,value pairs as its\r
+ * constants\r
+ */\r
+ public static <T> NodeContext buildWithData(Object... keyValuePairs) {\r
+ assert keyValuePairs.length % 2 == 0;\r
+ MapNodeContext context = new MapNodeContext();\r
+ int size = keyValuePairs.length;\r
+ Map<ConstantKey<?>, Object> map = new HashMap<ConstantKey<?>, Object>(size);\r
+\r
+ for (int i = 0; i < keyValuePairs.length; i += 2) {\r
+ map.put((ConstantKey<?>) keyValuePairs[i], keyValuePairs[i+1]);\r
+ }\r
+\r
+ context.data = map;\r
+ context.makeHash();\r
+ return context;\r
+ }\r
+\r
+ /**\r
+ * A generic case builder for an INodeContext that will take as many\r
+ * (key,value) pairs as it is given and insert all of them into a map that\r
+ * is backing the built INodeContext implementation. This method takes the\r
+ * keys and values separately, which allows the use of {@link ArrayMap} that\r
+ * provides a space-optimized Map implementation. Note that the key set of\r
+ * an {@link ArrayMap} is immutable.\r
+ * \r
+ * <p>\r
+ * This method works around the need to create NodeContextBuilder instances.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * Notice that your input keys and values must properly implement\r
+ * <code>equals</code> and <code>hashCode</code> in order for\r
+ * GraphExplorer's automatic node expansion state tracking to work.\r
+ * </p>\r
+ * \r
+ * @param keys the keys of the map, must equal values array in size\r
+ * @param values the values of the map, must equal keys array in size\r
+ * @return a <code>NodeContext</code> containing the specified key,value\r
+ * pairs as its constants\r
+ */\r
+ public static <T> NodeContext buildWithData(ConstantKey<?>[] keys, Object[] values) {\r
+ assert keys.length == values.length;\r
+ MapNodeContext context = new MapNodeContext();\r
+ Map<ConstantKey<?>, Object> map = new ArrayMap<ConstantKey<?>, Object>(keys, values);\r
+ context.data = map;\r
+ context.makeHash();\r
+ return context;\r
+ }\r
+\r
+ /**\r
+ * A special case builder for an INodeContext that will only take one\r
+ * (key,value) pair and insert that into the built INodeContext.\r
+ * \r
+ * <p>\r
+ * This method works around the need to create NodeContextBuilder instances.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * Notice that your input object must properly implement <code>equals</code>\r
+ * and <code>hashCode</code> in order for GraphExplorer's automatic node\r
+ * expansion state tracking to work.\r
+ * </p>\r
+ * \r
+ * @param input\r
+ * @return an INodeContext containing the specified object as with the key\r
+ * {@link BuiltinKeys#INPUT}\r
+ */\r
+ public static NodeContext buildWithInput(Object input) {\r
+ return new InputNodeContext(input);\r
+ }\r
+\r
+}\r