]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/GraphUI.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.spreadsheet.graph / src / org / simantics / spreadsheet / graph / GraphUI.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2018 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.spreadsheet.graph;
13
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.function.Consumer;
20
21 import org.eclipse.e4.core.contexts.IEclipseContext;
22 import org.eclipse.e4.ui.di.UISynchronize;
23 import org.eclipse.jface.dialogs.Dialog;
24 import org.eclipse.ui.PlatformUI;
25 import org.simantics.Simantics;
26 import org.simantics.databoard.Bindings;
27 import org.simantics.databoard.binding.Binding;
28 import org.simantics.databoard.binding.mutable.MutableVariant;
29 import org.simantics.databoard.binding.mutable.Variant;
30 import org.simantics.db.AsyncReadGraph;
31 import org.simantics.db.ReadGraph;
32 import org.simantics.db.RequestProcessor;
33 import org.simantics.db.Resource;
34 import org.simantics.db.WriteGraph;
35 import org.simantics.db.common.procedure.adapter.AsyncListenerSupport;
36 import org.simantics.db.common.procedure.adapter.ListenerSupport;
37 import org.simantics.db.common.procedure.adapter.SyncListenerSupport;
38 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
39 import org.simantics.db.common.procedure.single.SingleSetSyncListenerDelegate;
40 import org.simantics.db.common.request.ReadRequest;
41 import org.simantics.db.common.request.ResourceRead;
42 import org.simantics.db.common.request.UnaryRead;
43 import org.simantics.db.common.request.UniqueRead;
44 import org.simantics.db.common.request.WriteRequest;
45 import org.simantics.db.common.request.WriteResultRequest;
46 import org.simantics.db.common.session.SessionEventListenerAdapter;
47 import org.simantics.db.exception.DatabaseException;
48 import org.simantics.db.layer0.request.PossibleURIVariable;
49 import org.simantics.db.layer0.request.VariableName;
50 import org.simantics.db.layer0.request.VariableRead;
51 import org.simantics.db.layer0.variable.ProxyVariables;
52 import org.simantics.db.layer0.variable.Variable;
53 import org.simantics.db.layer0.variable.Variables;
54 import org.simantics.db.procedure.SyncListener;
55 import org.simantics.db.request.Write;
56 import org.simantics.db.service.SessionEventSupport;
57 import org.simantics.layer0.Layer0;
58 import org.simantics.simulator.toolkit.StandardRealm;
59 import org.simantics.spreadsheet.Adaptable;
60 import org.simantics.spreadsheet.CellEditor;
61 import org.simantics.spreadsheet.ClientModel;
62 import org.simantics.spreadsheet.OperationMode;
63 import org.simantics.spreadsheet.SheetCommands;
64 import org.simantics.spreadsheet.Transaction;
65 import org.simantics.spreadsheet.event.model.RemoveCellHandler;
66 import org.simantics.spreadsheet.resource.SpreadsheetResource;
67 import org.simantics.spreadsheet.solver.SheetNode;
68 import org.simantics.spreadsheet.solver.SpreadsheetBook;
69 import org.simantics.ui.selection.WorkbenchSelectionUtils;
70 import org.simantics.utils.datastructures.Pair;
71 import org.simantics.utils.strings.AlphanumComparator;
72 import org.simantics.utils.threads.logger.ITask;
73 import org.simantics.utils.threads.logger.ThreadLogger;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76
77 import gnu.trove.map.hash.THashMap;
78 import gnu.trove.map.hash.TObjectIntHashMap;
79
80 class FilteredVariableProperties extends UnaryRead<Variable, Collection<Pair<String,Variable>>> {
81
82         final static String CLASSIFICATION = SpreadsheetResource.URIs.Attribute;
83         
84     public FilteredVariableProperties(Variable variable) {
85         super(variable);
86     }
87
88     @Override
89     public Collection<Pair<String,Variable>> perform(ReadGraph graph) throws DatabaseException {
90         ArrayList<Pair<String,Variable>> result = new ArrayList<Pair<String,Variable>>();
91         for(Variable var : parameter.getProperties(graph, CLASSIFICATION)) {
92                 String name = var.getName(graph);
93                 String uri = var.getURI(graph);
94                 result.add(Pair.make(name, var));
95                 Variable expression = var.getPossibleProperty(graph, "expression");
96                 if(expression != null)
97                     result.add(Pair.make(name + "#expression", expression));
98                 Variable editable = var.getPossibleProperty(graph, "editable");
99             if(editable != null)
100                 result.add(Pair.make(name + "#editable", editable));
101         }
102         return result;
103     }
104
105 }
106
107 public class GraphUI implements Adaptable, ListenerSupport, AsyncListenerSupport, SyncListenerSupport {
108
109         private static final Logger LOGGER = LoggerFactory.getLogger(GraphUI.class);
110
111         final public static boolean DEBUG = false;
112         
113     final private RequestProcessor processor;
114     
115     private CellEditor<Write> cellEditor;
116     
117     private Variable run;
118     private ClientModel client;
119
120     private Map<String, PropertyListener> listenerCache = new THashMap<>();
121
122     public GraphUI(RequestProcessor processor) {
123         this.processor = processor;
124     }
125     
126     public void addCell(ReadGraph graph, Pair<String, Variable> child, final ClientModel client) throws DatabaseException {
127
128                 if(DEBUG) System.out.println("GraphUI adds cell  " + child.second.getURI(graph));
129
130         final String childName = child.second.getName(graph);
131         Boolean immutable = child.second.getPossiblePropertyValue(graph, "immutable", Bindings.BOOLEAN);
132         if(immutable != null && immutable) {
133                 Collection<Variable> properties = child.second.getProperties(graph, FilteredVariableProperties.CLASSIFICATION);
134                 addProperties(graph, properties, client, childName);
135         } else {
136             PropertyListener listener = listenerCache.get(child.first); 
137             if (listener == null) {
138                     listener = propertyListener(client, childName);
139                     listenerCache.put(child.first, listener);
140             }
141                 graph.syncRequest(new FilteredVariableProperties(child.second), listener);
142         }
143
144     }
145
146     public void removeCell(ReadGraph graph, Pair<String, Variable> child, final ClientModel client) throws DatabaseException {
147
148                 if(DEBUG) System.out.println("GraphUI removed cell " + child.first);
149                 
150                 client.clear(child.first);
151                 PropertyListener listener = listenerCache.remove(child.first);
152                 if (listener != null)
153                     listener.dispose();
154
155     }
156
157     public void loadCells(ReadGraph graph, Variable container, boolean immutable, final ClientModel client) throws DatabaseException {
158         
159                 if(DEBUG) System.out.println("GraphUI loads cells from " + container.getURI(graph));
160                 
161                 if(immutable) {
162                         for(Pair<String, Variable> cell : graph.syncRequest(new Cells(container), TransientCacheAsyncListener.<Collection<Pair<String, Variable>>>instance())) { 
163                                 addCell(graph, cell, client);
164                         }
165                 } else {
166                 graph.syncRequest(new Cells(container), new SingleSetSyncListenerDelegate<Pair<String, Variable>>(GraphUI.this) {
167         
168                     @Override
169                     public void add(ReadGraph graph, final Pair<String, Variable> child) throws DatabaseException {
170                         addCell(graph, child, client);
171                     }
172         
173                     @Override
174                     public void remove(ReadGraph graph, final Pair<String, Variable> child) throws DatabaseException {
175                         removeCell(graph, child, client);
176                     }
177                 });
178                 }
179         
180     }
181     
182     private SessionEventListenerAdapter listener;
183     
184     private String currentSource;
185
186     private boolean disposed;
187     
188     public Resource load(final Variable variable, final ClientModel client) throws DatabaseException {
189         
190 //        for (PropertyListener listener : listenerCache.values())
191 //            listener.dispose();
192 //        
193 //        listenerCache.clear();
194         
195         
196         
197         assert(variable != null);
198         
199         this.run = variable;
200         this.client = client;
201         
202         SessionEventSupport support = processor.getService(SessionEventSupport.class);
203         
204         for (PropertyListener listener : listenerCache.values()) {
205             listener.dispose();
206         }
207         listenerCache.clear();
208         
209         if(listener != null)
210                 support.removeListener(listener);
211         
212         listener = new SessionEventListenerAdapter() {
213                 
214                 @Override
215                 public void writeTransactionFinished() {
216                         client.flush();
217                 }
218                 
219         }; 
220         
221         support.addListener(listener);
222
223         this.cellEditor = processor.sync(new VariableRead<CellEditor<Write>>(variable) {
224
225                         @Override
226                         public CellEditor<Write> perform(ReadGraph graph) throws DatabaseException {
227                                 SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
228                                 return variable.getPropertyValue(graph, SHEET.cellEditor);
229                         }
230                 
231         });
232         
233                 final ITask task = ThreadLogger.getInstance().begin("GraphUI.init");
234
235                 client.clearAll();
236
237                 Map<String,Variable> sources = processor.syncRequest(new Sources(variable));
238
239                 List<String> sheetList = processor.syncRequest(new Sheets(variable));
240                 String currentSheet = processor.syncRequest(new VariableName(variable));
241                 
242                 Map<String, Resource> stateList = processor.syncRequest(new SpreadsheetStates(variable));
243
244                 if(currentSource == null) currentSource = "Sheet";
245                 
246                 ArrayList<String> sourceList = new ArrayList<String>(sources.keySet());
247                 
248                 Collections.sort(sourceList, AlphanumComparator.CASE_INSENSITIVE_COMPARATOR);
249                 if(!sourceList.contains(currentSource)) sourceList.add(currentSource);
250                 
251                 client.setProperty(ClientModel.SOURCES, ClientModel.SOURCES_AVAILABLE, sourceList.toArray(new String[sourceList.size()]));
252                 client.setProperty(ClientModel.SOURCES, ClientModel.SOURCES_CURRENT, currentSource);
253
254                 client.setProperty(ClientModel.SHEETS, ClientModel.SHEETS_AVAILABLE, sheetList.toArray(new String[sheetList.size()]));
255                 client.setProperty(ClientModel.SHEETS, ClientModel.SHEETS_CURRENT, currentSheet);
256                 
257                 client.setProperty(ClientModel.STATES, ClientModel.STATES_AVAILABLE, stateList.keySet().toArray(new String[stateList.size()]));
258                 
259                 client.setProperty(ClientModel.CONTEXT, ClientModel.CONTEXT_CURRENT, variable);
260                 
261                 client.setProperty(ClientModel.MODE, ClientModel.MODE_CURRENT, OperationMode.OPERATION);
262                 
263                 String currentState = processor.syncRequest(new UniqueRead<String>() {
264
265             @Override
266             public String perform(ReadGraph graph) throws DatabaseException {
267                 Resource book = variable.getParent(graph).getRepresents(graph);
268                 Resource ic = graph.getPossibleObject(book, SpreadsheetResource.getInstance(graph).Book_HasDefaultInitialCondition);
269                 if (ic == null)
270                         return "";
271                 return graph.getRelatedValue2(ic, Layer0.getInstance(graph).HasName, Bindings.STRING);
272             }
273         });
274                 
275                 client.setProperty(ClientModel.STATES, ClientModel.STATES_CURRENT, currentState);
276
277         processor.syncRequest(new ReadRequest() {
278
279             @Override
280             public void run(ReadGraph graph) throws DatabaseException {
281
282                 loadCells(graph, variable, false, client);
283
284                 graph.syncRequest(new Ranges(variable), new SingleSetSyncListenerDelegate<Variable>(GraphUI.this) {
285
286                     @Override
287                     public void add(ReadGraph graph, final Variable range) throws DatabaseException {
288
289                                 if(DEBUG) System.out.println("GraphUI adds range  " + range.getURI(graph));
290
291                         Boolean immutable = range.getPossiblePropertyValue(graph, "immutable", Bindings.BOOLEAN);
292                         loadCells(graph, range, immutable != null && immutable, client);
293
294                     }
295
296                     @Override
297                     public void remove(ReadGraph graph, final Variable range) throws DatabaseException {
298
299                     }
300                     
301                 });
302                 
303                 
304                 graph.syncRequest(new SheetLines(variable), new SingleSetSyncListenerDelegate<Variable>(GraphUI.this) {
305
306                     @Override
307                     public void add(ReadGraph graph, final Variable range) throws DatabaseException {
308
309                                 if(DEBUG) System.out.println("GraphUI adds line  " + range.getURI(graph));
310
311                         Boolean immutable = range.getPossiblePropertyValue(graph, "immutable", Bindings.BOOLEAN);
312                         loadCells(graph, range, immutable != null && immutable, client);
313
314                     }
315
316                     @Override
317                     public void remove(ReadGraph graph, final Variable range) throws DatabaseException {
318
319                     }
320                 });
321
322             }
323
324 //            @Override
325 //            public void remove(ReadGraph graph, Variable child) throws DatabaseException {
326 //
327 //              String location = locations.get(cellResource);
328 //              assert(location != null);
329 //
330 //              client.setProperty(location, "Label", null);
331 //              client.setProperty(location, "Expression", null);
332 //
333 //            }
334
335         });
336         
337
338                 task.finish();
339                 client.flush();
340                 
341                 return null;
342
343     }
344     
345     private static class PropertyListener extends SingleSetSyncListenerDelegate<Pair<String,Variable>> {
346
347         private static final Logger LOGGER = LoggerFactory.getLogger(PropertyListener.class);
348
349         private ClientModel client;
350         private String childName;
351         private boolean listenerDisposed;
352
353         public PropertyListener(SyncListenerSupport support, ClientModel client, String childName) {
354             super(support);
355             this.client = client;
356             this.childName = childName;
357         }
358         
359         @Override
360         public void add(ReadGraph graph, final Pair<String,Variable> property) throws DatabaseException {
361
362             if(DEBUG)
363                 System.out.println("GraphUI adds property  " + property.second.getURI(graph));
364
365             graph.syncRequest(new CellValue(property.second), new SyncListener<Object>() {
366
367                 @Override
368                 public void execute(ReadGraph graph, final Object value) throws DatabaseException {
369
370                     String propertyName = property.first;
371
372                     if(DEBUG)
373                         System.out.println("GraphUI detected content change(1) at  " + childName + " - " + propertyName + " -> " + value);
374                     client.setProperty(childName, propertyName, value);
375                     
376                 }
377
378                 @Override
379                 public void exception(ReadGraph graph, Throwable throwable) throws DatabaseException {
380                     
381                     LOGGER.error("PropertyListener.exception", throwable);
382                     
383                     String propertyName = property.first;
384                     if("content".equals(propertyName)) {
385                         if(throwable == null) throwable = new Exception();
386                         String message = throwable.getMessage();
387                         if(message == null) message = throwable.toString();
388                         client.setProperty(childName, propertyName, Variant.ofInstance(message));
389                     } else {
390                         client.setProperty(childName, propertyName, null);
391                     }
392                     
393                 }
394
395                 @Override
396                 public boolean isDisposed() {
397                     return listenerDisposed;
398                 }
399
400             });
401         }
402
403         public void dispose() {
404             listenerDisposed = true;
405         }
406         
407         @Override
408         public String toString() {
409             return super.toString() + ":" + childName;
410         }
411         
412     }
413     
414     private PropertyListener propertyListener(final ClientModel client, final String childName) {
415         return new PropertyListener(this, client, childName);
416     }
417     
418     private void addProperties(ReadGraph graph, final Collection<Variable> properties, final ClientModel client, final String childName) throws DatabaseException {
419
420         for(Variable property : properties) {
421                 
422                 if(DEBUG) System.out.println("GraphUI adds immutable property  " + property.getURI(graph));
423
424                 final String propertyName = property.getName(graph);
425                 
426                 Object value = property.getValue(graph);
427
428                 if(DEBUG) System.out.println("GraphUI detected change at  " + childName + " - " + propertyName + " -> " + value);
429                 client.setProperty(childName, propertyName, value);
430                 
431                 String expression = property.getPossiblePropertyValue(graph, "expression", Bindings.STRING);
432                 if(expression != null) {
433                         if(DEBUG) System.out.println("GraphUI detected change at  " + childName + " - " + (propertyName + "#expression") + " -> " + value);
434                         client.setProperty(childName, propertyName + "#expression", expression);
435                 }
436                 
437             Boolean editable = property.getPossiblePropertyValue(graph, "editable", Bindings.STRING);
438             if(editable != null) {
439                 if(DEBUG) System.out.println("GraphUI detected change at  " + childName + " - " + (propertyName + "#editable") + " -> " + value);
440                 client.setProperty(childName, propertyName + "#editable", editable);
441             }
442         
443         }
444
445     }
446     
447     @SuppressWarnings("unchecked")
448     @Override
449     public <T> T getAdapter(Class<T> clazz) {
450
451         if(Variable.class == clazz) {
452
453                 return (T)run;
454                 
455         } else if(RemoveCellHandler.class == clazz) {
456                 
457             return (T) new RemoveCellHandler() {
458
459                                 @Override
460                                 public void handle(final String location) {
461                                         
462                                         try {
463                                                 processor.syncRequest(new ReadRequest() {
464
465                                                         @Override
466                                                         public void run(ReadGraph graph) throws DatabaseException {
467
468                                                                 Variable cellVariable = run.getPossibleChild(graph, location);
469                                                                 if(cellVariable != null) {
470                                                                         final Resource config = cellVariable.getPossiblePropertyValue(graph, "Represents");
471                                                                         if(config != null) {
472
473                                                                                 graph.asyncRequest(new WriteRequest() {
474
475                                                                                         @Override
476                                                                                         public void perform(WriteGraph graph) throws DatabaseException {
477
478                                                                                                 Layer0 l0 = Layer0.getInstance(graph);
479 //                                                                                      SpreadsheetResource sr = SpreadsheetResource.getInstance(graph);
480                                                                                                 graph.deny(config, l0.PartOf);
481 //                                                                                      graph.deny(config, sr.RowOf);
482 //                                                                                      graph.deny(config, sr.ColumnOf);
483
484                                                                                         }
485
486                                                                                 });
487
488                                                                         }
489                                                                 }
490
491                                                         }
492
493                                                 });
494                                         } catch (DatabaseException e) {
495                                                 LOGGER.error("Unexpected exception while removing cell", e);
496                                         }
497                                         
498                                 }
499                 
500             };
501
502         } else if(CellEditor.class == clazz) {
503         
504                 return (T)new CellEditor<Write>() {
505
506                                 @Override
507                                 public <E> void edit(Transaction<Write> transaction, String location, String property, E value, Binding binding, Consumer<?> callback) {
508                                         
509                                     if (ClientModel.ITERATION_ENABLED.equals(location)) {
510                                         Simantics.getSession().asyncRequest(new ReadRequest() {
511                                                         @Override
512                                                         public void run(ReadGraph graph) throws DatabaseException {
513                                                                 getBook(graph).setIterationEnabled((boolean)value);
514                                                         }
515                                                 });
516                                         return;
517                                     }
518                                     
519                                     if (ClientModel.MODE.equals(location)) {
520                                         if (ClientModel.MODE_CURRENT.equals(property)) {
521                                             client.setProperty(location, property, value);
522                                             client.flush();
523                                             return;
524                                         }
525                                     }
526                                     
527                                     if (ClientModel.CONTEXT.equals(location)) {
528                                         if(ClientModel.CONTEXT_CURRENT.equals(property)) {
529                             if(value instanceof String) {
530                                 try {
531                                     Variable newContext = processor.syncRequest(new UnaryRead<String, Variable>((String)value) {
532
533                                         @Override
534                                         public Variable perform(ReadGraph graph) throws DatabaseException {
535                                       
536                                             String sheetName = run.getName(graph);
537                                             
538                                             Variable book = Variables.getContext(graph, run);
539                                             Resource bookResource = book.getRepresents(graph);
540                                             
541                                             Variable input = Variables.getVariable(graph, parameter);
542                                             Variable proxy = ProxyVariables.makeProxyVariable(graph, Variables.getVariable(graph, bookResource), input);
543                                             
544                                             return proxy.getChild(graph, sheetName);
545                                             
546 //                                            return variable.getParent(graph).getChild(graph, parameter);
547                                         }
548
549                                     });
550                                     
551                                     load(newContext, client);
552                                     return;
553                                 } catch (DatabaseException e) {
554                                     LOGGER.error("edit failed for model key '" + ClientModel.CONTEXT_CURRENT + "'", e);
555                                 }
556                             }
557                                         }
558                                     }
559                                     
560                                         if(ClientModel.SHEETS.equals(location)) {
561                                                 if(ClientModel.SHEETS_CURRENT.equals(property)) {
562                                                         
563                                                         if(value instanceof String) {
564
565                                                                 try {
566
567                                                                         Variable newInput = processor.syncRequest(new UnaryRead<String, Variable>((String)value) {
568
569                                                                                 @Override
570                                                                                 public Variable perform(ReadGraph graph) throws DatabaseException {
571                                                                                         return run.getParent(graph).getChild(graph, parameter);
572                                                                                 }
573
574                                                                         });
575
576                                                                         load(newInput, client);
577                                                                         return;
578                                                                 } catch (DatabaseException e) {
579                                                                         LOGGER.error("edit failed for model key '" + ClientModel.SHEETS_CURRENT + "'", e);
580                                                                 }
581                                                         }
582                                                 }
583                                         }
584                                         
585                    if(ClientModel.STATES.equals(location)) {
586                         if(ClientModel.STATES_CURRENT.equals(property)) {
587                             if(value instanceof String) {
588                                 final String parameter = (String) value;
589                                 try {
590                                     
591                                     String uri = processor.syncRequest(new WriteResultRequest<String>() {
592                 
593                                         @Override
594                                         public String perform(WriteGraph graph) throws DatabaseException {
595                                             
596                                             Map<String, Resource> states = graph.syncRequest(new SpreadsheetStates(run));
597                                             
598                                             Resource state = null;
599                                             for (Map.Entry<String, Resource> entry : states.entrySet()) {
600                                                 if (entry.getKey().equals(parameter)) {
601                                                     state = entry.getValue();
602                                                     break;
603                                                 }
604                                             }
605                                             if (state != null) {
606                                                 Variable context = Variables.getContext(graph, run);
607                                                 Resource bookResource = context.getRepresents(graph);
608                                                 SpreadsheetGraphUtils.setDefaultInitialConditionForBook(graph, bookResource, state);
609                                                 
610                                                 String contextURI = context.getURI(graph);
611                                                 
612                                                 String sessionName = context.getParent(graph).getURI(graph);
613                                                 SpreadsheetSessionManager.getInstance().removeRealm(graph, sessionName);
614                                                 SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
615                                             }
616                                             
617                                             return run.getURI(graph);
618                                         }
619                                     });
620                                     Variable newInput = processor.syncRequest(new PossibleURIVariable(uri));
621                                     load(newInput, client);
622 //                                    fullSynchronize();
623                                     return;
624                                 } catch (DatabaseException e) {
625                                     LOGGER.error("edit failed for model key '" + ClientModel.STATES_CURRENT + "'", e);
626                                 }
627                             }
628                         }
629                     }
630
631                                         if(ClientModel.SOURCES.equals(location)) {
632                                                 if(ClientModel.SOURCES_CURRENT.equals(property)) {
633                                                         try {
634                                                                 Resource res = WorkbenchSelectionUtils.getPossibleResource(value);
635                                                                 if(res != null) {
636                                                                         
637                                                                         Variable newInput = processor.syncRequest(new ResourceRead<Variable>(res) {
638
639                                                                                 @Override
640                                                                                 public Variable perform(ReadGraph graph) throws DatabaseException {
641                                                                                         Variable base = ProxyVariables.proxyVariableBase(graph, run);
642                                                                                         Variable in = Variables.getVariable(graph, resource);
643                                                                                         currentSource = in.getURI(graph);
644                                                                                         return ProxyVariables.makeProxyVariable(graph, base, in);
645                                                                                 }
646                                                 
647                                                                         });
648                                                                         
649                                                                         load(newInput, client);
650                                                                         
651                                                                         return;
652
653                                                                 } else if(value instanceof String) {
654                                                                         
655                                                                         Variable newInput = processor.syncRequest(new UnaryRead<String, Variable>((String)value) {
656
657                                                                                 @Override
658                                                                                 public Variable perform(ReadGraph graph) throws DatabaseException {
659                                                                                         
660                                                                                         Variable base = ProxyVariables.proxyVariableBase(graph, run);
661                                                                                         Map<String,Variable> sources = graph.syncRequest(new Sources(base));
662
663                                                                                         Variable found = sources.get(parameter);
664                                                                                         if(found == null) return null;
665                                                                                         
666                                                                                         currentSource = parameter;
667
668                                                                                         return ProxyVariables.makeProxyVariable(graph, base, found);
669                                                                                         
670                                                                                 }
671                                                 
672                                                                         });
673                                                                         
674                                                                         load(newInput, client);
675                                                                         return;
676                                                                 }
677                                                                 
678                                                         } catch (DatabaseException e) {
679                                                                 LOGGER.error("edit failed for model key '" + ClientModel.SOURCES_CURRENT + "'", e);
680                                                         }
681                                                 }
682                                                 return;
683                                         }
684                                         boolean needsCommit = false;
685                                         if (transaction == null) {
686                                             OperationMode mode = client.getPropertyAt(ClientModel.MODE, ClientModel.MODE_CURRENT);
687                                             transaction = startTransaction(mode);
688 //                                          if (mode.equals(OperationMode.OPERATION))
689                                         transaction.setContext(run);
690                                             needsCommit = true;
691                                         }
692                                         final Transaction<Write> finalTransaction = transaction;
693                                         cellEditor.edit(transaction, location, property, value, binding, new Consumer<Object>() {
694                         
695                         @Override
696                         public void accept(Object param) {
697                             if (finalTransaction.needSynchronization() != null)
698                                 synchronize(finalTransaction.needSynchronization());
699                         }
700                                         });
701                                         if (needsCommit)
702                                             transaction.commit();
703                                 }
704
705                                 @Override
706                                 public void edit(Transaction<Write> transaction, String location, Variant variant, Consumer<?> callback) {
707                                     boolean needsCommit = false;
708                                     if (transaction == null) {
709                                         OperationMode mode = client.getPropertyAt(ClientModel.MODE, ClientModel.MODE_CURRENT);
710                                         transaction = startTransaction(mode);
711 //                                      if (mode.equals(OperationMode.OPERATION))
712                                     transaction.setContext(run);
713                                         needsCommit = true;
714                                     }
715                                     final Transaction<Write> finalTransaction = transaction;
716                                         cellEditor.edit(transaction, location, variant, new Consumer<Object>() {
717                         
718                         @Override
719                         public void accept(Object param) {
720                             if (finalTransaction.needSynchronization() != null)
721                                 synchronize(finalTransaction.needSynchronization());
722                         }
723                     });
724                                         if (needsCommit)
725                                             transaction.commit();
726                                 }
727
728                                 @Override
729                                 public void copy(final Transaction<Write> transaction, String location, MutableVariant variant, Consumer<?> callback) {
730                                         cellEditor.edit(transaction, location, variant, new Consumer<Object>() {
731                         
732                         @Override
733                         public void accept(Object param) {
734                             if (transaction.needSynchronization() != null)
735                                 synchronize(transaction.needSynchronization());
736                         }
737                                         });
738                                 }
739
740                                 @Override
741                                 public Transaction<Write> startTransaction(OperationMode mode) {
742                                         return cellEditor.startTransaction(mode);
743                                 }
744                                 
745                 };
746                 
747         } else if (SheetCommands.class == clazz ) {
748             
749             return (T) new SheetCommands() {
750                 
751                 @Override
752                 public void saveState() {
753                     
754                     Simantics.getSession().asyncRequest(new ReadRequest() {
755                         
756                         @Override
757                         public void run(ReadGraph graph) throws DatabaseException {
758                             IEclipseContext context = PlatformUI.getWorkbench().getService(IEclipseContext.class);
759                             
760                             Resource uiContextResource = run.getRepresents(graph);
761                             Resource bookResource = Variables.getContext(graph, run).getRepresents(graph);
762                             Layer0 L0 = Layer0.getInstance(graph);
763                             String uiContextName = graph.getRelatedValue2(uiContextResource, L0.HasName, Bindings.STRING);
764                             String bookName = graph.getRelatedValue2(bookResource, L0.HasName, Bindings.STRING);
765                             
766                             UISynchronize synchronizer = context.get(UISynchronize.class);
767                             synchronizer.asyncExec(() -> {
768                                 Pair<String, Resource>[] pairs = new Pair[] {Pair.make(uiContextName, uiContextResource), Pair.make(bookName, bookResource) };
769                                 SaveSpreadsheetStateDialog dialog = new SaveSpreadsheetStateDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor().getSite(), "Save Spreadsheet state", pairs);
770                                 if (dialog.open() == Dialog.OK) {
771                                     Object[] result = dialog.getSelection();
772                                     if (result != null) {
773                                         Pair<Resource, String> p = (Pair<Resource, String>) result[0];
774                                         Simantics.getSession().asyncRequest(new WriteRequest() {
775                                             
776                                             @Override
777                                             public void perform(WriteGraph graph) throws DatabaseException {
778                                                 
779                                                 Variable parent = run.getParent(graph);
780                                                 Variable base = ProxyVariables.proxyVariableBase(graph, parent);
781                                                 SpreadsheetGraphUtils.saveInitialCondition(graph, parent, p.first, p.second);
782                                             }
783                                         });
784                                     }
785                                 } else {
786                                     return;
787                                 }
788                             });
789                         }
790                     });
791                 }
792             };
793         }
794
795         return null;
796
797     }
798
799     @Override
800     public void exception(Throwable t) {
801         LOGGER.error("Failed to read properties.", t);
802     }
803
804     @Override
805     public boolean isDisposed() {
806         return disposed;
807     }
808
809     @Override
810     public void exception(AsyncReadGraph graph, Throwable t) {
811         LOGGER.error("Failed to read properties.", t);
812     }
813
814     @Override
815     public void exception(ReadGraph graph, Throwable t) {
816         LOGGER.error("Failed to read properties.", t);
817     }
818
819     public void dispose() {
820         for (PropertyListener listener : listenerCache.values())
821             listener.dispose();
822         
823         listenerCache.clear();
824         SessionEventSupport support = processor.getService(SessionEventSupport.class);
825         support.removeListener(listener);
826         disposed = true;
827     }
828     
829     private void synchronize(List<Object> list) {
830         Simantics.getSession().asyncRequest(new FullSynchronizeBook(run, list));
831     }
832     
833     public static class FullSynchronizeBook extends ReadRequest {
834
835         private final Variable run;
836         private final List<Object> location;
837         
838         public FullSynchronizeBook(Variable run, List<Object> cellLocation) {
839             this.run = run;
840             this.location = cellLocation;
841         }
842         
843         @Override
844         public void run(ReadGraph graph) throws DatabaseException {
845             String uri = run.getURI(graph);
846             String parentUri = run.getParent(graph).getURI(graph);
847             System.err.println("Full sync for book " + parentUri);
848             
849             Resource sheetResource = run.getRepresents(graph);
850             Variable sheetVariable = Variables.getVariable(graph, sheetResource);
851             
852             TObjectIntHashMap<Variable> changes = null;
853             if (location != null) {
854                 changes = new TObjectIntHashMap<>(location.size());
855                 for (Object loc : location) {
856                     Variable var = (Variable) loc;
857                     changes.put(var, 1);
858                 };
859             }
860             SpreadsheetGraphUtils.partialSynchronization(graph, run.getParent(graph), changes);
861         }
862         
863     }
864     
865     private SpreadsheetBook getBook(ReadGraph graph) throws DatabaseException {
866         String sessionName = run.getParent(graph).getParent(graph).getURI(graph);
867         StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
868         SpreadsheetBook book = realm.getEngine();
869         return book;
870     }
871     
872 }