]> gerrit.simantics Code Review - simantics/platform.git/blob
ee57745ae857bf6bdf4cfd46ce6b0bba87364c2a
[simantics/platform.git] /
1 package org.simantics.spreadsheet.graph;
2
3 import java.io.File;
4 import java.io.FileNotFoundException;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.ObjectOutputStream;
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.HashMap;
11 import java.util.Iterator;
12 import java.util.List;
13 import java.util.Map;
14
15 import org.simantics.Simantics;
16 import org.simantics.databoard.Bindings;
17 import org.simantics.databoard.binding.mutable.Variant;
18 import org.simantics.databoard.util.binary.RandomAccessBinary;
19 import org.simantics.datatypes.DatatypeResource;
20 import org.simantics.datatypes.literal.Font;
21 import org.simantics.datatypes.literal.RGB;
22 import org.simantics.datatypes.utils.BTree;
23 import org.simantics.db.ReadGraph;
24 import org.simantics.db.Resource;
25 import org.simantics.db.WriteGraph;
26 import org.simantics.db.common.request.BinaryRead;
27 import org.simantics.db.common.request.ObjectsWithType;
28 import org.simantics.db.common.request.UnaryRead;
29 import org.simantics.db.common.utils.LiteralFileUtil;
30 import org.simantics.db.exception.DatabaseException;
31 import org.simantics.db.exception.ServiceException;
32 import org.simantics.db.layer0.util.Layer0Utils;
33 import org.simantics.db.layer0.variable.StandardGraphChildVariable;
34 import org.simantics.db.layer0.variable.Variable;
35 import org.simantics.db.layer0.variable.Variables;
36 import org.simantics.db.procedure.Listener;
37 import org.simantics.db.service.ClusteringSupport;
38 import org.simantics.layer0.Layer0;
39 import org.simantics.scl.compiler.commands.CommandSession;
40 import org.simantics.scl.runtime.SCLContext;
41 import org.simantics.scl.runtime.function.Function;
42 import org.simantics.scl.runtime.tuple.Tuple0;
43 import org.simantics.scl.runtime.tuple.Tuple2;
44 import org.simantics.simulator.toolkit.StandardRealm;
45 import org.simantics.spreadsheet.CellEditor;
46 import org.simantics.spreadsheet.ExternalRef;
47 import org.simantics.spreadsheet.OperationMode;
48 import org.simantics.spreadsheet.Range;
49 import org.simantics.spreadsheet.Spreadsheets;
50 import org.simantics.spreadsheet.Transaction;
51 import org.simantics.spreadsheet.graph.synchronization.SpreadsheetSynchronizationEventHandler;
52 import org.simantics.spreadsheet.resource.SpreadsheetResource;
53 import org.simantics.spreadsheet.solver.SheetNode;
54 import org.simantics.spreadsheet.solver.SpreadsheetBook;
55 import org.simantics.spreadsheet.solver.SpreadsheetCell;
56 import org.simantics.spreadsheet.solver.SpreadsheetEngine;
57 import org.simantics.spreadsheet.solver.SpreadsheetLine;
58 import org.simantics.spreadsheet.solver.SpreadsheetStyle;
59 import org.simantics.spreadsheet.util.SpreadsheetUtils;
60 import org.simantics.structural.synchronization.client.Synchronizer;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 import gnu.trove.iterator.TObjectIntIterator;
65 import gnu.trove.map.hash.TObjectIntHashMap;
66
67 public class SpreadsheetGraphUtils {
68
69     private static final Logger LOGGER = LoggerFactory.getLogger(SpreadsheetGraphUtils.class);
70
71     public static File extractInitialCondition(ReadGraph graph, Resource ic) throws DatabaseException, IOException {
72
73         SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
74
75         File temp = Simantics.getTempfile("excel","ic");
76
77         LiteralFileUtil.copyRandomAccessBinaryToFile(graph, ic, SR.InitialCondition_bytes, temp);
78         if (temp.length() == 0)
79             throw new FileNotFoundException("Snapshot file does not exist.\nThis seems to be a database bug that manifests as total loss of state file data.\nThis error prevents the program from crashing.");
80
81         return temp;
82         
83     }
84         
85     public static RandomAccessBinary getOrCreateRandomAccessBinary(WriteGraph graph, Resource initialCondition) throws DatabaseException, IOException {
86
87         SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
88         
89         // We put snapshot literals in their own clusters for now just to be safe
90         Resource literal = graph.getPossibleObject(initialCondition, SR.InitialCondition_bytes);
91         if (literal != null) {
92             RandomAccessBinary rab = graph.getRandomAccessBinary(literal);
93             rab.position(0);
94             rab.removeBytes(rab.length(), RandomAccessBinary.ByteSide.Right);
95             return rab;
96         } else {
97             Layer0 L0 = Layer0.getInstance(graph);
98             ClusteringSupport cs = graph.getService(ClusteringSupport.class);
99             literal = graph.newResource(cs.createCluster());
100             graph.claim(literal, L0.InstanceOf, null, L0.ByteArray);
101             graph.claim(initialCondition, SR.InitialCondition_bytes, SR.InitialCondition_bytes_Inverse, literal);
102             return graph.createRandomAccessBinary(literal, Bindings.BYTE_ARRAY.type(), null);
103         }
104     }
105
106     public static Resource saveInitialCondition(WriteGraph graph, Variable run, Resource container, String name) throws DatabaseException {
107
108                 String sessionName = run.getParent(graph).getURI(graph);
109
110                 Resource bookResource = run.getRepresents(graph);
111                 
112         StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
113         SpreadsheetBook book = realm.getEngine();
114
115         try {
116         
117                 File temp = Simantics.getTempfile("excel", "ic");
118                 System.err.println("Saving initial condition to " + temp.getAbsolutePath());
119                 
120                         FileOutputStream fileOut = new FileOutputStream(temp);
121                         ObjectOutputStream out = new ObjectOutputStream(fileOut);
122                         out.writeObject(book);
123                         out.close();
124                         fileOut.close();
125                         
126                         Layer0 L0 = Layer0.getInstance(graph);
127                 SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
128                         Resource ic = graph.newResource();
129                         graph.claim(ic, L0.InstanceOf, SR.InitialCondition);
130                         graph.addLiteral(ic, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING);
131                 
132                 RandomAccessBinary rab = getOrCreateRandomAccessBinary(graph, ic);
133                 LiteralFileUtil.copyRandomAccessBinaryFromFile(temp, rab);
134                 
135                         graph.claim(container, L0.ConsistsOf, L0.PartOf, ic);
136                         
137                         graph.deny(bookResource, SR.HasInitialCondition);
138                         graph.claim(bookResource, SR.HasInitialCondition, ic);
139                         graph.claim(ic, SR.InitialCondition_ConditionOf, bookResource);
140                         
141                         setDefaultInitialConditionForBook(graph, bookResource, ic);
142
143                 return ic;
144                 
145         } catch (IOException e) {
146                 
147                 throw new DatabaseException(e);
148                 
149         }
150     }
151     
152     public static void setDefaultInitialConditionForBook(WriteGraph graph, Resource book, Resource ic) throws ServiceException {
153         SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
154         graph.deny(book, SR.Book_HasDefaultInitialCondition);
155         graph.claim(ic, SR.InitialCondition_DefaultConditionOf, book);
156     }
157
158     public static void evaluateAll(ReadGraph graph, Variable run) throws DatabaseException {
159
160                 String sessionName = run.getParent(graph).getURI(graph);
161         StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
162         SpreadsheetBook book = realm.getEngine();
163         book.accept(new EvaluateAll(book));
164         
165     }
166
167     public static void invalidateAll(ReadGraph graph, Variable run) throws DatabaseException {
168
169                 String sessionName = run.getParent(graph).getURI(graph);
170         StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
171         SpreadsheetBook book = realm.getEngine();
172         book.accept(new InvalidateAll());
173         realm.getNodeManager().refreshVariables();
174         
175     }
176     
177     public static boolean fullSynchronization(ReadGraph graph, Variable run) throws DatabaseException {
178         return partialSynchronization(graph, run, null);
179     }
180
181     public static boolean partialSynchronization(ReadGraph graph, Variable run, TObjectIntHashMap<Variable> changeFlags) throws DatabaseException {
182
183         Synchronizer synchronizer = new Synchronizer(graph);
184                 String sessionName = run.getParent(graph).getURI(graph);
185                 
186         Resource bookResource = run.getRepresents(graph);
187         Variable configuration = Variables.getVariable(graph, bookResource);
188         
189         StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
190         SpreadsheetBook book = realm.getEngine();
191                         
192         SpreadsheetSynchronizationEventHandler handler = new SpreadsheetSynchronizationEventHandler(graph, book);
193         
194         if (changeFlags == null) {
195             synchronizer.fullSynchronization(configuration, handler);
196         } else {
197             
198             TObjectIntIterator<Variable> iter = changeFlags.iterator();
199             iter.advance();
200             Variable row = iter.key();
201             
202             Variable rowParent = row.getParent(graph);
203             while (!rowParent.equals(configuration)) {
204                 changeFlags.put(rowParent, 1);
205                 rowParent = rowParent.getParent(graph);
206             }
207             
208             changeFlags.put(configuration, 1);
209             
210             synchronizer.partialSynchronization(configuration, handler, changeFlags);
211         }
212         
213         realm.getNodeManager().fireNodeListeners();
214         return handler.getDidChanges();
215         
216     }
217
218     public static Variable findCell(ReadGraph graph, Variable run, String reference) throws DatabaseException {
219
220         int pos = reference.indexOf("!");
221         String sheetName = reference.substring(0, pos);
222         String cellName = reference.substring(pos+1);
223
224                 String sessionName = run.getParent(graph).getURI(graph);
225         StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
226         SpreadsheetBook book = realm.getEngine();
227         SpreadsheetEngine engine = book.getEngine(sheetName);
228         if(engine == null) return null;
229         
230         Range r = Spreadsheets.decodeCellAbsolute(cellName);
231         SpreadsheetLine line = engine.getLine(r.startRow);
232         if(line == null) return null;
233         
234         String path = line.getPath();
235         if(path == null) return null;
236         
237         Variable lineVariable = run.browse(graph, path);
238         if(lineVariable==null) return null;
239         
240         return lineVariable.getChild(graph, cellName);
241         
242     }
243
244     
245
246     public static List<Variable> possibleConfigurationCellVariables(ReadGraph graph, Variable sheet, Range range) throws DatabaseException {
247         List<Variable> rowVariables = possibleConfigurationLineVariables(graph, sheet, range);
248         List<Variable> result = new ArrayList<>();
249         for (Variable variable : rowVariables) {
250             Collection<Variable> children = variable.getChildren(graph);
251             for (Variable child : children) {
252                 if (variableInRange(graph, child, range)) {
253                     result.add(child);
254                 }
255             }
256         }
257         return result;
258     }
259     
260     public static Map<Integer, Resource> possibleConfigurationLineResources(ReadGraph graph, Variable sheet, Range range) throws DatabaseException {
261         Variable lines = sheet.getPossibleChild(graph, "Lines");
262         if (lines == null)
263             throw new DatabaseException("Invalid input variable " + sheet.getURI(graph));
264         Resource linesR = lines.getRepresents(graph);
265         BTree bt = new BTree(graph, linesR);
266         List<Tuple2> tuples = bt.searchRangeBTree(graph, Variant.ofInstance(range.startRow), Variant.ofInstance(range.endRow));
267         Map<Integer, Resource> result = new HashMap<>(tuples.size());
268         for (Tuple2 tuple : tuples) {
269             Integer lineNumber = (Integer)((Variant)tuple.c0).getValue();
270             Resource resource = (Resource)tuple.c1;
271             result.put(lineNumber, resource);
272         }
273         return result; 
274     }
275     
276     public static List<Variable> possibleConfigurationLineVariables(ReadGraph graph, Variable sheet, Range range) throws DatabaseException {
277         Map<Integer, Resource> rows = possibleConfigurationLineResources(graph, sheet, range);
278         List<Variable> result = new ArrayList<>(rows.size());
279         for (Resource row: rows.values()) {
280             Variable lineVar = Variables.getPossibleVariable(graph, row);
281             if (lineVar != null)
282                 result.add(lineVar);
283         }
284         return result;
285     }
286     
287     public static List<Variable> possibleRunLineVariables(ReadGraph graph, Variable sheetRun, Range range) throws DatabaseException {
288         
289         Variable run = sheetRun.getParent(graph);
290         
291         String sheetName = sheetRun.getName(graph);
292         String sessionName = run.getParent(graph).getURI(graph);
293         
294         StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
295         SpreadsheetBook book = realm.getEngine();
296         
297         SpreadsheetEngine engine = book.getEngine(sheetName);
298         if(engine == null) return null;
299         
300         List<Variable> result = new ArrayList<>();
301         
302         int end = range.endRow < engine.lines.getMaxRow() ? range.endRow : engine.lines.getMaxRow();
303         for (int i = range.startRow; i <= end; i++) {
304                 SpreadsheetLine line = engine.getLine(i);
305                 if(line == null)
306                         continue;
307                 
308                 String path = line.getPath();
309                 path = line.getPath();
310                 if(path == null)
311                         continue;
312                 
313                 Variable lineVariable = run.browse(graph, path);
314                 if(lineVariable==null)
315                         continue;
316                 result.add(lineVariable);
317         }
318     
319         return result;
320     }
321     
322     public static List<Variable> possibleRunCellVariables(ReadGraph graph, Variable sheetRun, Range range) throws DatabaseException {
323         List<Variable> runLineVariable = possibleRunLineVariables(graph, sheetRun, range);
324         List<Variable> result = new ArrayList<>();
325         for (Variable variable : runLineVariable) {
326 //              System.out.println("line: " + variable.getURI(graph));
327             for (Variable child : variable.getChildren(graph)) {
328 //              System.out.print("cell : " + child.getURI(graph));
329                 if (variableInRange(graph, child, range)) {
330                     result.add(child);
331                 }
332             }
333         }
334         return result;
335     }
336     
337     private static boolean variableInRange(ReadGraph graph, Variable child, Range range) throws DatabaseException {
338         String name = child.getName(graph);
339         Range childRange = Spreadsheets.decodeCellAbsolute(name);
340 //        System.out.print(" and range " + childRange);
341         if (childRange != null && range.contains(childRange)) {
342 //              System.out.println(" => range.contains(childRange) = true");
343             return true;
344         }
345 //        System.out.println();
346         return false;
347     }
348     
349     public static Map<Integer, Resource> createConfigurationLineResources(WriteGraph graph, Variable sheet, Range range) throws DatabaseException {
350         Layer0 L0 = Layer0.getInstance(graph);
351         SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
352         
353         Variable lines = sheet.getPossibleChild(graph, "Lines");
354         if (lines == null)
355             throw new DatabaseException("Invalid input variable " + sheet.getURI(graph));
356         Resource linesR = lines.getRepresents(graph);
357         BTree bt = new BTree(graph, linesR);
358         
359         Map<Integer, Resource> result = new HashMap<>();
360         for (int lineNumber = range.startRow; lineNumber <= range.endRow; lineNumber++) {
361             Resource line = graph.newResource();
362             graph.claim(line, L0.InstanceOf, null, SHEET.Line);
363             graph.claimLiteral(line, L0.HasName, L0.NameOf, L0.String, "Row" + lineNumber, Bindings.STRING);
364             bt.insertBTree(graph, Variant.ofInstance(lineNumber), line);
365             result.put(lineNumber, line);
366         }
367         return result;
368     }
369
370     public static List<Variable> getOrCreateConfigurationCellVariables(WriteGraph graph, Variable sheet, Range range) throws DatabaseException {
371         
372         List<Variable> rows = possibleConfigurationLineVariables(graph, sheet, range);
373         if (rows.isEmpty()) {
374             createConfigurationLineResources(graph, sheet, range);
375             rows = possibleConfigurationLineVariables(graph, sheet, range);
376         }
377         
378         List<Variable> cells = possibleConfigurationCellVariables(graph, sheet, range);
379         if (cells.isEmpty()) {
380             Iterator<Variable> rowIterator = rows.iterator();
381             for (int rowNumber = range.startRow; rowNumber <= range.endRow; rowNumber++) {
382                 Variable row = rowIterator.next();
383                 for (int colNumber = range.startColumn; colNumber <= range.endColumn; colNumber++) {
384                     String location = Spreadsheets.cellName(rowNumber, colNumber);
385                     defaultCreateCell(graph, row, location, new Variant(Bindings.STRING, ""));
386                 }
387             }
388         }
389         
390         cells = possibleConfigurationCellVariables(graph, sheet, range);
391         if(cells.isEmpty())
392             throw new DatabaseException("Unexpected problem while creating spreadsheet cell at '" + range + "'");
393         
394         return cells;
395     }
396     
397     private static void defaultCreateCell(WriteGraph graph, Variable parent, String location, Variant value) throws DatabaseException {
398
399         Layer0 L0 = Layer0.getInstance(graph);
400         SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
401         Resource container = parent.getRepresents(graph);
402         
403         Resource cell = graph.newResource();
404         graph.claim(cell, L0.InstanceOf, null, SHEET.TextCell);
405         graph.addLiteral(cell, L0.HasName, L0.NameOf, L0.String, location, Bindings.STRING);
406         graph.addLiteral(cell, SHEET.Cell_content, SHEET.Cell_content_Inverse, L0.Variant, value, Bindings.VARIANT);
407         graph.claim(cell, L0.PartOf, container);
408         
409         Resource book = Variables.getContext(graph, parent).getRepresents(graph);
410         
411         
412         Collection<Resource> objects = graph.sync(new ObjectsWithType(book, L0.ConsistsOf, SHEET.Style));
413         
414         int styleId = SpreadsheetStyle.empty().getStyleId();
415         Resource style = null;
416         for (Resource possibleStyle : objects) {
417             int possibleStyleId = graph.getRelatedValue2(possibleStyle, SHEET.Style_id, Bindings.INTEGER);
418             if (possibleStyleId == styleId) {
419                 style = possibleStyle;
420                 break;
421             }
422         }
423         
424         if (style == null) {
425             style = graph.newResource();
426             graph.claim(style, L0.InstanceOf, null, SHEET.Style);
427             graph.claim(style, L0.PartOf, book);
428             
429             int id = objects.size();
430             graph.claimLiteral(style, L0.HasName, "Style_" + id);
431             graph.claimLiteral(style, SHEET.Style_id, styleId, Bindings.INTEGER);
432         }
433         graph.claim(cell, SHEET.Cell_HasStyle, style);
434         Layer0Utils.addCommentMetadata(graph, "Created cell on location " + location + " with value " + value.toString());
435     }
436
437     public static Resource createStyle(WriteGraph graph, Resource book, SpreadsheetStyle sstyle) throws DatabaseException {
438         Layer0 L0 = Layer0.getInstance(graph);
439         SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
440         Resource style = graph.newResource();
441         graph.claim(style, L0.InstanceOf, null, SR.Style);
442         graph.claim(style, L0.PartOf, book);
443         
444         int styleId = sstyle.getStyleId();
445         String styleName = sstyle.name;
446         
447         graph.claimLiteral(style, L0.HasName, styleName);
448         //System.err.println("CREATING STYLE " + styleName + " WITH ID: " + styleId);
449         graph.claimLiteral(style, SR.Style_id, styleId, Bindings.INTEGER);
450         
451         DatatypeResource DATATYPES = DatatypeResource.getInstance(graph);
452         if (sstyle.foreground != null)
453             graph.claimLiteral(style, SR.Cell_foreground, DATATYPES.RGB_Integer, sstyle.foreground, RGB.Integer.BINDING);
454         if (sstyle.background != null)
455             graph.claimLiteral(style, SR.Cell_background, DATATYPES.RGB_Integer, sstyle.background, RGB.Integer.BINDING);
456         if (sstyle.align != -1)
457             graph.claimLiteral(style, SR.Cell_align, sstyle.align, Bindings.INTEGER);
458         if (sstyle.font != null)
459             graph.claimLiteral(style, SR.Cell_font, DATATYPES.Font, sstyle.font, Font.BINDING);
460         if (sstyle.border != -1)
461             graph.claimLiteral(style, SR.Cell_border, sstyle.border);
462         if (sstyle.formatString != null && !sstyle.formatString.isEmpty())
463             graph.claimLiteral(style, SR.Cell_formatString, sstyle.formatString, Bindings.STRING);
464         if (sstyle.formatIndex != -1)
465             graph.claimLiteral(style, SR.Cell_formatIndex, sstyle.formatIndex, Bindings.INTEGER);
466         
467         return style;
468     }
469     
470     public static Resource createBook(WriteGraph graph, Resource parent, String name) throws DatabaseException {
471         Layer0 L0 = Layer0.getInstance(graph);
472         SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
473         Resource book = graph.newResource();
474         graph.claim(book, L0.InstanceOf, SR.Book);
475         graph.claimLiteral(book, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING);
476         graph.claim(parent, L0.ConsistsOf, book);
477         
478         return book;
479     }
480     
481     public static Variable constructAndInitializeRunVariable(WriteGraph graph, Resource root) throws DatabaseException {
482         Variable run = SpreadsheetUtils.getBookVariable(graph, root);
483         SpreadsheetGraphUtils.fullSynchronization(graph, run);
484         SpreadsheetGraphUtils.evaluateAll(graph, run);
485         SpreadsheetGraphUtils.saveInitialCondition(graph, run, root, "Initial");
486         return run;
487     }
488     
489     public static Variant extRefVariable(ReadGraph graph, Variable var) throws DatabaseException {
490         return new Variant(Bindings.VOID, new ExternalRefVariable(graph, var));
491     }
492     
493     static class ExternalRefVariable implements ExternalRef {
494
495         final private String uri;
496         
497         public ExternalRefVariable(ReadGraph graph, Variable variable) throws DatabaseException {
498             this.uri = variable.getURI(graph);
499         }
500         
501         @Override
502         public void listen(Object context, ExternalRefListener listener) {
503             Simantics.getSession().asyncRequest(new UnaryRead<String, Variant>(uri) {
504
505                 @Override
506                 public Variant perform(ReadGraph graph) throws DatabaseException {
507                     Variable variable = Variables.getVariable(graph, parameter);
508                     return variable.getVariantValue(graph);
509                 }
510                 
511             }, new Listener<Variant>() {
512
513                 @Override
514                 public void execute(Variant result) {
515                     listener.newValue(result);
516                 }
517
518                 @Override
519                 public void exception(Throwable t) {
520                     LOGGER.error("Error while evaluating variable value", t);
521                 }
522
523                 @Override
524                 public boolean isDisposed() {
525                     return listener.isDisposed();
526                 }
527                 
528             });
529         }
530         
531     }
532
533     public static Variant extRefActiveVariable(ReadGraph graph, Variable var) throws DatabaseException {
534         return new Variant(Bindings.VOID, new ExternalRefActiveVariable(graph, var));
535     }
536     
537     static class ExternalRefActiveVariable implements ExternalRef {
538
539         final private String uri;
540         
541         public ExternalRefActiveVariable(ReadGraph graph, Variable variable) throws DatabaseException {
542             this.uri = variable.getURI(graph);
543         }
544         
545         @Override
546         public void listen(Object context, ExternalRefListener listener) {
547             Simantics.getSession().asyncRequest(new BinaryRead<String, String, Variant>((String)context, uri) {
548
549                 @Override
550                 public Variant perform(ReadGraph graph) throws DatabaseException {
551                     Variable contextVariable = Variables.getVariable(graph, parameter);
552                     Variable configVariable = Variables.getVariable(graph, parameter2);
553                     Variable activeVariable = Variables.switchPossibleContext(graph, configVariable, contextVariable.getRepresents(graph));
554                     return activeVariable.getVariantValue(graph);
555                 }
556             }, new Listener<Variant>() {
557
558                 @Override
559                 public void execute(Variant result) {
560                     listener.newValue(result);
561                 }
562
563                 @Override
564                 public void exception(Throwable t) {
565                     LOGGER.error("Error while evaluating variable value", t);
566                 }
567
568                 @Override
569                 public boolean isDisposed() {
570                     return listener.isDisposed();
571                 }
572                 
573             });
574         }
575         
576     }
577     
578     public static CellEditor cellEditor(ReadGraph graph, Resource sheet) throws DatabaseException {
579         SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
580         Variable sheetVariable = Variables.getVariable(graph, sheet);
581         return sheetVariable.getPropertyValue(graph, SHEET.cellEditor);
582     }
583     
584     public static final String SPREADSHEET_TRANSACTION = "spreadsheetTransaction";
585
586     @SuppressWarnings({ "rawtypes", "unchecked" })
587     public static Object syncExec(CellEditor editor, OperationMode mode, Function fun) throws InterruptedException {
588         
589         Transaction tr = editor.startTransaction(mode);
590         
591         SCLContext context = SCLContext.getCurrent();
592         Transaction oldTransaction = (Transaction)context.put(SPREADSHEET_TRANSACTION, tr);
593         
594         Object result = null;
595         
596         try {
597
598             result = fun.apply(Tuple0.INSTANCE);
599             
600         } finally {
601             
602             tr.commit();
603             
604             context.put(SPREADSHEET_TRANSACTION, oldTransaction);
605             
606         }
607         
608         return result;
609         
610     }
611     
612     public static int cellColumn(ReadGraph graph, Variable cell) {
613         if(cell instanceof StandardGraphChildVariable) {
614             StandardGraphChildVariable sgcv = (StandardGraphChildVariable)cell;
615             SpreadsheetCell sc = (SpreadsheetCell)sgcv.node.node;
616             return sc.getColumn();
617         }
618         throw new IllegalStateException("Expected StandardGraphChildVariable, got " + cell.getClass().getName());
619     }
620     
621 }