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