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