import org.simantics.scl.runtime.function.Function1;
import org.simantics.scl.runtime.function.Function2;
-abstract public class WikiDocumentNodeImpl extends ParentNode<WikiDocumentNode> implements WikiDocumentNode {
+public abstract class WikiDocumentNodeImpl extends ParentNode<WikiDocumentNode> implements WikiDocumentNode {
public Boolean printInPDF = false;
public String editText = null;
private static final long serialVersionUID = 3394059912639648935L;
- class M implements Map<String, WikiDocumentNode> {
-
- ArrayList<WikiDocumentNode> list = new ArrayList<WikiDocumentNode>();
- Hashtable<String, WikiDocumentNode> table = new Hashtable<String, WikiDocumentNode>();
-
+ static class M implements Map<String, INode> {
+
+ ArrayList<INode> list = new ArrayList<>();
+ Hashtable<String, INode> table = new Hashtable<>();
+
@Override
public void clear() {
table.clear();
return table.containsValue(arg0);
}
@Override
- public Set<java.util.Map.Entry<String, WikiDocumentNode>> entrySet() {
+ public Set<java.util.Map.Entry<String, INode>> entrySet() {
return table.entrySet();
}
@Override
- public WikiDocumentNode get(Object arg0) {
+ public INode get(Object arg0) {
return table.get(arg0);
}
@Override
return table.keySet();
}
@Override
- public WikiDocumentNode put(String arg0, WikiDocumentNode arg1) {
- WikiDocumentNode exist = table.put(arg0, arg1);
+ public INode put(String arg0, INode arg1) {
+ INode exist = table.put(arg0, arg1);
if(exist != null) list.remove(exist);
list.add(arg1);
return exist;
}
@Override
- public void putAll(Map<? extends String, ? extends WikiDocumentNode> arg0) {
- for(Map.Entry<? extends String, ? extends WikiDocumentNode> entry : arg0.entrySet())
+ public void putAll(Map<? extends String, ? extends INode> arg0) {
+ for(Map.Entry<? extends String, ? extends INode> entry : arg0.entrySet())
put(entry.getKey(), entry.getValue());
}
@Override
- public WikiDocumentNode remove(Object arg0) {
- WikiDocumentNode node = table.remove(arg0);
+ public INode remove(Object arg0) {
+ INode node = table.remove(arg0);
if(node != null) list.remove(node);
return node;
}
return table.size();
}
@Override
- public Collection<WikiDocumentNode> values() {
+ public Collection<INode> values() {
return list;
}
-
+
}
-
+
@Override
final public void asyncRemoveNode(INode node) {
throw new Error();
}
@Override
- protected Map<String, WikiDocumentNode> createChildMap() {
+ protected Map<String, INode> createChildMap(int initialCapacity) {
return new M();
}
-
+
@Override
public Function1<Object, Boolean> getPropertyFunction(String propertyName) {
return ScenegraphUtils.getMethodPropertyFunction(null, this, propertyName);
}
protected void createChildren(StringBuilder b, boolean isPDF) {
- for(WikiDocumentNode node : children.values()) node.create(b, isPDF);
+ for(WikiDocumentNode node : getNodes()) node.create(b, isPDF);
}
public void synchronizePrintInPDF(Boolean value) {
*******************************************************************************/
package org.simantics.scenegraph;
-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.HashMap;
import java.util.Map;
+import gnu.trove.map.hash.TLongObjectHashMap;
+
/**
* Base class of all scene graph nodes which can have a set of sub-nodes
* (children). This class only provides support for unordered children.
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
}
};
- protected transient Map<String, T> children = createChildMap();
+ protected transient Map<String, INode> children = createChildMap();
+ private transient TLongObjectHashMap<String> childrenIdMap = new TLongObjectHashMap<>();
/**
* A cached value for the root node of this parent node. This makes it
*/
protected transient volatile ParentNode<?> rootNodeCache;
- protected Map<String, T> createChildMap() {
- return new THashMap<String, T>(1);
+ protected Map<String, INode> createChildMap() {
+ return createChildMap(1);
}
-
+
+ protected Map<String, INode> createChildMap(int initialCapacity) {
+ // With JDK 1.8 HashMap is faster than Trove
+ return new HashMap<>(initialCapacity);
+ }
+
public final <TC> TC addNode(Class<TC> a) {
return addNode(java.util.UUID.randomUUID().toString(), a);
}
- @SuppressWarnings("unchecked")
public <TC extends INode> TC addNode(String id, TC child) {
+ return addNodeInternal(id, child, true, true);
+ }
+ private <TC extends INode> TC addNodeInternal(String id, TC child, boolean init, boolean addToChildren) {
child.setParent(this);
- children.put(id, (T)child);
+ if (addToChildren)
+ children.put(id, child);
+
+ childrenIdMap.put(child.getId(), id);
- child.init();
+ if (init)
+ 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;
-
+ return addNodeInternal(id, child, false, true);
}
@SuppressWarnings("unchecked")
public <TC extends INode> TC detachNode(String id) {
- T child = children.remove(id);
+ INode child = children.remove(id);
if (child == null)
return null;
+ childrenIdMap.remove(child.getId());
child.setParent(null);
childrenChanged();
return (TC) child;
}
- @SuppressWarnings("unchecked")
public <TC> TC addNode(String id, Class<TC> a) {
+ return addNodeInternal0(id, a, true);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <TC> TC addNodeInternal0(String id, Class<TC> a, boolean addToChildren) {
// a must be extended from Node
if(!Node.class.isAssignableFrom(a)) {
throw new IllegalArgumentException(a + " is not extended from org.simantics.scenegraph.Node");
} catch (IllegalAccessException e) {
throw new NodeException("Node " + Node.getSimpleClassName(a) + " instantiation failed, see exception for details.", e);
}
-
- child.setParent(this);
- children.put(id, (T)child);
-
- child.init();
- childrenChanged();
- return (TC)child;
+ return (TC) addNodeInternal(id, child, true, addToChildren);
}
@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);
+ return (TC) children.computeIfAbsent(id, key -> {
+ return (T) addNodeInternal(id, a, false, false);
+ });
}
@SuppressWarnings("unchecked")
public final <TC> TC getOrCreateNode(String id, Class<TC> a) {
- synchronized (children) {
- if (children.containsKey(id))
- return (TC) children.get(id);
- }
- return addNode(id, a);
+ return (TC) children.computeIfAbsent(id, key -> {
+ return (T) addNodeInternal0(id, a, false);
+ });
}
public final void removeNode(String id) {
- INode child = null;
- synchronized (children) {
- child = children.remove(id);
- }
- if (child != null) {
- if (child instanceof ParentNode<?>) {
- ((ParentNode<?>) child).removeNodes();
- }
- child.cleanup();
- child.setParent(null);
- childrenChanged();
- if (propertyChangeListener != null) {
- propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), NULL)); // "children" is a special field name
- }
- }
+ INode child = children.remove(id);
+ if (child != null)
+ removeNodeInternal(child, true);
}
public final void removeNode(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 (child instanceof ParentNode<?>) {
- ((ParentNode<?>) child).removeNodes();
- }
- child.cleanup();
- child.setParent(null);
-
- if (propertyChangeListener != null) {
- propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), NULL)); // "children" is a special field name
- }
+ String key = childrenIdMap.get(child.getId());
+ removeNode(key);
}
/**
* children).
*/
public final void removeNodes() {
- synchronized (children) {
- boolean changed = false;
- String[] keys = children.keySet().toArray(EMPTY_STRING_ARRAY);
- 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);
- if (propertyChangeListener != null) {
- propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), NULL)); // "children" is a special field name
- }
- }
+ boolean changed = children.size() > 0;
+ children.forEach((id, child) -> removeNodeInternal(child, false));
+ children.clear();
+ childrenIdMap.clear();
+
+ if (changed)
+ childrenChanged();
+ }
+
+ private void removeNodeInternal(INode child, boolean triggerChildrenChanged) {
+ if (child != null) {
+ if (child instanceof ParentNode<?>) {
+ ((ParentNode<?>) child).removeNodes();
}
- if (changed)
+ child.cleanup();
+ child.setParent(null);
+ if (triggerChildrenChanged)
childrenChanged();
+ triggerPropertyChangeEvent(child);
}
}
public abstract void asyncRemoveNode(INode node);
+ @SuppressWarnings("unchecked")
public T getNode(String id) {
- return children.get(id);
+ return (T) children.get(id);
}
/**
/**
* @return the collection of this node's children in an unspecified order
*/
+ @SuppressWarnings("unchecked")
public Collection<T> getNodes() {
- return children.values();
+ return (Collection<T>) children.values();
}
public int getNodeCount() {
*/
public void setPropertyChangeListener(PropertyChangeListener propertyChangeListener) {
this.propertyChangeListener = propertyChangeListener;
- synchronized(children) {
- for(T t : children.values()) {
- INode child = t;
- if(child instanceof ParentNode<?>) {
- ((ParentNode<?>)child).setPropertyChangeListener(propertyChangeListener);
- } else {
- ((Node)child).propertyChangeListener = propertyChangeListener; // FIXME
- }
+ children.forEach((id, child) -> {
+ if(child instanceof ParentNode<?>) {
+ ((ParentNode<?>)child).setPropertyChangeListener(propertyChangeListener);
+ } else {
+ ((Node)child).propertyChangeListener = propertyChangeListener; // FIXME
}
- }
+ });
}
- @SuppressWarnings("unchecked")
@Override
public void cleanup() {
retractMapping();
- if (children != DISPOSED_CHILDREN) {
- synchronized(children) {
- for(T child : children.values()) {
- ((INode)child).cleanup();
- ((INode)child).setParent(null);
- }
- children.clear();
- children = DISPOSED_CHILDREN;
- childrenChanged();
- }
+ if (children != null) {
+ children.forEach((id, child) -> {
+ child.cleanup();
+ child.setParent(null);
+ });
+ children.clear();
+ childrenIdMap.clear();
+ children = null;
+ childrenIdMap = null;
+ childrenChanged();
rootNodeCache = DISPOSED;
}
}
return;
}
- synchronized (children) {
- // 1. Add children under parent
- parent.appendChildren(children);
+ // 1. Add children under parent
+ parent.appendChildren(children);
- // 2. Clear children
- children.clear();
- }
+ // 2. Clear child maps to prevent cleanup from deleting them in step 4.
+ children.clear();
+ childrenIdMap.clear();
// 3. Remove this node from parent
parent.unlinkChild(this);
* 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
- }
- }
- }
+ protected void appendChildren(Map<String, INode> children) {
+ children.forEach((key, value) -> {
+ appendChildInternal(key, value);
+ });
}
- @SuppressWarnings("unchecked")
protected void appendChild(String id, INode child) {
- children.put(id, (T)child);
+ appendChildInternal(id, child);
+ }
+
+ private void appendChildInternal(String id, INode child) {
+ children.put(id, child);
+ childrenIdMap.put(child.getId(), id);
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
- }
+ triggerPropertyChangeEvent(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;
+ String id = childrenIdMap.remove(child.getId());
+ if(id != null) {
children.remove(id);
childrenChanged();
+ triggerPropertyChangeEvent(child);
}
+ }
- if(propertyChangeListener != null && location.equals(Location.LOCAL)) {
+ private void triggerPropertyChangeEvent(INode child) {
+ // 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()+"]", child.getClass(), UNLINK)); // "children" is a special field name
}
}
import org.simantics.scenegraph.INode;
import org.simantics.scenegraph.ParentNode;
-abstract public class SWTParentNode extends ParentNode<ISWTViewNode> implements ISWTViewNode {
+public abstract class SWTParentNode extends ParentNode<ISWTViewNode> implements ISWTViewNode {
private boolean disposed = false;
-
+
private static final long serialVersionUID = -3548136282051185971L;
- class M implements Map<String, ISWTViewNode> {
-
- ArrayList<ISWTViewNode> list = new ArrayList<ISWTViewNode>();
- Hashtable<String, ISWTViewNode> table = new Hashtable<String, ISWTViewNode>();
-
+ static class M implements Map<String, INode> {
+
+ ArrayList<INode> list = new ArrayList<>();
+ Hashtable<String, INode> table = new Hashtable<>();
+
@Override
public void clear() {
table.clear();
return table.containsValue(arg0);
}
@Override
- public Set<java.util.Map.Entry<String, ISWTViewNode>> entrySet() {
+ public Set<java.util.Map.Entry<String, INode>> entrySet() {
return table.entrySet();
}
@Override
- public ISWTViewNode get(Object arg0) {
+ public INode get(Object arg0) {
return table.get(arg0);
}
@Override
return table.keySet();
}
@Override
- public ISWTViewNode put(String arg0, ISWTViewNode arg1) {
- ISWTViewNode exist = table.put(arg0, arg1);
+ public INode put(String arg0, INode arg1) {
+ INode exist = table.put(arg0, arg1);
if(exist != null) list.remove(exist);
list.add(arg1);
return exist;
}
@Override
- public void putAll(Map<? extends String, ? extends ISWTViewNode> arg0) {
- for(Map.Entry<? extends String, ? extends ISWTViewNode> entry : arg0.entrySet())
+ public void putAll(Map<? extends String, ? extends INode> arg0) {
+ for(Map.Entry<? extends String, ? extends INode> entry : arg0.entrySet())
put(entry.getKey(), entry.getValue());
}
@Override
- public ISWTViewNode remove(Object arg0) {
- ISWTViewNode node = table.remove(arg0);
+ public INode remove(Object arg0) {
+ INode node = table.remove(arg0);
if(node != null) list.remove(node);
return node;
}
return table.size();
}
@Override
- public Collection<ISWTViewNode> values() {
+ public Collection<INode> values() {
return list;
}
-
+
}
-
+
@Override
public SWTRoot getRootNode() {
ParentNode<?> root = super.getRootNode();
final public void asyncRemoveNode(INode node) {
throw new Error();
}
-
+
@Override
- protected Map<String, ISWTViewNode> createChildMap() {
+ protected Map<String, INode> createChildMap(int initialCapacity) {
return new M();
}
-
+
protected void createChildComposites() {
createChildComposites((Composite)getControl());
}
protected void createChildComposites(Composite composite) {
- for(ISWTViewNode node : children.values()) node.createControls(composite);
+ for(ISWTViewNode node : getNodes()) node.createControls(composite);
}
@Override
}
public Collection<ISWTViewNode> getChildComposites() {
- return children.values();
+ return getNodes();
}
@Override