-/*******************************************************************************\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
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
package org.simantics.scenegraph;
-import gnu.trove.map.hash.THashMap;\r
-\r
-import java.beans.PropertyChangeEvent;\r
-import java.beans.PropertyChangeListener;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.Map;\r
-
-/**\r
- * Base class of all scene graph nodes which can have a set of sub-nodes\r
- * (children). This class only provides support for unordered children.\r
- * \r
- * @param <T>\r
- */\r
-public abstract class ParentNode<T extends INode> extends Node {\r
-\r
- private static final long serialVersionUID = 8519410262849626534L;\r
-\r
- public static final String EXISTING = "#EXISTING#";\r
- public static final String UNLINK = "#UNLINK#";\r
- public static final String NULL = "#NULL#";\r
-\r
- protected static final String[] EMPTY_STRING_ARRAY = {};\r
-\r
- @SuppressWarnings("rawtypes")\r
- private static final Map DISPOSED_CHILDREN = Collections.emptyMap();\r
-\r
- /**\r
- * A value used for {@link #rootNodeCache} to indicate the node has been\r
- * disposed and the root node cache is to be considered indefinitely\r
- * invalid.\r
- */\r
- protected static final ParentNode<?> DISPOSED = new ParentNode<INode>() {\r
- private static final long serialVersionUID = 6155494069158034123L;\r
- @Override\r
- public void asyncRemoveNode(INode node) {\r
- throw new Error();\r
- }\r
- };\r
-\r
- protected transient Map<String, T> children = createChildMap();\r
-
- /**\r
- * A cached value for the root node of this parent node. This makes it\r
- * possible to optimize {@link #getRootNode()} so that not all invocations\r
- * go through the whole node hierarchy to find the root. This helps in\r
- * making {@link ILookupService} located in the root node perform better and\r
- * therefore be more useful.\r
- * \r
- * @see #DISPOSED\r
- */\r
- protected transient volatile ParentNode<?> rootNodeCache;\r
-\r
- protected Map<String, T> createChildMap() {\r
- return new THashMap<String, T>(1);\r
- }\r
- \r
+import gnu.trove.map.hash.THashMap;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Base class of all scene graph nodes which can have a set of sub-nodes
+ * (children). This class only provides support for unordered children.
+ *
+ * @param <T>
+ */
+public abstract class ParentNode<T extends INode> extends Node {
+
+ private static final long serialVersionUID = 8519410262849626534L;
+
+ public static final String EXISTING = "#EXISTING#";
+ public static final String UNLINK = "#UNLINK#";
+ public static final String NULL = "#NULL#";
+
+ protected static final String[] EMPTY_STRING_ARRAY = {};
+
+ @SuppressWarnings("rawtypes")
+ private static final Map DISPOSED_CHILDREN = Collections.emptyMap();
+
+ /**
+ * A value used for {@link #rootNodeCache} to indicate the node has been
+ * disposed and the root node cache is to be considered indefinitely
+ * invalid.
+ */
+ protected static final ParentNode<?> DISPOSED = new ParentNode<INode>() {
+ private static final long serialVersionUID = 6155494069158034123L;
+ @Override
+ public void asyncRemoveNode(INode node) {
+ throw new Error();
+ }
+ };
+
+ protected transient Map<String, T> children = createChildMap();
+
+ /**
+ * A cached value for the root node of this parent node. This makes it
+ * possible to optimize {@link #getRootNode()} so that not all invocations
+ * go through the whole node hierarchy to find the root. This helps in
+ * making {@link ILookupService} located in the root node perform better and
+ * therefore be more useful.
+ *
+ * @see #DISPOSED
+ */
+ protected transient volatile ParentNode<?> rootNodeCache;
+
+ protected Map<String, T> createChildMap() {
+ return new THashMap<String, T>(1);
+ }
+
public final <TC> TC addNode(Class<TC> a) {
return addNode(java.util.UUID.randomUUID().toString(), a);
}
-\r
- @SuppressWarnings("unchecked")\r
- public <TC extends INode> TC addNode(String id, TC child) {\r
-\r
- child.setParent(this);\r
- children.put(id, (T)child);\r
-\r
- child.init();\r
- childrenChanged();\r
- return (TC)child;\r
- \r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- public <TC extends INode> TC attachNode(String id, TC child) {\r
-\r
- child.setParent(this);\r
- children.put(id, (T)child);\r
-\r
- child.attach();\r
- childrenChanged();\r
- return (TC)child;\r
- \r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- public <TC extends INode> TC detachNode(String id) {\r
- T child = children.remove(id);\r
- if (child == null)\r
- return null;\r
- child.setParent(null);\r
- childrenChanged();\r
- return (TC) child;\r
- }\r
-\r
+
+ @SuppressWarnings("unchecked")
+ public <TC extends INode> TC addNode(String id, TC child) {
+
+ child.setParent(this);
+ children.put(id, (T)child);
+
+ child.init();
+ childrenChanged();
+ return (TC)child;
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public <TC extends INode> TC attachNode(String id, TC child) {
+
+ child.setParent(this);
+ children.put(id, (T)child);
+
+ child.attach();
+ childrenChanged();
+ return (TC)child;
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public <TC extends INode> TC detachNode(String id) {
+ T child = children.remove(id);
+ if (child == null)
+ return null;
+ child.setParent(null);
+ childrenChanged();
+ return (TC) child;
+ }
+
@SuppressWarnings("unchecked")
public <TC> TC addNode(String id, Class<TC> a) {
// a must be extended from Node
if(!Node.class.isAssignableFrom(a)) {
throw new IllegalArgumentException(a + " is not extended from org.simantics.scenegraph.Node");
}
- INode child = null;\r
+ INode child = null;
try {
child = (Node) a.newInstance();
} catch (InstantiationException e) {
childrenChanged();
return (TC)child;
}
-\r
- @SuppressWarnings("unchecked")\r
- public final <TC extends INode> TC getOrAttachNode(String id, TC a) {\r
- synchronized (children) {\r
- if (children.containsKey(id))\r
- return (TC) children.get(id);\r
- }\r
- return attachNode(id, a);\r
- }\r
+
+ @SuppressWarnings("unchecked")
+ public final <TC extends INode> TC getOrAttachNode(String id, TC a) {
+ synchronized (children) {
+ if (children.containsKey(id))
+ return (TC) children.get(id);
+ }
+ return attachNode(id, a);
+ }
@SuppressWarnings("unchecked")
public final <TC> TC getOrCreateNode(String id, Class<TC> a) {
- synchronized (children) {\r
- if (children.containsKey(id))\r
- return (TC) children.get(id);\r
- }\r
+ synchronized (children) {
+ if (children.containsKey(id))
+ return (TC) children.get(id);
+ }
return addNode(id, a);
}
- public final void removeNode(String id) {\r
- INode child = null;\r
+ public final void removeNode(String id) {
+ INode child = null;
synchronized (children) {
- child = children.remove(id);\r
+ child = children.remove(id);
}
- if (child != null) {\r
- if (child instanceof ParentNode<?>) {\r
- ((ParentNode<?>) child).removeNodes();\r
- }\r
+ if (child != null) {
+ if (child instanceof ParentNode<?>) {
+ ((ParentNode<?>) child).removeNodes();
+ }
child.cleanup();
- child.setParent(null);\r
+ child.setParent(null);
childrenChanged();
- if (propertyChangeListener != null) {\r
+ if (propertyChangeListener != null) {
propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), NULL)); // "children" is a special field name
}
}
}
}
if(id == null) return;
- children.remove(id);\r
+ children.remove(id);
childrenChanged();
- }\r
- if (child instanceof ParentNode<?>) {\r
- ((ParentNode<?>) child).removeNodes();\r
+ }
+ if (child instanceof ParentNode<?>) {
+ ((ParentNode<?>) child).removeNodes();
}
child.cleanup();
- child.setParent(null);\r
+ child.setParent(null);
if (propertyChangeListener != null) {
propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), NULL)); // "children" is a special field name
}
- }\r
-\r
- /**\r
- * This method removes and disposes all children of the node (and their\r
- * children).\r
- */\r
+ }
+
+ /**
+ * This method removes and disposes all children of the node (and their
+ * children).
+ */
public final void removeNodes() {
- synchronized (children) {\r
+ synchronized (children) {
boolean changed = false;
String[] keys = children.keySet().toArray(EMPTY_STRING_ARRAY);
- for (String key : keys) {\r
- INode child = children.remove(key);\r
- if (child != null) {\r
- changed = true;\r
- if (child instanceof ParentNode<?>) {\r
- ((ParentNode<?>) child).removeNodes();\r
+ for (String key : keys) {
+ INode child = children.remove(key);
+ if (child != null) {
+ changed = true;
+ if (child instanceof ParentNode<?>) {
+ ((ParentNode<?>) child).removeNodes();
}
child.cleanup();
- child.setParent(null);\r
- if (propertyChangeListener != null) {\r
+ child.setParent(null);
+ if (propertyChangeListener != null) {
propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), NULL)); // "children" is a special field name
}
}
}
}
- /**\r
- * Invoked every time the set of child changes for a {@link ParentNode}.\r
- * Extending implementations may override to perform their own actions, such\r
- * as invalidating possible caches.\r
- */\r
+ /**
+ * Invoked every time the set of child changes for a {@link ParentNode}.
+ * Extending implementations may override to perform their own actions, such
+ * as invalidating possible caches.
+ */
protected void childrenChanged() {
}
return children.get(id);
}
- /**\r
- * @return the IDs of the child nodes as a collection. Returns internal\r
- * state which must not be directly modified by client code!\r
- */\r
+ /**
+ * @return the IDs of the child nodes as a collection. Returns internal
+ * state which must not be directly modified by client code!
+ */
public Collection<String> getNodeIds() {
return children.keySet();
}
* @return the collection of this node's children in an unspecified order
*/
public Collection<T> getNodes() {
- return children.values();\r
+ return children.values();
}
public int getNodeCount() {
- return children.size();\r
+ return children.size();
}
/**
}
}
- @SuppressWarnings("unchecked")\r
+ @SuppressWarnings("unchecked")
@Override
public void cleanup() {
- retractMapping();\r
- if (children != DISPOSED_CHILDREN) { \r
+ retractMapping();
+ if (children != DISPOSED_CHILDREN) {
synchronized(children) {
for(T child : children.values()) {
- ((INode)child).cleanup();\r
+ ((INode)child).cleanup();
((INode)child).setParent(null);
}
- children.clear();\r
+ children.clear();
children = DISPOSED_CHILDREN;
- childrenChanged();\r
- }\r
- rootNodeCache = DISPOSED;\r
+ childrenChanged();
+ }
+ rootNodeCache = DISPOSED;
}
- }\r
-\r
+ }
+
@Override
- public void delete() {\r
- if(parent == null) {\r
- return;\r
- }\r
-\r
- synchronized (children) {\r
- // 1. Add children under parent\r
- parent.appendChildren(children);\r
-\r
- // 2. Clear children\r
- children.clear();\r
- }\r
-\r
- // 3. Remove this node from parent\r
- parent.unlinkChild(this);\r
-\r
- // 4. Cleanup, this node is now disposed\r
- cleanup();\r
- }\r
-\r
- /**\r
- * Helper method for delete()\r
- * @param children\r
- */\r
- @SuppressWarnings("unchecked")\r
- protected void appendChildren(Map<String, ?> children) {\r
- synchronized(this.children) {\r
- for(String id : children.keySet()) {\r
- INode child = (INode)children.get(id);\r
- this.children.put(id, (T)child); // Hopefully cast works\r
- child.setParent(this);\r
-\r
- // Send notify only if we are on server side (or standalone)\r
- if (propertyChangeListener != null && location.equals(Location.LOCAL)) {\r
- propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", null, EXISTING)); // "children" is a special field name\r
- }\r
- }\r
- }\r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- protected void appendChild(String id, INode child) {\r
- children.put(id, (T)child);\r
- child.setParent(this);\r
- // Send notify only if we are on server side (or standalone)\r
- if (propertyChangeListener != null && location.equals(Location.LOCAL)) {\r
- propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+(child).getId()+"]", null, EXISTING)); // "children" is a special field name\r
- }\r
- }\r
-\r
- /**\r
- * Same as removeNode, but does not perform cleanup for the child\r
- * @param child\r
- */\r
- protected void unlinkChild(INode child) {\r
- synchronized(children) {\r
- String id = null;\r
- // FIXME: damn slow, needs more data structure to be supported well\r
- // One option would be to store the id<->node mappings in a BidiMap.\r
- for(String tmp : children.keySet()) {\r
- if(children.get(tmp).equals(child)) {\r
- id = tmp;\r
- break;\r
- }\r
- }\r
- if(id == null) return;\r
- children.remove(id);\r
- childrenChanged();\r
- }\r
-\r
- if(propertyChangeListener != null && location.equals(Location.LOCAL)) {\r
- propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), UNLINK)); // "children" is a special field name\r
- }\r
- }\r
+ public void delete() {
+ if(parent == null) {
+ return;
+ }
+
+ synchronized (children) {
+ // 1. Add children under parent
+ parent.appendChildren(children);
+
+ // 2. Clear children
+ children.clear();
+ }
+
+ // 3. Remove this node from parent
+ parent.unlinkChild(this);
+
+ // 4. Cleanup, this node is now disposed
+ cleanup();
+ }
+
+ /**
+ * Helper method for delete()
+ * @param children
+ */
+ @SuppressWarnings("unchecked")
+ protected void appendChildren(Map<String, ?> children) {
+ synchronized(this.children) {
+ for(String id : children.keySet()) {
+ INode child = (INode)children.get(id);
+ this.children.put(id, (T)child); // Hopefully cast works
+ child.setParent(this);
+
+ // Send notify only if we are on server side (or standalone)
+ if (propertyChangeListener != null && location.equals(Location.LOCAL)) {
+ propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", null, EXISTING)); // "children" is a special field name
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void appendChild(String id, INode child) {
+ children.put(id, (T)child);
+ child.setParent(this);
+ // Send notify only if we are on server side (or standalone)
+ if (propertyChangeListener != null && location.equals(Location.LOCAL)) {
+ propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+(child).getId()+"]", null, EXISTING)); // "children" is a special field name
+ }
+ }
+
+ /**
+ * Same as removeNode, but does not perform cleanup for the child
+ * @param child
+ */
+ protected void unlinkChild(INode child) {
+ synchronized(children) {
+ String id = null;
+ // FIXME: damn slow, needs more data structure to be supported well
+ // One option would be to store the id<->node mappings in a BidiMap.
+ for(String tmp : children.keySet()) {
+ if(children.get(tmp).equals(child)) {
+ id = tmp;
+ break;
+ }
+ }
+ if(id == null) return;
+ children.remove(id);
+ childrenChanged();
+ }
+
+ if(propertyChangeListener != null && location.equals(Location.LOCAL)) {
+ propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), UNLINK)); // "children" is a special field name
+ }
+ }
@Override
public String toString() {
return super.toString() + " [#child=" + children.size() + "]";
}
- @Override\r
- public ParentNode<?> getRootNode() {\r
- // Note: using double-checked locking idiom with volatile keyword.\r
- // Works with Java 1.5+\r
- ParentNode<?> result = rootNodeCache;\r
- if (result == DISPOSED)\r
- return null;\r
-\r
- if (result == null) {\r
- synchronized (this) {\r
- result = rootNodeCache;\r
- if (result == null) {\r
- if (parent != null) {\r
- result = parent.getRootNode();\r
- rootNodeCache = result;\r
- }\r
- }\r
- }\r
- }\r
- return result;\r
- }\r
-\r
+ @Override
+ public ParentNode<?> getRootNode() {
+ // Note: using double-checked locking idiom with volatile keyword.
+ // Works with Java 1.5+
+ ParentNode<?> result = rootNodeCache;
+ if (result == DISPOSED)
+ return null;
+
+ if (result == null) {
+ synchronized (this) {
+ result = rootNodeCache;
+ if (result == null) {
+ if (parent != null) {
+ result = parent.getRootNode();
+ rootNodeCache = result;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
}