1 package org.simantics.db.layer0.variable;
3 import java.util.Collections;
7 import org.simantics.databoard.util.ObjectUtils;
8 import org.simantics.db.ReadGraph;
9 import org.simantics.db.common.request.ParametrizedPrimitiveRead;
10 import org.simantics.db.exception.DatabaseException;
11 import org.simantics.db.layer0.variable.Variables.NodeStructure;
12 import org.simantics.db.procedure.Listener;
13 import org.simantics.simulator.variable.exceptions.NodeManagerException;
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
17 import gnu.trove.map.hash.THashMap;
19 @SuppressWarnings("rawtypes")
20 class NodeStructureRequest extends ParametrizedPrimitiveRead<VariableNode, NodeStructure> implements VariableNodeReadRunnable {
22 private static final Logger LOGGER = LoggerFactory.getLogger(NodeStructureRequest.class);
24 private Listener<NodeStructure> listener = null;
25 private NodeStructure value = Variables.PENDING_NODE_STRUCTURE;
26 private boolean wasRun = false;
28 static class Probe implements Runnable {
30 private VariableNode node;
31 public NodeStructure result;
33 public Probe(VariableNode node) {
37 @SuppressWarnings("unchecked")
41 result = NodeStructureRequest.get(node);
42 node.support.structureCache.put(node.node, result, 1000000000L);
43 } catch (NodeManagerException e) {
50 public NodeStructureRequest(VariableNode node) {
54 @SuppressWarnings("unchecked")
56 public void register(ReadGraph graph, final Listener<NodeStructure> procedure) {
58 if(procedure.isDisposed()) {
60 // We are not listening
61 NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node);
64 // Return cached value immediately
65 procedure.execute(result);
67 NodeStructureRequest.Probe probe = new Probe(parameter);
68 parameter.support.manager.getRealm().asyncExec(probe);
69 if(probe.result != null) {
70 procedure.execute(probe.result);
72 procedure.execute(Variables.PENDING_NODE_STRUCTURE);
83 parameter.support.manager.addNodeListener(parameter.node, this);
86 NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node);
88 procedure.execute(result);
90 procedure.execute(Variables.PENDING_NODE_STRUCTURE);
97 static class NodeListener implements VariableNodeReadRunnable {
99 private VariableNode node;
100 private NodeStructureRequest request;
102 public NodeListener(VariableNode node, NodeStructureRequest request) {
104 this.request = request;
107 @SuppressWarnings("unchecked")
110 node.support.manager.addNodeListener(node.node, request);
115 @SuppressWarnings("unchecked")
117 public void unregistered() {
118 parameter.support.manager.removeNodeListener(parameter.node, this);
119 parameter.support.structureCache.removeListening(parameter.node);
123 @SuppressWarnings("unchecked")
124 public static NodeStructure get(VariableNode parameter) throws NodeManagerException {
125 List<?> children = parameter.support.manager.getChildren(parameter.node);
126 List<?> properties = parameter.support.manager.getProperties(parameter.node);
127 Map<String, Object> childMap = Collections.emptyMap();
128 Map<String, Object> propertyMap = childMap;
129 if(!children.isEmpty()) {
130 childMap = new THashMap<>(children.size());
131 for(Object o : children) {
132 String name = parameter.support.manager.getName(o);
133 childMap.put(name, o);
136 if(!properties.isEmpty()) {
137 propertyMap = new THashMap<>(properties.size());
138 for(Object o : properties) {
139 String name = parameter.support.manager.getName(o);
140 propertyMap.put(name, o);
143 return new NodeStructure(childMap, propertyMap);
146 @SuppressWarnings("unchecked")
148 public synchronized void run() {
150 // Cache this value with infinite cache time since we are listening
151 NodeStructure newValue = get(parameter);
152 if (wasRun && ObjectUtils.objectEquals(value, newValue)) {
153 //System.out.println("CACHE VALUE MATCH (" + newValue + ") for " + node.node);
157 parameter.support.structureCache.put(parameter.node, value);
158 } catch (Throwable e) {
159 // Must catch everything to prevent DB client from getting stuck.
160 LOGGER.error("Error while computing node structure", e);
161 // Invoke the exception method of the listener
162 Listener<NodeStructure> listener = this.listener;
163 if (listener != null) {
164 listener.exception(new DatabaseException("External data access error", e));
170 // Must always invoke an existing listener, regardless of earlier errors.
171 Listener<NodeStructure> listener = this.listener;
172 if (listener != null) {
173 listener.execute(value);
179 public String toString() {
180 return "NodeStructureRequest.run @ " + System.identityHashCode(this);