Listenable queries in SCL
[simantics/platform.git] / bundles / org.simantics.scl.db / src / org / simantics / scl / db / SCLFunctions.java
1 package org.simantics.scl.db;
2
3 import java.io.IOException;
4
5 import org.cojen.classfile.TypeDesc;
6 import org.simantics.Simantics;
7 import org.simantics.db.ReadGraph;
8 import org.simantics.db.Resource;
9 import org.simantics.db.VirtualGraph;
10 import org.simantics.db.WriteGraph;
11 import org.simantics.db.common.procedure.adapter.SyncListenerAdapter;
12 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
13 import org.simantics.db.common.request.BinaryRead;
14 import org.simantics.db.common.request.DelayedWriteRequest;
15 import org.simantics.db.common.request.ReadRequest;
16 import org.simantics.db.common.request.WriteRequest;
17 import org.simantics.db.common.request.WriteResultRequest;
18 import org.simantics.db.exception.DatabaseException;
19 import org.simantics.db.layer0.util.Layer0Utils;
20 import org.simantics.db.layer0.variable.Variables;
21 import org.simantics.db.request.Read;
22 import org.simantics.db.service.ClusterControl;
23 import org.simantics.db.service.SerialisationSupport;
24 import org.simantics.db.service.VirtualGraphSupport;
25 import org.simantics.layer0.utils.triggers.IActivationManager;
26 import org.simantics.scl.compiler.environment.specification.EnvironmentSpecification;
27 import org.simantics.scl.compiler.errors.Failable;
28 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
29 import org.simantics.scl.compiler.module.Module;
30 import org.simantics.scl.compiler.module.repository.ImportFailureException;
31 import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
32 import org.simantics.scl.compiler.types.Type;
33 import org.simantics.scl.osgi.SCLOsgi;
34 import org.simantics.scl.runtime.SCLContext;
35 import org.simantics.scl.runtime.function.Function;
36 import org.simantics.scl.runtime.function.Function1;
37 import org.simantics.scl.runtime.tuple.Tuple;
38 import org.simantics.scl.runtime.tuple.Tuple0;
39 import org.simantics.utils.DataContainer;
40
41 @SuppressWarnings({"rawtypes", "unchecked"})
42 public class SCLFunctions {
43
44     public static final String GRAPH = "graph";
45
46     public static <T> T safeExec(final Function f) {
47         try {
48             return (T)f.apply(Tuple0.INSTANCE);
49         } catch (Throwable t) {
50             t.printStackTrace();
51             return null;
52         }
53     }
54     
55     public static void asyncRead(final Function f) throws DatabaseException {
56         final SCLContext context = SCLContext.getCurrent();
57         Object graph = context.get(GRAPH);
58         if (graph != null) {
59             f.apply(Tuple0.INSTANCE);
60         } else {
61             Simantics.getSession().asyncRequest(new ReadRequest() {
62                 @Override
63                 public void run(ReadGraph graph) throws DatabaseException {
64                     SCLContext.push(context);
65                     ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
66                     try {
67                         f.apply(Tuple0.INSTANCE);
68                     } finally {
69                         context.put(GRAPH, oldGraph);
70                         SCLContext.pop();
71                     }
72                 }
73             });
74         }
75     }
76     
77     public static <T> T syncRead(final Function f) throws DatabaseException {
78         final SCLContext context = SCLContext.getCurrent();
79         Object graph = context.get(GRAPH);
80         if (graph != null) {
81             return (T)f.apply(Tuple0.INSTANCE);
82         } else {
83             return Simantics.getSession().syncRequest(new Read<T>() {
84                 @Override
85                 public T perform(ReadGraph graph) throws DatabaseException {
86                     SCLContext.push(context);
87                     ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
88                     try {
89                         return (T)f.apply(Tuple0.INSTANCE);
90                     } finally {
91                         context.put(GRAPH, oldGraph);
92                         SCLContext.pop();
93                     }
94                 }
95             });
96         }
97     }
98     
99     public static void asyncWrite(final Function f) throws DatabaseException {
100         final SCLContext context = SCLContext.getCurrent();
101         Object graph = context.get(GRAPH);
102         if (graph != null) {
103             f.apply(Tuple0.INSTANCE);
104         } else {
105             Simantics.getSession().asyncRequest(new WriteRequest() {
106                 @Override
107                 public void perform(WriteGraph graph) throws DatabaseException {
108                     SCLContext.push(context);
109                     ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
110                     try {
111                         f.apply(Tuple0.INSTANCE);
112                     } finally {
113                         context.put(GRAPH, oldGraph);
114                         SCLContext.pop();
115                     }
116                 }
117             });
118         }
119     }
120     
121     public static <T> T syncWrite(final Function f) throws DatabaseException {
122         final SCLContext context = SCLContext.getCurrent();
123         Object graph = context.get(GRAPH);
124         if (graph != null) {
125             return (T)f.apply(Tuple0.INSTANCE);
126         } else {
127             return Simantics.getSession().syncRequest(new WriteResultRequest<T>() {
128                 @Override
129                 public T perform(WriteGraph graph) throws DatabaseException {
130                     SCLContext.push(context);
131                     ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
132                     try {
133                         return (T)f.apply(Tuple0.INSTANCE);
134                     } finally {
135                         context.put(GRAPH, oldGraph);
136                         SCLContext.pop();
137                     }
138                 }
139             });
140         }
141     }
142     
143     public static <T> T delayedSyncWrite(final Function f) throws DatabaseException {
144         final SCLContext context = SCLContext.getCurrent();
145         final DataContainer<T> dc = new DataContainer<T>(null);
146
147         DelayedWriteRequest request = new DelayedWriteRequest() {
148             @Override
149             public void perform(WriteGraph graph) throws DatabaseException {
150                 final SCLContext context = SCLContext.getCurrent();
151                 SCLContext.push(context);
152                 ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
153                 try {
154                     dc.set((T)f.apply(Tuple0.INSTANCE));
155                 } finally {
156                     context.put(GRAPH, oldGraph);
157                     SCLContext.pop();
158                 }
159             }
160         };
161         
162         Object graph = context.get(GRAPH);
163         if (graph != null) {
164             if (graph instanceof WriteGraph) {
165                 ((WriteGraph)graph).syncRequest(request);
166             } else {
167                 throw new DatabaseException("Caller is inside a read transaction.");
168             }
169         } else {
170             Simantics.getSession().syncRequest(request);
171         }
172         return dc.get();
173     }
174
175     public static <T> T virtualSyncWriteMem(WriteGraph graph, String virtualGraphId, final Function f) throws DatabaseException {
176         final SCLContext context = SCLContext.getCurrent();
177         VirtualGraphSupport vgs = graph.getService(VirtualGraphSupport.class);
178         VirtualGraph vg = vgs.getMemoryPersistent(virtualGraphId);
179         return graph.syncRequest(new WriteResultRequest<T>(vg) {
180             @Override
181             public T perform(WriteGraph graph) throws DatabaseException {
182                 SCLContext.push(context);
183                 ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
184                 try {
185                     return (T)f.apply(Tuple0.INSTANCE);
186                 } finally {
187                     context.put(GRAPH, oldGraph);
188                     SCLContext.pop();
189                 }
190             }
191         });
192     }
193     
194     public static <T> T virtualSyncWriteWS(WriteGraph graph, String virtualGraphId, final Function f) throws DatabaseException {
195         final SCLContext context = SCLContext.getCurrent();
196         VirtualGraphSupport vgs = graph.getService(VirtualGraphSupport.class);
197         VirtualGraph vg = vgs.getWorkspacePersistent(virtualGraphId);
198         return graph.syncRequest(new WriteResultRequest<T>(vg) {
199             @Override
200             public T perform(WriteGraph graph) throws DatabaseException {
201                 SCLContext.push(context);
202                 ReadGraph oldGraph = (ReadGraph)context.put(GRAPH, graph);
203                 try {
204                     return (T)f.apply(Tuple0.INSTANCE);
205                 } finally {
206                     context.put(GRAPH, oldGraph);
207                     SCLContext.pop();
208                 }
209             }
210         });
211     }
212     
213     public static <T> T readValue(final String uri) throws DatabaseException {
214         return Simantics.getSession().syncRequest(new Read<T>() {
215             @Override
216             public T perform(ReadGraph graph) throws DatabaseException {
217                 return Variables.getVariable(graph, uri).getValue(graph);
218             }            
219         });
220     }
221     
222     public static <T> void writeValue(final String uri, final T value) throws DatabaseException {
223         Simantics.getSession().syncRequest(new WriteRequest() {
224             @Override
225             public void perform(WriteGraph graph) throws DatabaseException {
226                 Variables.getVariable(graph, uri).setValue(graph, value);
227             }            
228         });
229     }
230     
231     public static void activateOnce(Resource r) {
232         Simantics.getSession().getService(IActivationManager.class).activateOnce(r);
233     }
234     
235     public static void syncActivateOnce(WriteGraph graph, Resource r) throws DatabaseException {
236         graph.getService(IActivationManager.class).activateOnce(graph, r);
237     }
238
239     public static Resource resourceFromId(ReadGraph graph, long id) throws DatabaseException, IOException {
240         SerialisationSupport ss = graph.getService(SerialisationSupport.class);
241         return ss.getResource(id);
242     }
243     
244     public static void disableDependencies(WriteGraph graph) {
245         Layer0Utils.setDependenciesIndexingDisabled(graph, true);       
246     }
247     
248     public static void enableDependencies(WriteGraph graph) {
249         Layer0Utils.setDependenciesIndexingDisabled(graph, false);       
250     }
251     
252     public static void collectClusters() {
253         Simantics.getSession().getService(ClusterControl.class).collectClusters(Integer.MAX_VALUE);
254     }
255     
256     public static class SCLUnaryRead extends BinaryRead<Function1<Object,Object>, Object, Object> {
257
258                 public SCLUnaryRead(Function1<Object, Object> parameter1, Object parameter2) {
259                         super(parameter1, parameter2);
260                 }
261
262                 @Override
263                 public Object perform(ReadGraph graph) throws DatabaseException {
264                         return Simantics.applySCLRead(graph, parameter, parameter2);
265                 }
266         
267     }
268     
269     public static Object unaryQuery(ReadGraph graph, Function1<Object,Object> fn, Object value) throws DatabaseException {
270         return graph.syncRequest(new SCLUnaryRead(fn, value));
271     }
272
273     public static Object unaryQueryCached(ReadGraph graph, Function1<Object,Object> fn, Object value) throws DatabaseException {
274         return graph.syncRequest(new SCLUnaryRead(fn, value), TransientCacheAsyncListener.<Object>instance());
275     }
276     
277
278     private static class Subquery implements Read<Object> {
279         Function q;
280
281         public Subquery(Function q) {
282             this.q = q;
283         }
284
285         @Override
286         public Object perform(ReadGraph graph) throws DatabaseException {
287             SCLContext sclContext = SCLContext.getCurrent();
288             Object oldGraph = sclContext.put("graph", graph);
289             try {
290                 return q.apply(Tuple0.INSTANCE);
291             } catch (Throwable e) {
292                 if(e instanceof DatabaseException)
293                     throw (DatabaseException)e;
294                 else
295                     throw new DatabaseException(e);
296             } finally {
297                 sclContext.put("graph", oldGraph);
298             }
299         }
300
301         @Override
302         public int hashCode() {
303             return q.hashCode();
304         }
305         
306         @Override
307         public boolean equals(Object obj) {
308             if(this == obj)
309                 return true;
310             if(obj == null || obj.getClass() != getClass())
311                 return false;
312             Subquery other = (Subquery)obj;
313             return q.equals(other.q);
314         }
315     }
316
317     public static Object subquery(ReadGraph graph, Function q) throws DatabaseException {
318         return graph.syncRequest(new Subquery(q));
319     }
320
321     public static Object subqueryC(ReadGraph graph, Function q) throws DatabaseException {
322         return graph.syncRequest(new Subquery(q), TransientCacheAsyncListener.<Object>instance());
323     }
324     
325     public static void subqueryL(ReadGraph graph, Function query, Function executeCallback, Function1<Throwable, Tuple> exceptionCallback, Function1<Tuple0, Boolean> isDisposedCallback) throws DatabaseException {
326         graph.asyncRequest(new Subquery(query), new SyncListenerAdapter<Object>() {
327             
328             @Override
329             public void execute(ReadGraph graph, Object result) {
330                 executeCallback.apply(result);
331             }
332             
333             @Override
334             public void exception(ReadGraph graph, Throwable t) {
335                 exceptionCallback.apply(t);
336             }
337             
338             @Override
339             public boolean isDisposed() {
340                 return isDisposedCallback.apply(Tuple0.INSTANCE);
341             }
342         });
343     }
344
345     public static Object possibleFromDynamic(Type expectedType, String moduleName, Object value) {
346         
347         try {
348
349                 
350             Failable<Module> failable = SCLOsgi.MODULE_REPOSITORY.getModule(moduleName);
351             Module module = failable.getResult();
352             
353                 RuntimeEnvironment env = SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment(
354                                 EnvironmentSpecification.of(moduleName, ""), module.getParentClassLoader());
355
356             JavaTypeTranslator tr = new JavaTypeTranslator(env.getEnvironment());
357             TypeDesc desc = tr.toTypeDesc(expectedType);
358             String className = desc.getFullName();
359             Class<?> clazz = env.getMutableClassLoader().loadClass(className);
360             if (!clazz.isAssignableFrom(value.getClass()))
361                 return null;
362             
363         } catch (ImportFailureException | ClassNotFoundException e) {
364         }
365         return value;
366     }
367
368 }