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