1 package org.simantics.interop.update.editor;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashSet;
7 import java.util.Stack;
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.db.ReadGraph;
44 import org.simantics.db.Resource;
45 import org.simantics.db.Session;
46 import org.simantics.db.Statement;
47 import org.simantics.db.WriteGraph;
48 import org.simantics.db.common.request.WriteRequest;
49 import org.simantics.db.exception.DatabaseException;
50 import org.simantics.db.exception.DoesNotContainValueException;
51 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
52 import org.simantics.db.exception.ServiceException;
53 import org.simantics.db.layer0.util.Layer0Utils;
54 import org.simantics.db.request.Read;
55 import org.simantics.interop.test.GraphChanges;
56 import org.simantics.interop.test.GraphComparator;
57 import org.simantics.interop.update.Activator;
58 import org.simantics.interop.update.model.UpdateNode;
59 import org.simantics.interop.update.model.UpdateNode.Status;
60 import org.simantics.interop.update.model.UpdateOp;
61 import org.simantics.interop.update.model.UpdateTree;
62 import org.simantics.interop.utils.TableUtils;
63 import org.simantics.ui.SimanticsUI;
64 import org.simantics.utils.datastructures.Callback;
65 import org.simantics.utils.datastructures.Pair;
66 import org.simantics.utils.ui.ExceptionUtils;
70 * Editor for updating models.
72 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
75 public abstract class ModelUpdateEditor extends Composite{
77 private Composite errorComposite;
79 private CheckboxTreeViewer changeBrowser;
80 private TableViewer changeViewer;
82 private GraphChanges changes;
83 private UpdateTree updateTree;
85 private Button updateAllButton;
86 private Button updateSelectedButton;
88 private LocalResourceManager manager = new LocalResourceManager(JFaceResources.getResources());
90 private Image checked;
91 private Image unchecked;
92 private Image warning;
94 private Color containsColor;
95 private Color deletedColor;
96 private Color addedColor;
98 private HashSet<Pair<Statement, Statement>> selected = new HashSet<Pair<Statement,Statement>>();
100 private HashSet<UpdateNode> selectedStructure = new HashSet<UpdateNode>();
102 private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
104 public ModelUpdateEditor(Composite parent) {
105 super(parent,SWT.NONE);
106 checked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/tick.png"));
107 unchecked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/cross.png"));
108 warning = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/error.png"));
111 containsColor = new Color(parent.getDisplay(), new RGB(255,255,220));
112 deletedColor = new Color(parent.getDisplay(), new RGB(255,220,220));
113 addedColor = new Color(parent.getDisplay(), new RGB(220,255,220));
115 this.setLayout(new GridLayout(1,false));
117 errorComposite = new Composite(this, SWT.BORDER);
118 GridData data = new GridData();
119 data.grabExcessHorizontalSpace = true;
120 data.grabExcessVerticalSpace = false;
121 data.horizontalAlignment = SWT.FILL;
122 data.verticalAlignment = SWT.TOP;
123 errorComposite.setLayoutData(data);
124 errorComposite.setLayout(new GridLayout(2, false));
126 errorComposite.setVisible(false);
128 // IEditorInput input = getEditorInput();
129 // if (!(input instanceof UpdateEditorInput)) {
130 // Label label = new Label(composite, SWT.NONE);
131 // label.setText("Unknown input.");
135 Composite fillComposite = new Composite(this, SWT.NONE);
136 data = new GridData();
137 data.grabExcessHorizontalSpace = true;
138 data.grabExcessVerticalSpace = true;
139 data.horizontalAlignment = SWT.FILL;
140 data.verticalAlignment = SWT.FILL;
141 fillComposite.setLayoutData(data);
142 fillComposite.setLayout(new FillLayout(SWT.VERTICAL));
145 changeBrowser = new CheckboxTreeViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION );
147 changeBrowser.setContentProvider(new UpdateTreeContentProvider());
149 changeBrowser.getTree().setHeaderVisible(true);
151 ColumnViewerToolTipSupport.enableFor(changeBrowser);
154 TreeViewerColumn dataColumn = TableUtils.addColumn(changeBrowser, "Data", true, 600);
156 dataColumn.setLabelProvider(new UpdateNodeLabelProvider());
158 changeBrowser.addCheckStateListener(new ICheckStateListener() {
161 public void checkStateChanged(CheckStateChangedEvent event) {
162 UpdateNode node = (UpdateNode) event.getElement();
163 if (node.getOp() != null) {
164 node.getOp().select(Boolean.TRUE.equals(event.getChecked()));
171 changeBrowser.addTreeListener(new ITreeViewerListener() {
174 public void treeExpanded(TreeExpansionEvent event) {
175 event.getTreeViewer().getControl().getDisplay().asyncExec(new Runnable() {
179 // TreeViewer uses lazy load, checked states must be updated when the tree is expanded.
187 public void treeCollapsed(TreeExpansionEvent event) {
191 changeBrowser.setUseHashlookup(true);
195 changeViewer = new TableViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION);
197 changeViewer.getTable().setHeaderVisible(true);
198 changeViewer.getTable().setLinesVisible(true);
200 changeViewer.setContentProvider(new ModificationListContentProvider());
202 changeViewer.setUseHashlookup(true);
204 TableViewerColumn selection = TableUtils.addColumn(changeViewer, getColumntTitle(0), false, false, 20);
205 TableViewerColumn diagram = TableUtils.addColumn(changeViewer, getColumntTitle(1), true, true, 100);
206 TableViewerColumn symbol = TableUtils.addColumn(changeViewer, getColumntTitle(2), true, true, 100);
207 TableViewerColumn property = TableUtils.addColumn(changeViewer, getColumntTitle(3), true, true, 100);
208 TableViewerColumn oldValue = TableUtils.addColumn(changeViewer, getColumntTitle(4), true, true, 100);
209 TableViewerColumn newValue = TableUtils.addColumn(changeViewer, getColumntTitle(5), true, true, 100);
211 diagram.setLabelProvider(getLabelProvider(1));
212 symbol.setLabelProvider(getLabelProvider(2));
213 property.setLabelProvider(getLabelProvider(3));
214 oldValue.setLabelProvider(getLabelProvider(4));
215 newValue.setLabelProvider(getLabelProvider(5));
217 selection.setLabelProvider(new SelectionLabelProvider(selected));
218 selection.getColumn().addSelectionListener(new SelectionAdapter() {
220 public void widgetSelected(SelectionEvent e) {
221 if (changes.getModifications().size() > 0) {
222 if (selected.contains(changes.getModifications().get(0))) {
223 for (Pair<Statement, Statement> nr : changes.getModifications())
226 for (Pair<Statement, Statement> nr : changes.getModifications())
229 changeViewer.refresh();
233 selection.setEditingSupport(new SelectionEditingSupport(changeViewer, selected));
236 Composite buttonComposite = new Composite(this, SWT.NONE);
238 data = new GridData();
239 data.grabExcessHorizontalSpace = true;
240 data.grabExcessVerticalSpace = false;
241 data.horizontalAlignment = SWT.FILL;
242 data.verticalAlignment = SWT.BOTTOM;
244 buttonComposite.setLayoutData(data);
246 buttonComposite.setLayout(new GridLayout(3, false));
248 Label label = new Label(buttonComposite, SWT.NONE);
249 data = new GridData();
250 data.grabExcessHorizontalSpace = true;
251 data.grabExcessVerticalSpace = false;
252 data.horizontalAlignment = SWT.FILL;
253 data.verticalAlignment = SWT.CENTER;
254 label.setLayoutData(data);
256 updateAllButton = new Button(buttonComposite, SWT.PUSH);
257 updateAllButton.setText("Update All");
258 updateAllButton.addSelectionListener(new SelectionAdapter() {
260 public void widgetSelected(SelectionEvent e) {
264 updateSelectedButton = new Button(buttonComposite, SWT.PUSH);
265 updateSelectedButton.setText("Update Selected");
266 updateSelectedButton.addSelectionListener(new SelectionAdapter() {
268 public void widgetSelected(SelectionEvent e) {
274 protected Session getSession() {
275 return SimanticsUI.getSession();
278 protected String getColumntTitle(int i) {
293 throw new RuntimeException("Unknown column index" + i);
298 protected abstract ColumnLabelProvider getLabelProvider(int i);
299 protected abstract Pair<GraphComparator,Boolean> getChanges(Resource r1, Resource r2) throws DatabaseException;
300 protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
302 protected void addFilters(List<ChangeFilter> filters) {
306 public GraphChanges getChanges() {
310 public UpdateTree getUpdateTree() {
314 public CheckboxTreeViewer getChangeBrowser() {
315 return changeBrowser;
318 public TableViewer getChangeViewer() {
322 private void showWarning(String text) {
323 errorComposite.setVisible(true);
325 Label label = new Label(errorComposite, SWT.NONE);
326 label.setImage(warning);
327 label = new Label(errorComposite, SWT.NONE);
329 //this.setStatusMessage("Update contains structural changes (new or deleted symbols), please create a new model.");
334 private List<ICheckStateListener> checkStateListeners = new ArrayList<>();
337 public void addCheckStateListener(ICheckStateListener listener) {
338 checkStateListeners.add(listener);
341 public void removeCheckStateListener(ICheckStateListener listener) {
342 checkStateListeners.remove(listener);
345 public void refreshChecked() {
346 Stack<UpdateNode> nodeStack = new Stack<UpdateNode>();
347 nodeStack.push((UpdateNode)updateTree.getRootNode());
348 while (!nodeStack.isEmpty()) {
349 UpdateNode n = nodeStack.pop();
350 if (n.getOp() != null) {
351 UpdateOp op = n.getOp();
352 if (!op.isAdd() && !op.isDelete()) {
353 changeBrowser.setGrayed(n, true);
354 changeBrowser.setChecked(n, true);
356 boolean applied = op.applied();
358 changeBrowser.setChecked(n, true);
359 changeBrowser.setGrayed(n, true);
360 selectedStructure.remove(n);
362 boolean sel = op.selected();
364 selectedStructure.add(n);
367 selectedStructure.remove(n);
369 changeBrowser.setChecked(n, sel);
370 changeBrowser.setGrayed(n, false);
374 changeBrowser.setGrayed(n, true);
375 changeBrowser.setChecked(n, true);
377 for (UpdateNode c : n.getChildren()) {
378 nodeStack.add((UpdateNode)c);
382 changeBrowser.refresh();
383 for (ICheckStateListener l : checkStateListeners) {
384 l.checkStateChanged(new CheckStateChangedEvent(changeBrowser, null, false));
390 public void load(UpdateEditorInput uei) {
394 Resource r1 = uei.getR1();
395 Resource r2 = uei.getR2();
400 Pair<GraphComparator,Boolean> result = getChanges(r1,r2);
401 GraphComparator comparator = result.first;
403 showWarning("Structural symbols have been changed. Model update is not able to update these, please create a new model.");
404 comparator.test(getSession());
405 changes = comparator.getChanges();
406 changes = getSession().syncRequest(new FilterChangesRead(changes));
407 updateTree = getUpdateTree(changes);
408 } catch (DatabaseException e) {
409 Text text = new Text(this, SWT.MULTI);
410 text.setText(e.getMessage());
417 changeViewer.setInput(changes.getModifications());
418 changeBrowser.setInput(updateTree);
424 private void applyAll() {
425 updateAllButton.setEnabled(false);
426 updateSelectedButton.setEnabled(false);
427 getSession().asyncRequest(new WriteRequest(){
429 public void perform(WriteGraph graph) throws DatabaseException {
430 Layer0Utils.addCommentMetadata(graph, "Apply all model updates");
431 graph.markUndoPoint();
432 for (Pair<Statement, Statement> mod : changes.getModifications()) {
433 applyLiteralChange(graph, mod);
436 changes.getModifications().clear();
438 updateTree.getUpdateOps().applyAll(graph);
440 Display.getDefault().asyncExec(new Runnable() {
445 updateAllButton.setEnabled(true);
446 updateSelectedButton.setEnabled(true);
448 changeViewer.refresh();
454 }, new Callback<DatabaseException>() {
456 public void run(DatabaseException parameter) {
457 if (parameter != null)
458 ExceptionUtils.logAndShowError("Cannot update model", parameter);
463 private void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DoesNotContainValueException, ServiceException, ManyObjectsForFunctionalRelationException {
465 Resource s = changes.getComparable().getLeft(mod.second.getSubject());
466 Resource pred = mod.first.getPredicate();
467 if (graph.hasValue(mod.second.getObject())) {
468 Object value = graph.getValue(mod.second.getObject());
469 graph.claimLiteral(s, pred, value);
477 private void applySelected() {
478 updateAllButton.setEnabled(false);
479 updateSelectedButton.setEnabled(false);
480 getSession().asyncRequest(new WriteRequest(){
482 public void perform(WriteGraph graph) throws DatabaseException {
483 Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
484 graph.markUndoPoint();
485 for (Pair<Statement, Statement> mod : selected) {
486 changes.getModifications().remove(mod);
487 applyLiteralChange(graph, mod);
491 updateTree.getUpdateOps().applySelected(graph);
493 Display.getDefault().asyncExec(new Runnable() {
497 changeViewer.refresh();
498 updateAllButton.setEnabled(true);
499 updateSelectedButton.setEnabled(true);
510 * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
511 * 2. Runs custom filters for value changes.
516 * @throws DatabaseException
518 protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException
521 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
523 for (Pair<Statement, Statement> mod : changes.getModifications()) {
525 boolean accept = true;
526 for (ChangeFilter filter : filters) {
527 if (!filter.accept(g, mod)) {
533 modifications.add(mod);
535 GraphChanges newChanges = new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
539 public interface ChangeFilter {
540 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
548 * Filters floating point value changes (default filter is set filter when the change is less than 1%)
551 protected class FPValueFilter implements ChangeFilter {
553 private double percentage = 0.01;
555 public FPValueFilter() {
559 public FPValueFilter(double percentage) {
560 if (percentage < 0.0 || percentage > 1.0)
561 throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
562 this.percentage = percentage;
566 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
567 //filter floating point values that have less than 1% difference.
568 if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
570 Object v1 = g.getValue(change.first.getObject());
571 Object v2 = g.getValue(change.second.getObject());
573 if (v1 instanceof Double && v2 instanceof Double) {
574 double d1 = (Double)v1;
575 double d2 = (Double)v2;
576 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
578 } else if (v1 instanceof Float && v2 instanceof Float) {
579 float d1 = (Float)v1;
580 float d2 = (Float)v2;
581 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
590 private class FilterChangesRead implements Read<GraphChanges> {
591 private GraphChanges changes;
592 public FilterChangesRead(GraphChanges changes) {
593 this.changes = changes;
597 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
598 return filterChanges(graph, changes);
603 private class ModificationListContentProvider implements IStructuredContentProvider {
605 @SuppressWarnings("unchecked")
607 public Object[] getElements(Object inputElement) {
608 if (inputElement == null)
610 Collection<Pair<Statement, Statement>> coll = (Collection<Pair<Statement, Statement>>)inputElement;
611 return coll.toArray();
615 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
620 public void dispose() {
625 private class UpdateTreeContentProvider implements ITreeContentProvider {
627 public Object[] getElements(Object inputElement) {
628 if (inputElement instanceof UpdateTree)
629 return new Object[]{((UpdateTree)inputElement).getRootNode()};
630 if (inputElement instanceof UpdateNode) {
631 UpdateNode node = (UpdateNode)inputElement;
632 return node.getChildren().toArray();
634 return new Object[0];
638 public Object getParent(Object element) {
643 public Object[] getChildren(Object parentElement) {
644 UpdateNode node = (UpdateNode)parentElement;
645 return node.getChildren().toArray();
649 public boolean hasChildren(Object element) {
650 UpdateNode node = (UpdateNode)element;
651 return node.getChildren().size() > 0;
655 public void dispose() {
660 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
665 private class SelectionLabelProvider extends ColumnLabelProvider {
667 Collection<?> selected;
668 public SelectionLabelProvider(Collection<?> selected) {
669 this.selected = selected;
672 public String getText(Object element) {
677 public Image getImage(Object element) {
678 if (selected.contains(element))
685 private class UpdateNodeLabelProvider extends ColumnLabelProvider {
688 public String getText(Object element) {
689 final UpdateNode node = (UpdateNode)element;
690 return node.getLabel();
694 public Image getImage(Object element) {
695 final UpdateNode node = (UpdateNode)element;
697 ImageDescriptor id = getSession().syncRequest(new Read<ImageDescriptor>() {
699 public ImageDescriptor perform(ReadGraph graph) throws DatabaseException {
700 return node.getImage(graph);
703 return manager.createImage(id);
704 } catch (Exception e) {
710 public String getToolTipText(Object element) {
711 final UpdateNode node = (UpdateNode)element;
712 if (node.getOp() != null) {
713 return node.getOp().toString();
720 public int getToolTipDisplayDelayTime(Object object) {
725 public int getToolTipTimeDisplayed(Object object) {
730 public Color getBackground(Object element) {
731 final UpdateNode node = (UpdateNode)element;
732 Status status = node.getStatus();
733 if (status == Status.CONTAINS)
734 return containsColor;
735 if (status == Status.DELETED)
737 if (status == Status.NEW)
743 private class SelectionEditingSupport extends EditingSupport {
745 @SuppressWarnings("rawtypes")
749 @SuppressWarnings("rawtypes")
750 public SelectionEditingSupport(ColumnViewer viewer, Collection selected) {
752 this.selected = selected;
757 protected boolean canEdit(Object element) {
762 protected CellEditor getCellEditor(Object element) {
763 return new CheckboxCellEditor(null, SWT.CHECK);
767 protected Object getValue(Object element) {
768 return selected.contains(element);
771 @SuppressWarnings("unchecked")
773 protected void setValue(Object element, Object value) {
774 if (Boolean.TRUE.equals(value))
775 selected.add(element);
777 selected.remove(element);
779 getViewer().refresh(element);