--- /dev/null
+package org.simantics.scenegraph.utils;\r
+\r
+import java.util.Collection;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.UUID;\r
+\r
+import org.simantics.scenegraph.ILookupService;\r
+import org.simantics.scenegraph.INode;\r
+\r
+/**\r
+ * A scene graph utility that manages a set of String ID <-> {@link INode}\r
+ * mappings by ensuring that a specified set of nodes have ID mappings.\r
+ * \r
+ * When a node mapper is no longer needed, you should perform cleanup by\r
+ * invoking {@link #clear()}. This will remove all the ID <-> INode mappings\r
+ * managed by the mapper.\r
+ * \r
+ * The implementation is not thread-safe, it should only be used from the canvas\r
+ * context thread (see {@link ICanvasContext#getThreadAccess()).\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ */\r
+public final class NodeMapper {\r
+\r
+ Map<INode, String> nodes = new HashMap<INode, String>();\r
+ Map<INode, String> managedNodes = new HashMap<INode, String>();\r
+\r
+ public NodeMapper() {\r
+ }\r
+\r
+ public NodeMapper(INode node) {\r
+ add0(node);\r
+ }\r
+\r
+ public void clear() {\r
+ for (Map.Entry<INode, String> entry : managedNodes.entrySet()) {\r
+ ILookupService lu = NodeUtil.tryGetLookupService(entry.getKey());\r
+ if (lu != null) {\r
+ String mappedId = lu.lookupId(entry.getKey());\r
+ if (entry.getValue().equals(mappedId)) {\r
+ lu.unmap(entry.getKey());\r
+ }\r
+ }\r
+ }\r
+ managedNodes.clear();\r
+ nodes.clear();\r
+ }\r
+\r
+ public NodeMapper(Collection<? extends INode> nodes) {\r
+ addAll(nodes);\r
+ }\r
+\r
+ public String add(INode node) {\r
+ return add0(node);\r
+ }\r
+\r
+ public void addAll(Collection<? extends INode> nodes) {\r
+ for (INode n : nodes) {\r
+ add0(n);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param node\r
+ * @return\r
+ */\r
+ public String remove(INode node) {\r
+ return remove0(node);\r
+ }\r
+\r
+ public String getId(INode node) {\r
+ return nodes.get(node);\r
+ }\r
+\r
+ private String add0(INode node) {\r
+ String id = nodes.get(node);\r
+ if (id != null)\r
+ return id;\r
+\r
+ id = NodeUtil.lookupId(node);\r
+ if (id != null) {\r
+ nodes.put(node, id);\r
+ return id;\r
+ }\r
+\r
+ id = UUID.randomUUID().toString();\r
+ NodeUtil.map(node, id);\r
+ nodes.put(node, id);\r
+ managedNodes.put(node, id);\r
+ return id;\r
+ }\r
+\r
+ /**\r
+ * Removes mapping for specified node if and only if it is mapped through\r
+ * <em>this</em> mapper and the ID currently mapped to matches this mapper's\r
+ * conception of what the ID should be.\r
+ * \r
+ * @param node\r
+ * @return\r
+ */\r
+ private String remove0(INode node) {\r
+ String id = null;\r
+ if ((id = nodes.remove(node)) != null) {\r
+ if (managedNodes.remove(node) != null) {\r
+ ILookupService lu = NodeUtil.tryGetLookupService(node);\r
+ if (lu != null) {\r
+ String mappedId = lu.lookupId(node);\r
+ if (id.equals(mappedId)) {\r
+ lu.unmap(node);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return id;\r
+ }\r
+\r
+}\r