]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/GraphUI.java
Multiple reader thread support for db client
[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.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.syncRequest(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(SyncListenerSupport 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.syncRequest(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                                         try {
460                                                 processor.syncRequest(new ReadRequest() {
461
462                                                         @Override
463                                                         public void run(ReadGraph graph) throws DatabaseException {
464
465                                                                 Variable cellVariable = run.getPossibleChild(graph, location);
466                                                                 if(cellVariable != null) {
467                                                                         final Resource config = cellVariable.getPossiblePropertyValue(graph, "Represents");
468                                                                         if(config != null) {
469
470                                                                                 graph.asyncRequest(new WriteRequest() {
471
472                                                                                         @Override
473                                                                                         public void perform(WriteGraph graph) throws DatabaseException {
474
475                                                                                                 Layer0 l0 = Layer0.getInstance(graph);
476 //                                                                                      SpreadsheetResource sr = SpreadsheetResource.getInstance(graph);
477                                                                                                 graph.deny(config, l0.PartOf);
478 //                                                                                      graph.deny(config, sr.RowOf);
479 //                                                                                      graph.deny(config, sr.ColumnOf);
480
481                                                                                         }
482
483                                                                                 });
484
485                                                                         }
486                                                                 }
487
488                                                         }
489
490                                                 });
491                                         } catch (DatabaseException e) {
492                                                 LOGGER.error("Unexpected exception while removing cell", e);
493                                         }
494                                         
495                                 }
496                 
497             };
498
499         } else if(CellEditor.class == clazz) {
500         
501                 return (T)new CellEditor<Write>() {
502
503                                 @Override
504                                 public <E> void edit(Transaction<Write> transaction, String location, String property, E value, Binding binding, Consumer<?> callback) {
505                                         
506                                     if (ClientModel.ITERATION_ENABLED.equals(location)) {
507                                         Simantics.getSession().asyncRequest(new ReadRequest() {
508                                                         @Override
509                                                         public void run(ReadGraph graph) throws DatabaseException {
510                                                                 getBook(graph).setIterationEnabled((boolean)value);
511                                                         }
512                                                 });
513                                         return;
514                                     }
515                                     
516                                     if (ClientModel.MODE.equals(location)) {
517                                         if (ClientModel.MODE_CURRENT.equals(property)) {
518                                             client.setProperty(location, property, value);
519                                             client.flush();
520                                             return;
521                                         }
522                                     }
523                                     
524                                     if (ClientModel.CONTEXT.equals(location)) {
525                                         if(ClientModel.CONTEXT_CURRENT.equals(property)) {
526                             if(value instanceof String) {
527                                 try {
528                                     Variable newContext = processor.syncRequest(new UnaryRead<String, Variable>((String)value) {
529
530                                         @Override
531                                         public Variable perform(ReadGraph graph) throws DatabaseException {
532                                       
533                                             String sheetName = run.getName(graph);
534                                             
535                                             Variable book = Variables.getContext(graph, run);
536                                             Resource bookResource = book.getRepresents(graph);
537                                             
538                                             Variable input = Variables.getVariable(graph, parameter);
539                                             Variable proxy = ProxyVariables.makeProxyVariable(graph, Variables.getVariable(graph, bookResource), input);
540                                             
541                                             return proxy.getChild(graph, sheetName);
542                                             
543 //                                            return variable.getParent(graph).getChild(graph, parameter);
544                                         }
545
546                                     });
547                                     
548                                     load(newContext, client);
549                                     return;
550                                 } catch (DatabaseException e) {
551                                     LOGGER.error("edit failed for model key '" + ClientModel.CONTEXT_CURRENT + "'", e);
552                                 }
553                             }
554                                         }
555                                     }
556                                     
557                                         if(ClientModel.SHEETS.equals(location)) {
558                                                 if(ClientModel.SHEETS_CURRENT.equals(property)) {
559                                                         
560                                                         if(value instanceof String) {
561
562                                                                 try {
563
564                                                                         Variable newInput = processor.syncRequest(new UnaryRead<String, Variable>((String)value) {
565
566                                                                                 @Override
567                                                                                 public Variable perform(ReadGraph graph) throws DatabaseException {
568                                                                                         return run.getParent(graph).getChild(graph, parameter);
569                                                                                 }
570
571                                                                         });
572
573                                                                         load(newInput, client);
574                                                                         return;
575                                                                 } catch (DatabaseException e) {
576                                                                         LOGGER.error("edit failed for model key '" + ClientModel.SHEETS_CURRENT + "'", e);
577                                                                 }
578                                                         }
579                                                 }
580                                         }
581                                         
582                    if(ClientModel.STATES.equals(location)) {
583                         if(ClientModel.STATES_CURRENT.equals(property)) {
584                             if(value instanceof String) {
585                                 final String parameter = (String) value;
586                                 try {
587                                     
588                                     String uri = processor.syncRequest(new WriteResultRequest<String>() {
589                 
590                                         @Override
591                                         public String perform(WriteGraph graph) throws DatabaseException {
592                                             
593                                             Map<String, Resource> states = graph.syncRequest(new SpreadsheetStates(run));
594                                             
595                                             Resource state = null;
596                                             for (Map.Entry<String, Resource> entry : states.entrySet()) {
597                                                 if (entry.getKey().equals(parameter)) {
598                                                     state = entry.getValue();
599                                                     break;
600                                                 }
601                                             }
602                                             if (state != null) {
603                                                 Variable context = Variables.getContext(graph, run);
604                                                 Resource bookResource = context.getRepresents(graph);
605                                                 SpreadsheetGraphUtils.setDefaultInitialConditionForBook(graph, bookResource, state);
606                                                 
607                                                 String contextURI = context.getURI(graph);
608                                                 
609                                                 String sessionName = context.getParent(graph).getURI(graph);
610                                                 SpreadsheetSessionManager.getInstance().removeRealm(graph, sessionName);
611                                                 SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
612                                             }
613                                             
614                                             return run.getURI(graph);
615                                         }
616                                     });
617                                     Variable newInput = processor.syncRequest(new PossibleURIVariable(uri));
618                                     load(newInput, client);
619 //                                    fullSynchronize();
620                                     return;
621                                 } catch (DatabaseException e) {
622                                     LOGGER.error("edit failed for model key '" + ClientModel.STATES_CURRENT + "'", e);
623                                 }
624                             }
625                         }
626                     }
627
628                                         if(ClientModel.SOURCES.equals(location)) {
629                                                 if(ClientModel.SOURCES_CURRENT.equals(property)) {
630                                                         try {
631                                                                 Resource res = WorkbenchSelectionUtils.getPossibleResource(value);
632                                                                 if(res != null) {
633                                                                         
634                                                                         Variable newInput = processor.syncRequest(new ResourceRead<Variable>(res) {
635
636                                                                                 @Override
637                                                                                 public Variable perform(ReadGraph graph) throws DatabaseException {
638                                                                                         Variable base = ProxyVariables.proxyVariableBase(graph, run);
639                                                                                         Variable in = Variables.getVariable(graph, resource);
640                                                                                         currentSource = in.getURI(graph);
641                                                                                         return ProxyVariables.makeProxyVariable(graph, base, in);
642                                                                                 }
643                                                 
644                                                                         });
645                                                                         
646                                                                         load(newInput, client);
647                                                                         
648                                                                         return;
649
650                                                                 } else if(value instanceof String) {
651                                                                         
652                                                                         Variable newInput = processor.syncRequest(new UnaryRead<String, Variable>((String)value) {
653
654                                                                                 @Override
655                                                                                 public Variable perform(ReadGraph graph) throws DatabaseException {
656                                                                                         
657                                                                                         Variable base = ProxyVariables.proxyVariableBase(graph, run);
658                                                                                         Map<String,Variable> sources = graph.syncRequest(new Sources(base));
659
660                                                                                         Variable found = sources.get(parameter);
661                                                                                         if(found == null) return null;
662                                                                                         
663                                                                                         currentSource = parameter;
664
665                                                                                         return ProxyVariables.makeProxyVariable(graph, base, found);
666                                                                                         
667                                                                                 }
668                                                 
669                                                                         });
670                                                                         
671                                                                         load(newInput, client);
672                                                                         return;
673                                                                 }
674                                                                 
675                                                         } catch (DatabaseException e) {
676                                                                 LOGGER.error("edit failed for model key '" + ClientModel.SOURCES_CURRENT + "'", e);
677                                                         }
678                                                 }
679                                                 return;
680                                         }
681                                         boolean needsCommit = false;
682                                         if (transaction == null) {
683                                             OperationMode mode = client.getPropertyAt(ClientModel.MODE, ClientModel.MODE_CURRENT);
684                                             transaction = startTransaction(mode);
685 //                                          if (mode.equals(OperationMode.OPERATION))
686                                         transaction.setContext(run);
687                                             needsCommit = true;
688                                         }
689                                         final Transaction<Write> finalTransaction = transaction;
690                                         cellEditor.edit(transaction, location, property, value, binding, new Consumer<Object>() {
691                         
692                         @Override
693                         public void accept(Object param) {
694                             if (finalTransaction.needSynchronization() != null)
695                                 synchronize(finalTransaction.needSynchronization());
696                         }
697                                         });
698                                         if (needsCommit)
699                                             transaction.commit();
700                                 }
701
702                                 @Override
703                                 public void edit(Transaction<Write> transaction, String location, Variant variant, Consumer<?> callback) {
704                                     boolean needsCommit = false;
705                                     if (transaction == null) {
706                                         OperationMode mode = client.getPropertyAt(ClientModel.MODE, ClientModel.MODE_CURRENT);
707                                         transaction = startTransaction(mode);
708 //                                      if (mode.equals(OperationMode.OPERATION))
709                                     transaction.setContext(run);
710                                         needsCommit = true;
711                                     }
712                                     final Transaction<Write> finalTransaction = transaction;
713                                         cellEditor.edit(transaction, location, variant, new Consumer<Object>() {
714                         
715                         @Override
716                         public void accept(Object param) {
717                             if (finalTransaction.needSynchronization() != null)
718                                 synchronize(finalTransaction.needSynchronization());
719                         }
720                     });
721                                         if (needsCommit)
722                                             transaction.commit();
723                                 }
724
725                                 @Override
726                                 public void copy(final Transaction<Write> transaction, String location, MutableVariant variant, Consumer<?> callback) {
727                                         cellEditor.edit(transaction, location, variant, new Consumer<Object>() {
728                         
729                         @Override
730                         public void accept(Object param) {
731                             if (transaction.needSynchronization() != null)
732                                 synchronize(transaction.needSynchronization());
733                         }
734                                         });
735                                 }
736
737                                 @Override
738                                 public Transaction<Write> startTransaction(OperationMode mode) {
739                                         return cellEditor.startTransaction(mode);
740                                 }
741                                 
742                 };
743                 
744         } else if (SheetCommands.class == clazz ) {
745             
746             return (T) new SheetCommands() {
747                 
748                 @Override
749                 public void saveState() {
750                     
751                     Simantics.getSession().asyncRequest(new ReadRequest() {
752                         
753                         @Override
754                         public void run(ReadGraph graph) throws DatabaseException {
755                             IEclipseContext context = PlatformUI.getWorkbench().getService(IEclipseContext.class);
756                             
757                             Resource uiContextResource = run.getRepresents(graph);
758                             Resource bookResource = Variables.getContext(graph, run).getRepresents(graph);
759                             Layer0 L0 = Layer0.getInstance(graph);
760                             String uiContextName = graph.getRelatedValue2(uiContextResource, L0.HasName, Bindings.STRING);
761                             String bookName = graph.getRelatedValue2(bookResource, L0.HasName, Bindings.STRING);
762                             
763                             UISynchronize synchronizer = context.get(UISynchronize.class);
764                             synchronizer.asyncExec(() -> {
765                                 Pair<String, Resource>[] pairs = new Pair[] {Pair.make(uiContextName, uiContextResource), Pair.make(bookName, bookResource) };
766                                 SaveSpreadsheetStateDialog dialog = new SaveSpreadsheetStateDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor().getSite(), "Save Spreadsheet state", pairs);
767                                 if (dialog.open() == Dialog.OK) {
768                                     Object[] result = dialog.getSelection();
769                                     if (result != null) {
770                                         Pair<Resource, String> p = (Pair<Resource, String>) result[0];
771                                         Simantics.getSession().asyncRequest(new WriteRequest() {
772                                             
773                                             @Override
774                                             public void perform(WriteGraph graph) throws DatabaseException {
775                                                 
776                                                 Variable parent = run.getParent(graph);
777                                                 Variable base = ProxyVariables.proxyVariableBase(graph, parent);
778                                                 SpreadsheetGraphUtils.saveInitialCondition(graph, parent, p.first, p.second);
779                                             }
780                                         });
781                                     }
782                                 } else {
783                                     return;
784                                 }
785                             });
786                         }
787                     });
788                 }
789             };
790         }
791
792         return null;
793
794     }
795
796     @Override
797     public void exception(Throwable t) {
798         LOGGER.error("Failed to read properties.", t);
799     }
800
801     @Override
802     public boolean isDisposed() {
803         return disposed;
804     }
805
806     @Override
807     public void exception(AsyncReadGraph graph, Throwable t) {
808         LOGGER.error("Failed to read properties.", t);
809     }
810
811     @Override
812     public void exception(ReadGraph graph, Throwable t) {
813         LOGGER.error("Failed to read properties.", t);
814     }
815
816     public void dispose() {
817         for (PropertyListener listener : listenerCache.values())
818             listener.dispose();
819         
820         listenerCache.clear();
821         SessionEventSupport support = processor.getService(SessionEventSupport.class);
822         support.removeListener(listener);
823         disposed = true;
824     }
825     
826     private void synchronize(List<Object> list) {
827         Simantics.getSession().asyncRequest(new FullSynchronizeBook(run, list));
828     }
829     
830     public static class FullSynchronizeBook extends ReadRequest {
831
832         private final Variable run;
833         private final List<Object> location;
834         
835         public FullSynchronizeBook(Variable run, List<Object> cellLocation) {
836             this.run = run;
837             this.location = cellLocation;
838         }
839         
840         @Override
841         public void run(ReadGraph graph) throws DatabaseException {
842             String uri = run.getURI(graph);
843             String parentUri = run.getParent(graph).getURI(graph);
844             System.err.println("Full sync for book " + parentUri);
845             
846             Resource sheetResource = run.getRepresents(graph);
847             Variable sheetVariable = Variables.getVariable(graph, sheetResource);
848             
849             TObjectIntHashMap<Variable> changes = null;
850             if (location != null) {
851                 changes = new TObjectIntHashMap<>(location.size());
852                 for (Object loc : location) {
853                     Variable var = (Variable) loc;
854                     changes.put(var, 1);
855                 };
856             }
857             SpreadsheetGraphUtils.partialSynchronization(graph, run.getParent(graph), changes);
858         }
859         
860     }
861     
862     private SpreadsheetBook getBook(ReadGraph graph) throws DatabaseException {
863         String sessionName = run.getParent(graph).getParent(graph).getURI(graph);
864         StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
865         SpreadsheetBook book = realm.getEngine();
866         return book;
867     }
868     
869 }