]> gerrit.simantics Code Review - simantics/interop.git/blob - org.simantics.interop.update/src/org/simantics/interop/update/editor/ModelUpdateEditor.java
Initial implementation of model updates with three-way comparison.
[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 import java.util.Map.Entry;
9
10 import org.eclipse.jface.resource.ImageDescriptor;
11 import org.eclipse.jface.resource.JFaceResources;
12 import org.eclipse.jface.resource.LocalResourceManager;
13 import org.eclipse.jface.viewers.CellEditor;
14 import org.eclipse.jface.viewers.CheckStateChangedEvent;
15 import org.eclipse.jface.viewers.CheckboxCellEditor;
16 import org.eclipse.jface.viewers.CheckboxTreeViewer;
17 import org.eclipse.jface.viewers.ColumnLabelProvider;
18 import org.eclipse.jface.viewers.ColumnViewer;
19 import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
20 import org.eclipse.jface.viewers.EditingSupport;
21 import org.eclipse.jface.viewers.ICheckStateListener;
22 import org.eclipse.jface.viewers.IStructuredContentProvider;
23 import org.eclipse.jface.viewers.ITreeContentProvider;
24 import org.eclipse.jface.viewers.ITreeViewerListener;
25 import org.eclipse.jface.viewers.TableViewer;
26 import org.eclipse.jface.viewers.TableViewerColumn;
27 import org.eclipse.jface.viewers.TreeExpansionEvent;
28 import org.eclipse.jface.viewers.TreeViewerColumn;
29 import org.eclipse.jface.viewers.Viewer;
30 import org.eclipse.swt.SWT;
31 import org.eclipse.swt.events.SelectionAdapter;
32 import org.eclipse.swt.events.SelectionEvent;
33 import org.eclipse.swt.graphics.Color;
34 import org.eclipse.swt.graphics.Image;
35 import org.eclipse.swt.graphics.RGB;
36 import org.eclipse.swt.layout.FillLayout;
37 import org.eclipse.swt.layout.GridData;
38 import org.eclipse.swt.layout.GridLayout;
39 import org.eclipse.swt.widgets.Button;
40 import org.eclipse.swt.widgets.Composite;
41 import org.eclipse.swt.widgets.Display;
42 import org.eclipse.swt.widgets.Label;
43 import org.eclipse.swt.widgets.Text;
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.layer0.util.Layer0Utils;
52 import org.simantics.db.request.Read;
53 import org.simantics.interop.test.GraphChanges;
54 import org.simantics.interop.test.GraphComparator;
55 import org.simantics.interop.update.Activator;
56 import org.simantics.interop.update.model.UpdateList;
57 import org.simantics.interop.update.model.UpdateNode;
58 import org.simantics.interop.update.model.UpdateNode.Status;
59 import org.simantics.interop.update.model.UpdateOp;
60 import org.simantics.interop.update.model.UpdateTree;
61 import org.simantics.interop.utils.TableUtils;
62 import org.simantics.ui.SimanticsUI;
63 import org.simantics.utils.datastructures.Callback;
64 import org.simantics.utils.datastructures.Pair;
65 import org.simantics.utils.ui.ExceptionUtils;
66
67
68 /**
69  * Editor for updating models.
70  * 
71  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
72  *
73  */
74 public abstract class ModelUpdateEditor extends Composite{
75
76         private Composite errorComposite;
77         
78         private CheckboxTreeViewer changeBrowser;
79         private TableViewer changeViewer;
80         
81         private GraphChanges changes;
82         private UpdateTree updateTree;
83         private UpdateList updateList;
84         
85         private GraphChanges changes2;
86         private UpdateTree updateTree2;
87         private UpdateList updateList2;
88         
89         private Button updateAllButton;
90         private Button updateSelectedButton;
91         
92         private LocalResourceManager manager = new LocalResourceManager(JFaceResources.getResources());
93         
94         private Image checked;
95         private Image unchecked;
96         private Image warning;
97         
98         private Color containsColor;
99         private Color deletedColor;
100         private Color addedColor;
101
102         
103         private HashSet<UpdateNode> selectedStructure = new HashSet<UpdateNode>();
104         
105         private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
106         
107         public ModelUpdateEditor(Composite parent) {
108                 super(parent,SWT.NONE);
109                 checked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/tick.png"));
110                 unchecked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/cross.png"));
111                 warning = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/error.png"));
112
113                 
114                 containsColor = new Color(parent.getDisplay(), new RGB(255,255,220));
115                 deletedColor = new Color(parent.getDisplay(), new RGB(255,220,220));
116                 addedColor = new Color(parent.getDisplay(), new RGB(220,255,220));
117                 
118                 this.setLayout(new GridLayout(1,false));
119                 
120                 errorComposite = new Composite(this, SWT.BORDER);
121                 GridData data = new GridData();
122                 data.grabExcessHorizontalSpace = true;
123                 data.grabExcessVerticalSpace = false;
124                 data.horizontalAlignment = SWT.FILL;
125                 data.verticalAlignment = SWT.TOP;
126                 errorComposite.setLayoutData(data);
127                 errorComposite.setLayout(new GridLayout(2, false));
128                 
129                 errorComposite.setVisible(false);
130
131 //              IEditorInput input = getEditorInput();
132 //              if (!(input instanceof UpdateEditorInput)) {
133 //                      Label label = new Label(composite, SWT.NONE);
134 //                      label.setText("Unknown input.");
135 //                      return;
136 //              }
137                 
138                 Composite fillComposite = new Composite(this, SWT.NONE);
139                 data = new GridData();
140                 data.grabExcessHorizontalSpace = true;
141                 data.grabExcessVerticalSpace = true;
142                 data.horizontalAlignment = SWT.FILL;
143                 data.verticalAlignment = SWT.FILL;
144                 fillComposite.setLayoutData(data);
145                 fillComposite.setLayout(new FillLayout(SWT.VERTICAL));
146
147                 {
148                         changeBrowser = new CheckboxTreeViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION  );
149                         
150                         changeBrowser.setContentProvider(new UpdateTreeContentProvider());
151                         
152                         changeBrowser.getTree().setHeaderVisible(true);
153                         
154                         ColumnViewerToolTipSupport.enableFor(changeBrowser);
155
156                         
157                         TreeViewerColumn dataColumn = TableUtils.addColumn(changeBrowser, "Data", true, 600);
158
159                         dataColumn.setLabelProvider(new UpdateNodeLabelProvider());
160                         
161                         changeBrowser.addCheckStateListener(new ICheckStateListener() {
162                                 
163                                 @Override
164                                 public void checkStateChanged(CheckStateChangedEvent event) {
165                                         UpdateNode node = (UpdateNode) event.getElement();
166                                         if (node.getOp() != null) {
167                                                 node.getOp().select(Boolean.TRUE.equals(event.getChecked()));
168                                                 
169                                         }
170                                         refreshChecked();
171                                         
172                                 }
173                         });
174                         changeBrowser.addTreeListener(new ITreeViewerListener() {
175                                 
176                                 @Override
177                                 public void treeExpanded(TreeExpansionEvent event) {
178                                         event.getTreeViewer().getControl().getDisplay().asyncExec(new Runnable() {
179                                                 
180                                                 @Override
181                                                 public void run() {
182                                                         // TreeViewer uses lazy load, checked states must be updated when the tree is expanded. 
183                                                         refreshChecked();
184                                                 }
185                                         });
186                                         
187                                 }
188                                 
189                                 @Override
190                                 public void treeCollapsed(TreeExpansionEvent event) {
191                                         
192                                 }
193                         });
194                         changeBrowser.setUseHashlookup(true);
195                         
196                 }
197                 {
198                         changeViewer = new TableViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION);
199                         
200                         changeViewer.getTable().setHeaderVisible(true);
201                         changeViewer.getTable().setLinesVisible(true);
202                         
203                         changeViewer.setContentProvider(new ModificationListContentProvider());
204                         
205                         changeViewer.setUseHashlookup(true);
206                         
207                         TableViewerColumn selection = TableUtils.addColumn(changeViewer, getColumntTitle(0), false, false, 20);
208                         TableViewerColumn diagram = TableUtils.addColumn(changeViewer, getColumntTitle(1), true, true, 100);
209                         TableViewerColumn symbol = TableUtils.addColumn(changeViewer, getColumntTitle(2), true, true, 100);
210                         TableViewerColumn property = TableUtils.addColumn(changeViewer, getColumntTitle(3), true, true, 100);
211                         TableViewerColumn oldValue = TableUtils.addColumn(changeViewer, getColumntTitle(4), true, true, 100);
212                         TableViewerColumn newValue = TableUtils.addColumn(changeViewer, getColumntTitle(5), true, true, 100);
213                         
214                         diagram.setLabelProvider(getLabelProvider(1));
215                         symbol.setLabelProvider(getLabelProvider(2));
216                         property.setLabelProvider(getLabelProvider(3));
217                         oldValue.setLabelProvider(getLabelProvider(4));
218                         newValue.setLabelProvider(getLabelProvider(5));
219                         
220                         selection.setLabelProvider(new SelectionLabelProvider());
221                         selection.getColumn().addSelectionListener(new SelectionAdapter() {
222                                 @Override
223                                 public void widgetSelected(SelectionEvent e) {
224                                         if (updateList.getChanges().size() > 0) {
225                                                 if (updateList.getSelected().size() > 0) {
226                                                         updateList.clearSelected();
227                                                 } else {
228                                                         for (Pair<Statement, Statement> nr : updateList.getChanges())
229                                                                 updateList.addSelected(nr);
230                                                 }
231                                                 changeViewer.refresh();
232                                         }
233                                 }
234                         });
235                         selection.setEditingSupport(new SelectionEditingSupport(changeViewer));
236                 
237                 }
238                 Composite buttonComposite = new Composite(this, SWT.NONE);
239                 
240                 data = new GridData();
241                 data.grabExcessHorizontalSpace = true;
242                 data.grabExcessVerticalSpace = false;
243                 data.horizontalAlignment = SWT.FILL;
244                 data.verticalAlignment = SWT.BOTTOM;
245                 
246                 buttonComposite.setLayoutData(data);
247                 
248                 buttonComposite.setLayout(new GridLayout(3, false));
249                 
250                 Label label = new Label(buttonComposite, SWT.NONE);
251                 data = new GridData();
252                 data.grabExcessHorizontalSpace = true;
253                 data.grabExcessVerticalSpace = false;
254                 data.horizontalAlignment = SWT.FILL;
255                 data.verticalAlignment = SWT.CENTER;
256                 label.setLayoutData(data);
257                 
258                 updateAllButton = new Button(buttonComposite, SWT.PUSH);
259                 updateAllButton.setText("Update All");
260                 updateAllButton.addSelectionListener(new SelectionAdapter() {
261                         @Override
262                         public void widgetSelected(SelectionEvent e) {
263                                 applyAll();
264                         }
265                 });
266                 updateSelectedButton = new Button(buttonComposite, SWT.PUSH);
267                 updateSelectedButton.setText("Update Selected");
268                 updateSelectedButton.addSelectionListener(new SelectionAdapter() {
269                         @Override
270                         public void widgetSelected(SelectionEvent e) {
271                                 applySelected();
272                         }
273                 });
274         }
275         
276         protected Session getSession() {
277                 return SimanticsUI.getSession();
278         }
279         
280         protected String getColumntTitle(int i) {
281                 switch (i) {
282                 case 0:
283                         return "!";
284                 case 1:
285                         return "Diagram";
286                 case 2:
287                         return "Symbol";
288                 case 3:
289                         return "Property";
290                 case 4:
291                         return "Old Value";
292                 case 5:
293                         return "New Value";
294                 default:
295                         throw new RuntimeException("Unknown column index" + i);
296                         
297                 }
298         }
299         
300         protected abstract ColumnLabelProvider getLabelProvider(int i);
301         protected abstract Pair<GraphComparator,String> getChanges(Resource r1, Resource r2)  throws DatabaseException;
302         protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
303         protected UpdateList getUpdateList(GraphChanges changes) throws DatabaseException {
304                 return new UpdateList(changes.getModifications());
305         }
306         
307         protected void addFilters(List<ChangeFilter> filters) {
308                 
309         }
310         
311         public GraphChanges getChanges() {
312                 return changes;
313         }
314         
315         public UpdateTree getUpdateTree() {
316                 return updateTree;
317         }
318         
319         public UpdateList getUpdateList() {
320                 return updateList;
321         }
322         
323         public CheckboxTreeViewer getChangeBrowser() {
324                 return changeBrowser;
325         }
326         
327         public TableViewer getChangeViewer() {
328                 return changeViewer;
329         }
330         
331         private void showWarning(String text) {
332                 errorComposite.setVisible(true);
333                 
334                 Label label = new Label(errorComposite, SWT.NONE);
335                 label.setImage(warning);
336                 label = new Label(errorComposite, SWT.NONE);
337                 label.setText(text);
338                 //this.setStatusMessage("Update contains structural changes (new or deleted symbols), please create a new model.");
339                 this.layout(true);
340         }
341         
342         
343         private List<ICheckStateListener> checkStateListeners = new ArrayList<>();
344         
345         
346         public void addCheckStateListener(ICheckStateListener listener) {
347                 checkStateListeners.add(listener);
348         }
349         
350         public void removeCheckStateListener(ICheckStateListener listener) {
351                 checkStateListeners.remove(listener);
352         }
353         
354         public void refreshChecked() {
355                 Stack<UpdateNode> nodeStack = new Stack<UpdateNode>();
356                 nodeStack.push((UpdateNode)updateTree.getRootNode());
357                 while (!nodeStack.isEmpty()) {
358                         UpdateNode n = nodeStack.pop();
359                         if (n.getOp() != null) {
360                                 UpdateOp op = n.getOp();
361                                 if (!op.isAdd() && !op.isDelete()) {
362                                         changeBrowser.setGrayed(n, true);
363                                         changeBrowser.setChecked(n, true);
364                                 } else {
365                                         boolean applied = op.applied();
366                                         if (applied) {
367                                                 changeBrowser.setChecked(n, true);
368                                                 changeBrowser.setGrayed(n, true);
369                                                 selectedStructure.remove(n);
370                                         } else {
371                                                 boolean sel = op.selected();
372                                                 if (sel) {
373                                                         selectedStructure.add(n);
374                                                         
375                                                 } else {
376                                                         selectedStructure.remove(n);
377                                                 }
378                                                 changeBrowser.setChecked(n, sel);
379                                                 changeBrowser.setGrayed(n, false);
380                                         }
381                                 }
382                         } else {
383                                 changeBrowser.setGrayed(n, true);
384                                 changeBrowser.setChecked(n, true);
385                         }
386                         for (UpdateNode c : n.getChildren()) {
387                                 nodeStack.add((UpdateNode)c);
388                         }
389                 }
390                 
391                 changeBrowser.refresh();
392                 for (ICheckStateListener l : checkStateListeners) {
393                         l.checkStateChanged(new CheckStateChangedEvent(changeBrowser, null, false));
394                 }
395                 changeViewer.refresh();
396         }
397         
398         
399         
400         public void load(UpdateEditorInput uei) {
401                 
402                 addFilters(filters);
403
404                 Resource r1 = uei.getR1();
405                 Resource r2 = uei.getR2();
406                 Resource r3 = uei.getR3();
407
408                 try {
409                         
410                         Pair<GraphComparator,String> result = getChanges(r1,r2);
411                         GraphComparator comparator  = result.first;
412                         if (result.second != null)
413                                 showWarning(result.second);
414                         comparator.test(getSession());
415                         changes = comparator.getChanges();
416                         changes = getSession().syncRequest(new FilterChangesRead(changes));
417                         updateTree = getUpdateTree(changes);
418                         updateList = getUpdateList(changes);
419                         if (r3 != null) {
420                                 Pair<GraphComparator,String> result2 = getChanges(r1,r3);
421                                 GraphComparator comparator2  = result2.first;
422                                 if (result2.second != null)
423                                         showWarning(result2.second);
424                                 comparator2.test(getSession());
425                                 changes2 = comparator2.getChanges();
426                                 changes2 = getSession().syncRequest(new FilterChangesRead(changes2));
427                                 updateTree2 = getUpdateTree(changes2);
428                                 updateList2 = getUpdateList(changes2);
429                                 
430                                 createDefaultSelections();
431                         }
432                 } catch (DatabaseException e) {
433                         Text text = new Text(this, SWT.MULTI);
434                         text.setText(e.getMessage());
435                         e.printStackTrace();
436                         return;
437                 }
438         
439                 setInputs();
440                 
441                 refreshChecked();
442         }
443         
444         protected void setInputs() {
445                 changeViewer.setInput(updateList.getChanges());
446                 changeBrowser.setInput(updateTree);
447         }
448         
449         private void applyAll() {
450                 updateAllButton.setEnabled(false);
451                 updateSelectedButton.setEnabled(false);
452                 getSession().asyncRequest(new WriteRequest(){
453                         @Override
454                         public void perform(WriteGraph graph) throws DatabaseException {
455                                 Layer0Utils.addCommentMetadata(graph, "Apply all model updates");
456                                 graph.markUndoPoint();
457                                 for (Pair<Statement, Statement> mod : updateList.getChanges()) {
458                                         applyLiteralChange(graph, mod);
459                                 }
460                                 updateList.clear();
461                                 
462                                 updateTree.getUpdateOps().applyAll(graph);
463                                 
464                                 Display.getDefault().asyncExec(new Runnable() {
465                                         
466                                         @Override
467                                         public void run() {
468                                                 
469                                                 updateAllButton.setEnabled(true);
470                                                 updateSelectedButton.setEnabled(true);
471                                                 refreshChecked();
472                                                 changeViewer.refresh();
473                                         }
474                                 });
475                         }
476                         
477                         
478                 }, new Callback<DatabaseException>() {
479                         @Override
480                         public void run(DatabaseException parameter) {
481                                 if (parameter != null)
482                                         ExceptionUtils.logAndShowError("Cannot update model", parameter);
483                         }
484                 });
485         }
486         
487         protected void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DatabaseException {
488                 if (mod.second == null) {
489                         graph.deny(mod.first);
490                         return;
491                 } 
492                 Resource s = changes.getComparable().getLeft(mod.second.getSubject());
493                 Resource pred = mod.second.getPredicate();
494                 if (graph.hasValue(mod.second.getObject())) {
495                         Object value = graph.getValue(mod.second.getObject());
496                         graph.claimLiteral(s, pred, value);
497                 } else {
498                         graph.deny(s,pred);
499                 }
500                 
501                 
502         }
503         
504         private void applySelected() {
505                 updateAllButton.setEnabled(false);
506                 updateSelectedButton.setEnabled(false);
507                 getSession().asyncRequest(new WriteRequest(){
508                         @Override
509                         public void perform(WriteGraph graph) throws DatabaseException {
510                                 Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
511                                 graph.markUndoPoint();
512                                 for (Pair<Statement, Statement> mod : updateList.getSelected()) {
513                                         updateList.removeChange(mod);
514                                         applyLiteralChange(graph, mod);
515                                 }
516                                 
517                                 updateTree.getUpdateOps().applySelected(graph);
518                                 
519                                 Display.getDefault().asyncExec(new Runnable() {
520                                         
521                                         @Override
522                                         public void run() {
523                                                 changeViewer.refresh();
524                                                 updateAllButton.setEnabled(true);
525                                                 updateSelectedButton.setEnabled(true);
526                                                 refreshChecked();
527                                         }
528                                 });
529                         }
530                 });
531         }
532
533         
534         /**
535          * Filters changes:
536          * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
537          * 2. Runs custom filters for value changes. 
538          * 
539          * @param g
540          * @param changes
541          * @return
542          * @throws DatabaseException
543          */
544         protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException 
545     {
546                 
547                 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
548                 
549         for (Pair<Statement, Statement> mod : changes.getModifications()) {
550                 
551                 boolean accept = true;
552                 for (ChangeFilter filter : filters) {
553                         if (!filter.accept(g, mod)) {
554                                 accept = false;
555                                 break;
556                         }       
557                 }
558                 if (accept)
559                         modifications.add(mod);
560         }
561         GraphChanges newChanges =  new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
562         return newChanges;
563     }
564         
565         public interface ChangeFilter {
566                 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
567         }
568         
569         
570         protected void createDefaultSelections() {
571                 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
572                         UpdateOp op2 = updateTree2.getUpdateOps().getUpdateOp(op.getKey());
573                         if (op2 == null) {
574                                 op.getValue().select(true);
575                         }
576                 }
577                 
578                 for (Entry<Statement, UpdateOp> op : updateTree.getUpdateOps().getStatementMap().entrySet()) {
579                         UpdateOp op2 = updateTree2.getUpdateOps().getUpdateOp(op.getKey());
580                         if (op2 == null) {
581                                 op.getValue().select(true);
582                         }
583                 }
584                 
585                 for (Pair<Statement, Statement> pair : updateList.getChanges()) {
586                         if (pair.first != null) {
587                                 boolean found = false;
588                                 for (Pair<Statement, Statement> pair2 : updateList2.getChanges()) {
589                                         if (pair.first.equals(pair2.first)) {
590                                                 found = true;
591                                                 break;
592                                         }
593                                 }
594                                 if (!found) {
595                                         updateList.addSelected(pair);
596                                 }
597                         } else {
598                                 updateList.addSelected(pair);
599                         }
600                 }
601         }
602         
603         
604         
605         /**
606          * 
607          * Filters floating point value changes (default filter is set filter when the change is less than 1%)  
608          *
609          */
610         protected class FPValueFilter implements ChangeFilter  {
611                 
612                 private double percentage = 0.01;
613                 
614                 public FPValueFilter() {
615                         
616                 }
617                 
618                 public FPValueFilter(double percentage) {
619                         if (percentage < 0.0 || percentage > 1.0)
620                                 throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
621                         this.percentage = percentage;
622                 }
623                 
624                 @Override
625                 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
626                         //filter floating point values that have less than 1% difference.
627                         if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
628                                 return true;
629                 Object v1 = g.getValue(change.first.getObject());
630                 Object v2 = g.getValue(change.second.getObject());
631                 
632                 if (v1 instanceof Double && v2 instanceof Double) {
633                         double d1 = (Double)v1;
634                         double d2 = (Double)v2;
635                         if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
636                                 return false;
637                 } else if (v1 instanceof Float && v2 instanceof Float) {
638                         float d1 = (Float)v1;
639                         float d2 = (Float)v2;
640                         if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
641                                 return false;
642                 }
643
644                 return true;
645                 }
646         }
647
648         
649         private class FilterChangesRead implements Read<GraphChanges> {
650                 private GraphChanges changes;
651                 public FilterChangesRead(GraphChanges changes) {
652                         this.changes = changes;
653                 }
654                 
655                 @Override
656                 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
657                         return filterChanges(graph, changes);
658                 }
659         }
660         
661         
662         private class ModificationListContentProvider implements IStructuredContentProvider {
663                 
664                 @SuppressWarnings("unchecked")
665                 @Override
666                 public Object[] getElements(Object inputElement) {
667                         if (inputElement == null)
668                                 return null;
669                         Collection<Pair<Statement, Statement>> coll = (Collection<Pair<Statement, Statement>>)inputElement;
670                         return coll.toArray();
671                 }
672                 
673                 @Override
674                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
675                         
676                 }
677                 
678                 @Override
679                 public void dispose() {
680                         
681                 }
682         }
683         
684         private class UpdateTreeContentProvider implements ITreeContentProvider {
685                 @Override
686                 public Object[] getElements(Object inputElement) {
687                         if (inputElement instanceof UpdateTree)
688                                 return new Object[]{((UpdateTree)inputElement).getRootNode()};
689                         if (inputElement instanceof UpdateNode) {
690                                 UpdateNode node = (UpdateNode)inputElement;
691                                 return node.getChildren().toArray();
692                         }
693                         return new Object[0];
694                 }
695                 
696                 @Override
697                 public Object getParent(Object element) {
698                         return null;
699                 }
700                 
701                 @Override
702                 public Object[] getChildren(Object parentElement) {
703                         UpdateNode node = (UpdateNode)parentElement;
704                         return node.getChildren().toArray();
705                 }
706                 
707                 @Override
708                 public boolean hasChildren(Object element) {
709                         UpdateNode node = (UpdateNode)element;
710                         return node.getChildren().size() > 0;
711                 }
712                 
713                 @Override
714                 public void dispose() {
715                         
716                 }
717                 
718                 @Override
719                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
720                         
721                 }
722         }
723         
724         private class SelectionLabelProvider extends ColumnLabelProvider {
725                 
726                 public SelectionLabelProvider() {
727                         
728                 }
729                 @Override
730                 public String getText(Object element) {
731                         return "";
732                 }
733                 
734                 @Override
735                 public Image getImage(Object element) {
736                         if (updateList == null)
737                                 return null;
738                         if (updateList.isSelected((Pair<Statement, Statement>) element))
739                                 return checked;
740                         else
741                                 return unchecked;
742                 }
743         }
744         
745         private class UpdateNodeLabelProvider extends ColumnLabelProvider {
746                 
747                 @Override
748                 public String getText(Object element) {
749                         final UpdateNode node = (UpdateNode)element;
750                         return node.getLabel();
751                 }
752                 
753                 @Override
754                 public Image getImage(Object element) {
755                         final UpdateNode node = (UpdateNode)element;
756                         try  {
757                                 ImageDescriptor id = getSession().syncRequest(new Read<ImageDescriptor>() {
758                                         @Override
759                                         public ImageDescriptor perform(ReadGraph graph) throws DatabaseException {
760                                                 return node.getImage(graph);
761                                         }
762                                 });
763                                 return manager.createImage(id);
764                         } catch (Exception e) {
765                                 return null;
766                         }
767                 }
768                 
769                 @Override
770                 public String getToolTipText(Object element) {
771                         final UpdateNode node = (UpdateNode)element;
772                         if (node.getOp() != null) {
773                                 return node.getOp().toString();
774                         } else {
775                                 return null;
776                         }
777                 }
778                 
779                 @Override
780                 public int getToolTipDisplayDelayTime(Object object) {
781                         return 1000;
782                 }
783                 
784                 @Override
785                 public int getToolTipTimeDisplayed(Object object) {
786                         return 10000;
787                 }
788
789                 @Override
790                 public Color getBackground(Object element) {
791                         final UpdateNode node = (UpdateNode)element;
792                         Status status = node.getStatus();
793                         if (status == Status.CONTAINS)
794                                 return containsColor;
795                         if (status == Status.DELETED)
796                                 return deletedColor;
797                         if (status == Status.NEW)
798                                 return addedColor;
799                         return null;            
800                 }
801         }
802         
803         private class SelectionEditingSupport extends EditingSupport {
804                 
805                 
806                 @SuppressWarnings("rawtypes")
807                 public SelectionEditingSupport(ColumnViewer viewer) {
808                         super(viewer);
809                         
810                 }
811
812                 @Override
813                 protected boolean canEdit(Object element) {
814                         return true;
815                 }
816                 
817                 @Override
818                 protected CellEditor getCellEditor(Object element) {
819                         return new CheckboxCellEditor(null, SWT.CHECK);
820                 }
821                 
822                 @Override
823                 protected Object getValue(Object element) {
824                         if (updateList == null)
825                                 return false;
826                         return updateList.isSelected((Pair<Statement, Statement>) element);
827                 }
828                 
829                 @SuppressWarnings("unchecked")
830                 @Override
831                 protected void setValue(Object element, Object value) {
832                         if (updateList == null)
833                                 return;
834                         if (Boolean.TRUE.equals(value))
835                                 updateList.addSelected((Pair<Statement, Statement>) element);
836                         else
837                                 updateList.removeSelected((Pair<Statement, Statement>) element);
838                         
839                         getViewer().refresh(element);
840                 }
841                 
842                 
843         }
844
845
846 }