]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeStructureRequest.java
a87a2042a9452cbe8e187b352f47da116b8f9d8c
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / variable / NodeStructureRequest.java
1 package org.simantics.db.layer0.variable;\r
2 \r
3 import java.util.Collections;\r
4 import java.util.List;\r
5 import java.util.Map;\r
6 \r
7 import org.simantics.databoard.util.ObjectUtils;\r
8 import org.simantics.db.ReadGraph;\r
9 import org.simantics.db.common.request.ParametrizedPrimitiveRead;\r
10 import org.simantics.db.common.utils.Logger;\r
11 import org.simantics.db.exception.DatabaseException;\r
12 import org.simantics.db.layer0.variable.Variables.NodeStructure;\r
13 import org.simantics.db.procedure.Listener;\r
14 import org.simantics.simulator.variable.exceptions.NodeManagerException;\r
15 \r
16 import gnu.trove.map.hash.THashMap;\r
17 \r
18 @SuppressWarnings("rawtypes")\r
19 class NodeStructureRequest extends ParametrizedPrimitiveRead<VariableNode, NodeStructure> implements VariableNodeReadRunnable {\r
20 \r
21         private Listener<NodeStructure> listener = null;\r
22         private NodeStructure value = Variables.PENDING_NODE_STRUCTURE;\r
23         private boolean wasRun = false;\r
24 \r
25         static class Probe implements Runnable {\r
26 \r
27                 private VariableNode node;\r
28                 public NodeStructure result;\r
29 \r
30                 public Probe(VariableNode node) {\r
31                         this.node = node;\r
32                 }\r
33 \r
34                 @SuppressWarnings("unchecked")\r
35                 @Override\r
36                 public void run() {\r
37                         try {\r
38                                 result = NodeStructureRequest.get(node);\r
39                                 node.support.structureCache.put(node.node, result, 1000000000L);\r
40                         } catch (NodeManagerException e) {\r
41                                 e.printStackTrace();\r
42                         }\r
43                 }\r
44 \r
45         }\r
46 \r
47         public NodeStructureRequest(VariableNode node) {\r
48                 super(node);\r
49         }\r
50 \r
51         @SuppressWarnings("unchecked")\r
52         @Override\r
53         public void register(ReadGraph graph, final Listener<NodeStructure> procedure) {\r
54 \r
55                 if(procedure.isDisposed()) {\r
56 \r
57                         // We are not listening\r
58                         NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node);\r
59 \r
60                         if(result != null) {\r
61                                 // Return cached value immediately\r
62                                 procedure.execute(result);\r
63                         } else {\r
64                                 NodeStructureRequest.Probe probe = new Probe(parameter);\r
65                                 parameter.support.manager.getRealm().asyncExec(probe);\r
66                                 if(probe.result != null) {\r
67                                         procedure.execute(probe.result);\r
68                                 } else {\r
69                                         procedure.execute(Variables.PENDING_NODE_STRUCTURE);\r
70                                 }\r
71                         }\r
72 \r
73                         return;\r
74 \r
75                 }\r
76 \r
77                 // We need to listen\r
78                 listener = procedure;\r
79                 // Register listening\r
80                 parameter.support.manager.addNodeListener(parameter.node, this);\r
81                 synchronized(this) {\r
82                         if(wasRun) {\r
83                                 procedure.execute(value);\r
84                         } else {\r
85                                 NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node);\r
86                                 if(result != null) {\r
87                                         procedure.execute(result);\r
88                                 } else {\r
89                                         procedure.execute(Variables.PENDING_NODE_STRUCTURE);\r
90                                 }\r
91                         }\r
92                 }\r
93 \r
94         }\r
95 \r
96         static class NodeListener implements VariableNodeReadRunnable {\r
97 \r
98                 private VariableNode node;\r
99                 private NodeStructureRequest request;\r
100 \r
101                 public NodeListener(VariableNode node, NodeStructureRequest request) {\r
102                         this.node = node;\r
103                         this.request = request;\r
104                 }\r
105 \r
106                 @SuppressWarnings("unchecked")\r
107                 @Override\r
108                 public void run() {\r
109                         node.support.manager.addNodeListener(node.node, request);\r
110                 }\r
111 \r
112         }\r
113 \r
114         @SuppressWarnings("unchecked")\r
115         @Override\r
116         public void unregistered() {\r
117                 parameter.support.manager.removeNodeListener(parameter.node, this);\r
118                 parameter.support.structureCache.removeListening(parameter.node);\r
119                 listener = null;\r
120         }\r
121 \r
122         @SuppressWarnings("unchecked")\r
123         public static NodeStructure get(VariableNode parameter) throws NodeManagerException {\r
124                 List<?> children = parameter.support.manager.getChildren(parameter.node);\r
125                 List<?> properties = parameter.support.manager.getProperties(parameter.node);\r
126                 Map<String, Object> childMap = Collections.emptyMap();\r
127                 Map<String, Object> propertyMap = childMap;\r
128                 if(!children.isEmpty()) {\r
129                         childMap = new THashMap<>(children.size());\r
130                         for(Object o : children) {\r
131                                 String name = parameter.support.manager.getName(o);\r
132                                 childMap.put(name, o);\r
133                         }\r
134                 }\r
135                 if(!properties.isEmpty()) {\r
136                         propertyMap = new THashMap<>(properties.size());\r
137                         for(Object o : properties) {\r
138                                 String name = parameter.support.manager.getName(o);\r
139                                 propertyMap.put(name, o);\r
140                         }\r
141                 }\r
142                 return new NodeStructure(childMap, propertyMap);\r
143         }\r
144 \r
145         @SuppressWarnings("unchecked")\r
146         @Override\r
147         public synchronized void run() {\r
148                 try {\r
149                         // Cache this value with infinite cache time since we are listening\r
150                         NodeStructure newValue = get(parameter);\r
151                         if (wasRun && ObjectUtils.objectEquals(value, newValue)) {\r
152                                 //System.out.println("CACHE VALUE MATCH (" + newValue + ") for " + node.node);\r
153                                 return;\r
154                         }\r
155                         value = newValue;\r
156                         parameter.support.structureCache.put(parameter.node, value);\r
157                 } catch (Throwable e) {\r
158                         // Must catch everything to prevent DB client from getting stuck.\r
159                         Logger.defaultLogError(e);\r
160                         // Invoke the exception method of the listener\r
161                         Listener<NodeStructure> listener = this.listener;\r
162                         if (listener != null) listener.exception(new DatabaseException("External data access error", e));\r
163                         wasRun = true;\r
164                         return;\r
165                 }\r
166 \r
167                 // Must always invoke an existing listener, regardless of earlier errors.\r
168                 Listener<NodeStructure> listener = this.listener;\r
169                 if (listener != null) {\r
170                         listener.execute(value);\r
171                 }\r
172                 wasRun = true;\r
173         }\r
174 \r
175         @Override\r
176         public String toString() {\r
177                 return "NodeStructureRequest.run @ " + System.identityHashCode(this);\r
178         }\r
179 \r
180 }