]> gerrit.simantics Code Review - simantics/interop.git/blob - org.simantics.interop.update/src/org/simantics/interop/update/editor/ModelUpdateEditor.java
Improve matching logic for three way UpdateTree nodes.
[simantics/interop.git] / org.simantics.interop.update / src / org / simantics / interop / update / editor / ModelUpdateEditor.java
1 package org.simantics.interop.update.editor;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Stack;
8
9 import org.eclipse.jface.resource.ImageDescriptor;
10 import org.eclipse.jface.resource.JFaceResources;
11 import org.eclipse.jface.resource.LocalResourceManager;
12 import org.eclipse.jface.viewers.CellEditor;
13 import org.eclipse.jface.viewers.CheckStateChangedEvent;
14 import org.eclipse.jface.viewers.CheckboxCellEditor;
15 import org.eclipse.jface.viewers.CheckboxTreeViewer;
16 import org.eclipse.jface.viewers.ColumnLabelProvider;
17 import org.eclipse.jface.viewers.ColumnViewer;
18 import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
19 import org.eclipse.jface.viewers.EditingSupport;
20 import org.eclipse.jface.viewers.ICheckStateListener;
21 import org.eclipse.jface.viewers.IStructuredContentProvider;
22 import org.eclipse.jface.viewers.ITreeContentProvider;
23 import org.eclipse.jface.viewers.ITreeViewerListener;
24 import org.eclipse.jface.viewers.TableViewer;
25 import org.eclipse.jface.viewers.TableViewerColumn;
26 import org.eclipse.jface.viewers.TreeExpansionEvent;
27 import org.eclipse.jface.viewers.TreeViewerColumn;
28 import org.eclipse.jface.viewers.Viewer;
29 import org.eclipse.swt.SWT;
30 import org.eclipse.swt.events.SelectionAdapter;
31 import org.eclipse.swt.events.SelectionEvent;
32 import org.eclipse.swt.graphics.Color;
33 import org.eclipse.swt.graphics.Image;
34 import org.eclipse.swt.graphics.RGB;
35 import org.eclipse.swt.layout.FillLayout;
36 import org.eclipse.swt.layout.GridData;
37 import org.eclipse.swt.layout.GridLayout;
38 import org.eclipse.swt.widgets.Button;
39 import org.eclipse.swt.widgets.Composite;
40 import org.eclipse.swt.widgets.Display;
41 import org.eclipse.swt.widgets.Label;
42 import org.eclipse.swt.widgets.Text;
43 import org.simantics.Simantics;
44 import org.simantics.db.ReadGraph;
45 import org.simantics.db.Resource;
46 import org.simantics.db.Session;
47 import org.simantics.db.Statement;
48 import org.simantics.db.WriteGraph;
49 import org.simantics.db.common.request.WriteRequest;
50 import org.simantics.db.exception.DatabaseException;
51 import org.simantics.db.request.Read;
52 import org.simantics.interop.test.GraphChanges;
53 import org.simantics.interop.update.Activator;
54 import org.simantics.interop.update.model.ModelUpdate;
55 import org.simantics.interop.update.model.ModelUpdate.WarningListener;
56 import org.simantics.interop.update.model.PropertyChange;
57 import org.simantics.interop.update.model.UpdateList;
58 import org.simantics.interop.update.model.UpdateNode;
59 import org.simantics.interop.update.model.UpdateOp;
60 import org.simantics.interop.update.model.UpdateStatus;
61 import org.simantics.interop.update.model.UpdateTree;
62 import org.simantics.interop.utils.TableUtils;
63 import org.simantics.utils.datastructures.Pair;
64 import org.simantics.utils.ui.ExceptionUtils;
65
66
67 /**
68  * Editor for updating models.
69  * 
70  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
71  *
72  */
73 public abstract class ModelUpdateEditor extends Composite implements WarningListener{
74
75         private Composite errorComposite;
76         
77         private CheckboxTreeViewer changeBrowser;
78         private TableViewer changeViewer;
79         
80         private Button updateAllButton;
81         private Button updateSelectedButton;
82         
83         private LocalResourceManager manager = new LocalResourceManager(JFaceResources.getResources());
84         
85         private Image checked;
86         private Image unchecked;
87         private Image warning;
88         
89         private Color containsColor;
90         private Color deletedColor;
91         private Color addedColor;
92
93         private ModelUpdate update;
94         
95         
96         private HashSet<UpdateNode> selectedStructure = new HashSet<UpdateNode>();
97         
98         public ModelUpdateEditor(Composite parent) {
99                 super(parent,SWT.NONE);
100                 checked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/tick.png"));
101                 unchecked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/cross.png"));
102                 warning = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/error.png"));
103
104                 
105                 containsColor = new Color(parent.getDisplay(), new RGB(255,255,220));
106                 deletedColor = new Color(parent.getDisplay(), new RGB(255,220,220));
107                 addedColor = new Color(parent.getDisplay(), new RGB(220,255,220));
108                 
109                 this.setLayout(new GridLayout(1,false));
110                 
111                 errorComposite = new Composite(this, SWT.BORDER);
112                 GridData data = new GridData();
113                 data.grabExcessHorizontalSpace = true;
114                 data.grabExcessVerticalSpace = false;
115                 data.horizontalAlignment = SWT.FILL;
116                 data.verticalAlignment = SWT.TOP;
117                 errorComposite.setLayoutData(data);
118                 errorComposite.setLayout(new GridLayout(2, false));
119                 
120                 errorComposite.setVisible(false);
121
122 //              IEditorInput input = getEditorInput();
123 //              if (!(input instanceof UpdateEditorInput)) {
124 //                      Label label = new Label(composite, SWT.NONE);
125 //                      label.setText("Unknown input.");
126 //                      return;
127 //              }
128                 
129                 Composite fillComposite = new Composite(this, SWT.NONE);
130                 data = new GridData();
131                 data.grabExcessHorizontalSpace = true;
132                 data.grabExcessVerticalSpace = true;
133                 data.horizontalAlignment = SWT.FILL;
134                 data.verticalAlignment = SWT.FILL;
135                 fillComposite.setLayoutData(data);
136                 fillComposite.setLayout(new FillLayout(SWT.VERTICAL));
137
138                 {
139                         changeBrowser = new CheckboxTreeViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION  );
140                         
141                         changeBrowser.setContentProvider(new UpdateTreeContentProvider());
142                         
143                         changeBrowser.getTree().setHeaderVisible(true);
144                         
145                         ColumnViewerToolTipSupport.enableFor(changeBrowser);
146
147                         
148                         TreeViewerColumn dataColumn = TableUtils.addColumn(changeBrowser, "Data", true, 600);
149
150                         dataColumn.setLabelProvider(new UpdateNodeLabelProvider());
151                         
152                         changeBrowser.addCheckStateListener(new ICheckStateListener() {
153                                 
154                                 @Override
155                                 public void checkStateChanged(CheckStateChangedEvent event) {
156                                         UpdateNode node = (UpdateNode) event.getElement();
157                                         if (node.getOp() != null) {
158                                                 node.getOp().select(Boolean.TRUE.equals(event.getChecked()));
159                                                 
160                                         }
161                                         refreshChecked();
162                                         
163                                 }
164                         });
165                         changeBrowser.addTreeListener(new ITreeViewerListener() {
166                                 
167                                 @Override
168                                 public void treeExpanded(TreeExpansionEvent event) {
169                                         event.getTreeViewer().getControl().getDisplay().asyncExec(new Runnable() {
170                                                 
171                                                 @Override
172                                                 public void run() {
173                                                         // TreeViewer uses lazy load, checked states must be updated when the tree is expanded. 
174                                                         refreshChecked();
175                                                 }
176                                         });
177                                         
178                                 }
179                                 
180                                 @Override
181                                 public void treeCollapsed(TreeExpansionEvent event) {
182                                         
183                                 }
184                         });
185                         changeBrowser.setUseHashlookup(true);
186                         
187                 }
188                 {
189                         changeViewer = new TableViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION);
190                         
191                         changeViewer.getTable().setHeaderVisible(true);
192                         changeViewer.getTable().setLinesVisible(true);
193                         
194                         changeViewer.setContentProvider(new ModificationListContentProvider());
195                         
196                         changeViewer.setUseHashlookup(true);
197                         
198                         TableViewerColumn selection = TableUtils.addColumn(changeViewer, getColumntTitle(0), false, false, 20);
199                         TableViewerColumn diagram = TableUtils.addColumn(changeViewer, getColumntTitle(1), true, true, 100);
200                         TableViewerColumn symbol = TableUtils.addColumn(changeViewer, getColumntTitle(2), true, true, 100);
201                         TableViewerColumn property = TableUtils.addColumn(changeViewer, getColumntTitle(3), true, true, 100);
202                         TableViewerColumn oldValue = TableUtils.addColumn(changeViewer, getColumntTitle(4), true, true, 100);
203                         TableViewerColumn newValue = TableUtils.addColumn(changeViewer, getColumntTitle(5), true, true, 100);
204                         
205                         diagram.setLabelProvider(getLabelProvider(1));
206                         symbol.setLabelProvider(getLabelProvider(2));
207                         property.setLabelProvider(getLabelProvider(3));
208                         oldValue.setLabelProvider(getLabelProvider(4));
209                         newValue.setLabelProvider(getLabelProvider(5));
210                         
211                         selection.setLabelProvider(new SelectionLabelProvider());
212                         selection.getColumn().addSelectionListener(new SelectionAdapter() {
213                                 @Override
214                                 public void widgetSelected(SelectionEvent e) {
215                                         if (update.getUpdateList().getChanges().size() > 0) {
216                                                 if (update.getUpdateList().getSelected().size() > 0) {
217                                                         update.getUpdateList().clearSelected();
218                                                 } else {
219                                                         for (PropertyChange nr : update.getUpdateList().getChanges())
220                                                                 nr.select(true);
221                                                 }
222                                                 changeViewer.refresh();
223                                         }
224                                 }
225                         });
226                         selection.setEditingSupport(new SelectionEditingSupport(changeViewer));
227                 
228                 }
229                 Composite buttonComposite = new Composite(this, SWT.NONE);
230                 
231                 data = new GridData();
232                 data.grabExcessHorizontalSpace = true;
233                 data.grabExcessVerticalSpace = false;
234                 data.horizontalAlignment = SWT.FILL;
235                 data.verticalAlignment = SWT.BOTTOM;
236                 
237                 buttonComposite.setLayoutData(data);
238                 
239                 buttonComposite.setLayout(new GridLayout(3, false));
240                 
241                 Label label = new Label(buttonComposite, SWT.NONE);
242                 data = new GridData();
243                 data.grabExcessHorizontalSpace = true;
244                 data.grabExcessVerticalSpace = false;
245                 data.horizontalAlignment = SWT.FILL;
246                 data.verticalAlignment = SWT.CENTER;
247                 label.setLayoutData(data);
248                 
249                 updateAllButton = new Button(buttonComposite, SWT.PUSH);
250                 updateAllButton.setText("Update All");
251                 updateAllButton.addSelectionListener(new SelectionAdapter() {
252                         @Override
253                         public void widgetSelected(SelectionEvent e) {
254                                 applyAll();
255                         }
256                 });
257                 updateSelectedButton = new Button(buttonComposite, SWT.PUSH);
258                 updateSelectedButton.setText("Update Selected");
259                 updateSelectedButton.addSelectionListener(new SelectionAdapter() {
260                         @Override
261                         public void widgetSelected(SelectionEvent e) {
262                                 applySelected();
263                         }
264                 });
265         }
266         
267         protected Session getSession() {
268                 return Simantics.getSession();
269         }
270         
271         protected String getColumntTitle(int i) {
272                 switch (i) {
273                 case 0:
274                         return "!";
275                 case 1:
276                         return "Diagram";
277                 case 2:
278                         return "Symbol";
279                 case 3:
280                         return "Property";
281                 case 4:
282                         return "Old Value";
283                 case 5:
284                         return "New Value";
285                 default:
286                         throw new RuntimeException("Unknown column index" + i);
287                         
288                 }
289         }
290         
291         protected abstract ColumnLabelProvider getLabelProvider(int i);
292         
293         public GraphChanges getChanges() {
294                 return update.getChanges();
295         }
296         
297         public UpdateTree getUpdateTree() {
298                 return update.getUpdateTree();
299         }
300         
301         public UpdateList getUpdateList() {
302                 return update.getUpdateList();
303         }
304         
305         public CheckboxTreeViewer getChangeBrowser() {
306                 return changeBrowser;
307         }
308         
309         public TableViewer getChangeViewer() {
310                 return changeViewer;
311         }
312         
313         public void showWarning(ModelUpdate update, String text) {
314                 errorComposite.setVisible(true);
315                 
316                 Label label = new Label(errorComposite, SWT.NONE);
317                 label.setImage(warning);
318                 label = new Label(errorComposite, SWT.NONE);
319                 label.setText(text);
320                 //this.setStatusMessage("Update contains structural changes (new or deleted symbols), please create a new model.");
321                 this.layout(true);
322         }
323         
324         
325         private List<ICheckStateListener> checkStateListeners = new ArrayList<>();
326         
327         
328         public void addCheckStateListener(ICheckStateListener listener) {
329                 checkStateListeners.add(listener);
330         }
331         
332         public void removeCheckStateListener(ICheckStateListener listener) {
333                 checkStateListeners.remove(listener);
334         }
335         
336         public void refreshChecked() {
337                 Stack<UpdateNode> nodeStack = new Stack<UpdateNode>();
338                 nodeStack.push((UpdateNode)update.getUpdateTree().getRootNode());
339                 while (!nodeStack.isEmpty()) {
340                         UpdateNode n = nodeStack.pop();
341                         if (n.getOp() != null) {
342                                 UpdateOp op = n.getOp();
343                                 if (!op.isChange()) {
344                                         changeBrowser.setGrayed(n, true);
345                                         changeBrowser.setChecked(n, true);
346                                 } else {
347                                         boolean applied = op.applied();
348                                         if (applied) {
349                                                 changeBrowser.setChecked(n, true);
350                                                 changeBrowser.setGrayed(n, true);
351                                                 selectedStructure.remove(n);
352                                         } else {
353                                                 boolean sel = op.selected();
354                                                 if (sel) {
355                                                         selectedStructure.add(n);
356                                                         
357                                                 } else {
358                                                         selectedStructure.remove(n);
359                                                 }
360                                                 changeBrowser.setChecked(n, sel);
361                                                 changeBrowser.setGrayed(n, false);
362                                         }
363                                 }
364                         } else {
365                                 changeBrowser.setGrayed(n, true);
366                                 changeBrowser.setChecked(n, true);
367                         }
368                         for (UpdateNode c : n.getChildren()) {
369                                 nodeStack.add((UpdateNode)c);
370                         }
371                 }
372                 
373                 changeBrowser.refresh();
374                 for (ICheckStateListener l : checkStateListeners) {
375                         l.checkStateChanged(new CheckStateChangedEvent(changeBrowser, null, false));
376                 }
377                 changeViewer.refresh();
378         }
379         
380         protected abstract ModelUpdate createUpdate();
381         
382         
383         
384         public void load(UpdateEditorInput uei) {
385
386                 Resource oldModel = uei.getR1(); // old model that is being updated, contains user made changes
387                 Resource newModel = uei.getR2(); // new model, 
388                 Resource originalModel = uei.getR3(); // original old model without user made changes 
389                 boolean newDistinct = uei.isNewDistinct();
390                 try {
391                         if (update != null)
392                                 update.removeListener(this);
393                         update = createUpdate();
394                         update.addListener(this);
395                         update.setInput(oldModel, newModel, originalModel, newDistinct);                        
396                 } catch (DatabaseException e) {
397                         Text text = new Text(this, SWT.MULTI);
398                         text.setText(e.getMessage());
399                         e.printStackTrace();
400                         return;
401                 }
402         
403                 setInputs();
404                 
405                 refreshChecked();
406         }
407         
408         protected void setInputs() {
409                 changeViewer.setInput(update.getUpdateList().getChanges());
410                 changeBrowser.setInput(update.getUpdateTree());
411         }
412         
413         private void applyAll() {
414                 updateAllButton.setEnabled(false);
415                 updateSelectedButton.setEnabled(false);
416                 getSession().asyncRequest(new WriteRequest(){
417                         @Override
418                         public void perform(WriteGraph graph) throws DatabaseException {
419                                 update.applyAll(graph);
420                                 
421                                 Display.getDefault().asyncExec(new Runnable() {
422                                         
423                                         @Override
424                                         public void run() {
425                                                 
426                                                 updateAllButton.setEnabled(true);
427                                                 updateSelectedButton.setEnabled(true);
428                                                 refreshChecked();
429                                                 changeViewer.refresh();
430                                         }
431                                 });
432                         }
433                         
434                         
435                 }, e -> {
436                         if (e != null)
437                                 ExceptionUtils.logAndShowError("Cannot update model", e);
438                 });
439         }
440         
441         private void applySelected() {
442                 updateAllButton.setEnabled(false);
443                 updateSelectedButton.setEnabled(false);
444                 getSession().asyncRequest(new WriteRequest(){
445                         @Override
446                         public void perform(WriteGraph graph) throws DatabaseException {
447                                 update.applySelected(graph);
448                                 
449                                 Display.getDefault().asyncExec(new Runnable() {
450                                         
451                                         @Override
452                                         public void run() {
453                                                 changeViewer.refresh();
454                                                 updateAllButton.setEnabled(true);
455                                                 updateSelectedButton.setEnabled(true);
456                                                 refreshChecked();
457                                         }
458                                 });
459                         }
460                 });
461         }       
462         
463         private class ModificationListContentProvider implements IStructuredContentProvider {
464                 
465                 @SuppressWarnings("unchecked")
466                 @Override
467                 public Object[] getElements(Object inputElement) {
468                         if (inputElement == null)
469                                 return null;
470                         Collection<Pair<Statement, Statement>> coll = (Collection<Pair<Statement, Statement>>)inputElement;
471                         return coll.toArray();
472                 }
473                 
474                 @Override
475                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
476                         
477                 }
478                 
479                 @Override
480                 public void dispose() {
481                         
482                 }
483         }
484         
485         private class UpdateTreeContentProvider implements ITreeContentProvider {
486                 @Override
487                 public Object[] getElements(Object inputElement) {
488                         if (inputElement instanceof UpdateTree)
489                                 return new Object[]{((UpdateTree)inputElement).getRootNode()};
490                         if (inputElement instanceof UpdateNode) {
491                                 UpdateNode node = (UpdateNode)inputElement;
492                                 return node.getChildren().toArray();
493                         }
494                         return new Object[0];
495                 }
496                 
497                 @Override
498                 public Object getParent(Object element) {
499                         return null;
500                 }
501                 
502                 @Override
503                 public Object[] getChildren(Object parentElement) {
504                         UpdateNode node = (UpdateNode)parentElement;
505                         return node.getChildren().toArray();
506                 }
507                 
508                 @Override
509                 public boolean hasChildren(Object element) {
510                         UpdateNode node = (UpdateNode)element;
511                         return node.getChildren().size() > 0;
512                 }
513                 
514                 @Override
515                 public void dispose() {
516                         
517                 }
518                 
519                 @Override
520                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
521                         
522                 }
523         }
524         
525         private class SelectionLabelProvider extends ColumnLabelProvider {
526                 
527                 public SelectionLabelProvider() {
528                         
529                 }
530                 @Override
531                 public String getText(Object element) {
532                         return "";
533                 }
534                 
535                 @Override
536                 public Image getImage(Object element) {
537                         if (update == null || !update.isInit())
538                                 return null;
539                         PropertyChange pc = (PropertyChange)element;
540                         if (pc.selected())
541                                 return checked;
542                         else
543                                 return unchecked;
544                 }
545         }
546         
547         private class UpdateNodeLabelProvider extends ColumnLabelProvider {
548                 
549                 @Override
550                 public String getText(Object element) {
551                         final UpdateNode node = (UpdateNode)element;
552                         return node.getLabel();
553                 }
554                 
555                 @Override
556                 public Image getImage(Object element) {
557                         final UpdateNode node = (UpdateNode)element;
558                         try  {
559                                 ImageDescriptor id = getSession().syncRequest(new Read<ImageDescriptor>() {
560                                         @Override
561                                         public ImageDescriptor perform(ReadGraph graph) throws DatabaseException {
562                                                 return node.getImage(graph);
563                                         }
564                                 });
565                                 return manager.createImage(id);
566                         } catch (Exception e) {
567                                 return null;
568                         }
569                 }
570                 
571                 @Override
572                 public String getToolTipText(Object element) {
573                         final UpdateNode node = (UpdateNode)element;
574                         if (node.getOp() != null) {
575                                 return node.getOp().toString();
576                         } else {
577                                 return null;
578                         }
579                 }
580                 
581                 @Override
582                 public int getToolTipDisplayDelayTime(Object object) {
583                         return 1000;
584                 }
585                 
586                 @Override
587                 public int getToolTipTimeDisplayed(Object object) {
588                         return 10000;
589                 }
590
591                 @Override
592                 public Color getBackground(Object element) {
593                         final UpdateNode node = (UpdateNode)element;
594                         UpdateStatus status = node.getStatus();
595                         if (status == UpdateStatus.CONTAINS)
596                                 return containsColor;
597                         if (status == UpdateStatus.DELETED)
598                                 return deletedColor;
599                         if (status == UpdateStatus.NEW)
600                                 return addedColor;
601                         return null;            
602                 }
603         }
604         
605         private class SelectionEditingSupport extends EditingSupport {
606                 
607                 
608                 public SelectionEditingSupport(ColumnViewer viewer) {
609                         super(viewer);
610                 }
611
612                 @Override
613                 protected boolean canEdit(Object element) {
614                         return true;
615                 }
616                 
617                 @Override
618                 protected CellEditor getCellEditor(Object element) {
619                         return new CheckboxCellEditor(null, SWT.CHECK);
620                 }
621                 
622                 @Override
623                 protected Object getValue(Object element) {
624                         if (update == null || !update.isInit())
625                                 return false;
626                         PropertyChange pc = (PropertyChange)element;
627                         return pc.selected();
628                 }
629                 
630                 @Override
631                 protected void setValue(Object element, Object value) {
632                         if (update == null || !update.isInit())
633                                 return;
634                         PropertyChange pc = (PropertyChange)element;
635                         if (Boolean.TRUE.equals(value))
636                                 pc.select(true);
637                         else
638                                 pc.select(false);
639                         
640                         getViewer().refresh(element);
641                 }
642                 
643                 
644         }
645
646
647 }