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.layer0.util.Layer0Utils;
51 import org.simantics.db.request.Read;
52 import org.simantics.interop.test.GraphChanges;
53 import org.simantics.interop.test.GraphComparator;
54 import org.simantics.interop.update.Activator;
55 import org.simantics.interop.update.model.UpdateList;
56 import org.simantics.interop.update.model.UpdateNode;
57 import org.simantics.interop.update.model.UpdateNode.Status;
58 import org.simantics.interop.update.model.UpdateOp;
59 import org.simantics.interop.update.model.UpdateTree;
60 import org.simantics.interop.utils.TableUtils;
61 import org.simantics.ui.SimanticsUI;
62 import org.simantics.utils.datastructures.Callback;
63 import org.simantics.utils.datastructures.Pair;
64 import org.simantics.utils.ui.ExceptionUtils;
68 * Editor for updating models.
70 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
73 public abstract class ModelUpdateEditor extends Composite{
75 private Composite errorComposite;
77 private CheckboxTreeViewer changeBrowser;
78 private TableViewer changeViewer;
80 private GraphChanges changes;
81 private UpdateTree updateTree;
82 private UpdateList updateList;
84 private Button updateAllButton;
85 private Button updateSelectedButton;
87 private LocalResourceManager manager = new LocalResourceManager(JFaceResources.getResources());
89 private Image checked;
90 private Image unchecked;
91 private Image warning;
93 private Color containsColor;
94 private Color deletedColor;
95 private Color addedColor;
98 private HashSet<UpdateNode> selectedStructure = new HashSet<UpdateNode>();
100 private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
102 public ModelUpdateEditor(Composite parent) {
103 super(parent,SWT.NONE);
104 checked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/tick.png"));
105 unchecked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/cross.png"));
106 warning = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/error.png"));
109 containsColor = new Color(parent.getDisplay(), new RGB(255,255,220));
110 deletedColor = new Color(parent.getDisplay(), new RGB(255,220,220));
111 addedColor = new Color(parent.getDisplay(), new RGB(220,255,220));
113 this.setLayout(new GridLayout(1,false));
115 errorComposite = new Composite(this, SWT.BORDER);
116 GridData data = new GridData();
117 data.grabExcessHorizontalSpace = true;
118 data.grabExcessVerticalSpace = false;
119 data.horizontalAlignment = SWT.FILL;
120 data.verticalAlignment = SWT.TOP;
121 errorComposite.setLayoutData(data);
122 errorComposite.setLayout(new GridLayout(2, false));
124 errorComposite.setVisible(false);
126 // IEditorInput input = getEditorInput();
127 // if (!(input instanceof UpdateEditorInput)) {
128 // Label label = new Label(composite, SWT.NONE);
129 // label.setText("Unknown input.");
133 Composite fillComposite = new Composite(this, SWT.NONE);
134 data = new GridData();
135 data.grabExcessHorizontalSpace = true;
136 data.grabExcessVerticalSpace = true;
137 data.horizontalAlignment = SWT.FILL;
138 data.verticalAlignment = SWT.FILL;
139 fillComposite.setLayoutData(data);
140 fillComposite.setLayout(new FillLayout(SWT.VERTICAL));
143 changeBrowser = new CheckboxTreeViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION );
145 changeBrowser.setContentProvider(new UpdateTreeContentProvider());
147 changeBrowser.getTree().setHeaderVisible(true);
149 ColumnViewerToolTipSupport.enableFor(changeBrowser);
152 TreeViewerColumn dataColumn = TableUtils.addColumn(changeBrowser, "Data", true, 600);
154 dataColumn.setLabelProvider(new UpdateNodeLabelProvider());
156 changeBrowser.addCheckStateListener(new ICheckStateListener() {
159 public void checkStateChanged(CheckStateChangedEvent event) {
160 UpdateNode node = (UpdateNode) event.getElement();
161 if (node.getOp() != null) {
162 node.getOp().select(Boolean.TRUE.equals(event.getChecked()));
169 changeBrowser.addTreeListener(new ITreeViewerListener() {
172 public void treeExpanded(TreeExpansionEvent event) {
173 event.getTreeViewer().getControl().getDisplay().asyncExec(new Runnable() {
177 // TreeViewer uses lazy load, checked states must be updated when the tree is expanded.
185 public void treeCollapsed(TreeExpansionEvent event) {
189 changeBrowser.setUseHashlookup(true);
193 changeViewer = new TableViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION);
195 changeViewer.getTable().setHeaderVisible(true);
196 changeViewer.getTable().setLinesVisible(true);
198 changeViewer.setContentProvider(new ModificationListContentProvider());
200 changeViewer.setUseHashlookup(true);
202 TableViewerColumn selection = TableUtils.addColumn(changeViewer, getColumntTitle(0), false, false, 20);
203 TableViewerColumn diagram = TableUtils.addColumn(changeViewer, getColumntTitle(1), true, true, 100);
204 TableViewerColumn symbol = TableUtils.addColumn(changeViewer, getColumntTitle(2), true, true, 100);
205 TableViewerColumn property = TableUtils.addColumn(changeViewer, getColumntTitle(3), true, true, 100);
206 TableViewerColumn oldValue = TableUtils.addColumn(changeViewer, getColumntTitle(4), true, true, 100);
207 TableViewerColumn newValue = TableUtils.addColumn(changeViewer, getColumntTitle(5), true, true, 100);
209 diagram.setLabelProvider(getLabelProvider(1));
210 symbol.setLabelProvider(getLabelProvider(2));
211 property.setLabelProvider(getLabelProvider(3));
212 oldValue.setLabelProvider(getLabelProvider(4));
213 newValue.setLabelProvider(getLabelProvider(5));
215 selection.setLabelProvider(new SelectionLabelProvider());
216 selection.getColumn().addSelectionListener(new SelectionAdapter() {
218 public void widgetSelected(SelectionEvent e) {
219 if (updateList.getChanges().size() > 0) {
220 if (updateList.getSelected().contains(updateList.getChanges().iterator().next())) {
221 for (Pair<Statement, Statement> nr : updateList.getChanges())
222 updateList.removeSelected(nr);
224 for (Pair<Statement, Statement> nr : updateList.getChanges())
225 updateList.addSelected(nr);
227 changeViewer.refresh();
231 selection.setEditingSupport(new SelectionEditingSupport(changeViewer));
234 Composite buttonComposite = new Composite(this, SWT.NONE);
236 data = new GridData();
237 data.grabExcessHorizontalSpace = true;
238 data.grabExcessVerticalSpace = false;
239 data.horizontalAlignment = SWT.FILL;
240 data.verticalAlignment = SWT.BOTTOM;
242 buttonComposite.setLayoutData(data);
244 buttonComposite.setLayout(new GridLayout(3, false));
246 Label label = new Label(buttonComposite, SWT.NONE);
247 data = new GridData();
248 data.grabExcessHorizontalSpace = true;
249 data.grabExcessVerticalSpace = false;
250 data.horizontalAlignment = SWT.FILL;
251 data.verticalAlignment = SWT.CENTER;
252 label.setLayoutData(data);
254 updateAllButton = new Button(buttonComposite, SWT.PUSH);
255 updateAllButton.setText("Update All");
256 updateAllButton.addSelectionListener(new SelectionAdapter() {
258 public void widgetSelected(SelectionEvent e) {
262 updateSelectedButton = new Button(buttonComposite, SWT.PUSH);
263 updateSelectedButton.setText("Update Selected");
264 updateSelectedButton.addSelectionListener(new SelectionAdapter() {
266 public void widgetSelected(SelectionEvent e) {
272 protected Session getSession() {
273 return SimanticsUI.getSession();
276 protected String getColumntTitle(int i) {
291 throw new RuntimeException("Unknown column index" + i);
296 protected abstract ColumnLabelProvider getLabelProvider(int i);
297 protected abstract Pair<GraphComparator,Boolean> getChanges(Resource r1, Resource r2) throws DatabaseException;
298 protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
299 protected UpdateList getUpdateList(GraphChanges changes) throws DatabaseException {
300 return new UpdateList(changes.getModifications());
303 protected void addFilters(List<ChangeFilter> filters) {
307 public GraphChanges getChanges() {
311 public UpdateTree getUpdateTree() {
315 public UpdateList getUpdateList() {
319 public CheckboxTreeViewer getChangeBrowser() {
320 return changeBrowser;
323 public TableViewer getChangeViewer() {
327 private void showWarning(String text) {
328 errorComposite.setVisible(true);
330 Label label = new Label(errorComposite, SWT.NONE);
331 label.setImage(warning);
332 label = new Label(errorComposite, SWT.NONE);
334 //this.setStatusMessage("Update contains structural changes (new or deleted symbols), please create a new model.");
339 private List<ICheckStateListener> checkStateListeners = new ArrayList<>();
342 public void addCheckStateListener(ICheckStateListener listener) {
343 checkStateListeners.add(listener);
346 public void removeCheckStateListener(ICheckStateListener listener) {
347 checkStateListeners.remove(listener);
350 public void refreshChecked() {
351 Stack<UpdateNode> nodeStack = new Stack<UpdateNode>();
352 nodeStack.push((UpdateNode)updateTree.getRootNode());
353 while (!nodeStack.isEmpty()) {
354 UpdateNode n = nodeStack.pop();
355 if (n.getOp() != null) {
356 UpdateOp op = n.getOp();
357 if (!op.isAdd() && !op.isDelete()) {
358 changeBrowser.setGrayed(n, true);
359 changeBrowser.setChecked(n, true);
361 boolean applied = op.applied();
363 changeBrowser.setChecked(n, true);
364 changeBrowser.setGrayed(n, true);
365 selectedStructure.remove(n);
367 boolean sel = op.selected();
369 selectedStructure.add(n);
372 selectedStructure.remove(n);
374 changeBrowser.setChecked(n, sel);
375 changeBrowser.setGrayed(n, false);
379 changeBrowser.setGrayed(n, true);
380 changeBrowser.setChecked(n, true);
382 for (UpdateNode c : n.getChildren()) {
383 nodeStack.add((UpdateNode)c);
387 changeBrowser.refresh();
388 for (ICheckStateListener l : checkStateListeners) {
389 l.checkStateChanged(new CheckStateChangedEvent(changeBrowser, null, false));
391 changeViewer.refresh();
396 public void load(UpdateEditorInput uei) {
400 Resource r1 = uei.getR1();
401 Resource r2 = uei.getR2();
406 Pair<GraphComparator,Boolean> result = getChanges(r1,r2);
407 GraphComparator comparator = result.first;
409 showWarning("Structural symbols have been changed. Model update is not able to update these, please create a new model.");
410 comparator.test(getSession());
411 changes = comparator.getChanges();
412 changes = getSession().syncRequest(new FilterChangesRead(changes));
413 updateTree = getUpdateTree(changes);
414 updateList = getUpdateList(changes);
415 } catch (DatabaseException e) {
416 Text text = new Text(this, SWT.MULTI);
417 text.setText(e.getMessage());
427 protected void setInputs() {
428 changeViewer.setInput(updateList.getChanges());
429 changeBrowser.setInput(updateTree);
432 private void applyAll() {
433 updateAllButton.setEnabled(false);
434 updateSelectedButton.setEnabled(false);
435 getSession().asyncRequest(new WriteRequest(){
437 public void perform(WriteGraph graph) throws DatabaseException {
438 Layer0Utils.addCommentMetadata(graph, "Apply all model updates");
439 graph.markUndoPoint();
440 for (Pair<Statement, Statement> mod : updateList.getChanges()) {
441 applyLiteralChange(graph, mod);
445 updateTree.getUpdateOps().applyAll(graph);
447 Display.getDefault().asyncExec(new Runnable() {
452 updateAllButton.setEnabled(true);
453 updateSelectedButton.setEnabled(true);
455 changeViewer.refresh();
461 }, new Callback<DatabaseException>() {
463 public void run(DatabaseException parameter) {
464 if (parameter != null)
465 ExceptionUtils.logAndShowError("Cannot update model", parameter);
470 protected void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DatabaseException {
471 if (mod.second == null) {
472 graph.deny(mod.first);
475 Resource s = changes.getComparable().getLeft(mod.second.getSubject());
476 Resource pred = mod.second.getPredicate();
477 if (graph.hasValue(mod.second.getObject())) {
478 Object value = graph.getValue(mod.second.getObject());
479 graph.claimLiteral(s, pred, value);
487 private void applySelected() {
488 updateAllButton.setEnabled(false);
489 updateSelectedButton.setEnabled(false);
490 getSession().asyncRequest(new WriteRequest(){
492 public void perform(WriteGraph graph) throws DatabaseException {
493 Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
494 graph.markUndoPoint();
495 for (Pair<Statement, Statement> mod : updateList.getSelected()) {
496 updateList.removeChange(mod);
497 applyLiteralChange(graph, mod);
500 updateTree.getUpdateOps().applySelected(graph);
502 Display.getDefault().asyncExec(new Runnable() {
506 changeViewer.refresh();
507 updateAllButton.setEnabled(true);
508 updateSelectedButton.setEnabled(true);
519 * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
520 * 2. Runs custom filters for value changes.
525 * @throws DatabaseException
527 protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException
530 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
532 for (Pair<Statement, Statement> mod : changes.getModifications()) {
534 boolean accept = true;
535 for (ChangeFilter filter : filters) {
536 if (!filter.accept(g, mod)) {
542 modifications.add(mod);
544 GraphChanges newChanges = new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
548 public interface ChangeFilter {
549 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
557 * Filters floating point value changes (default filter is set filter when the change is less than 1%)
560 protected class FPValueFilter implements ChangeFilter {
562 private double percentage = 0.01;
564 public FPValueFilter() {
568 public FPValueFilter(double percentage) {
569 if (percentage < 0.0 || percentage > 1.0)
570 throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
571 this.percentage = percentage;
575 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
576 //filter floating point values that have less than 1% difference.
577 if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
579 Object v1 = g.getValue(change.first.getObject());
580 Object v2 = g.getValue(change.second.getObject());
582 if (v1 instanceof Double && v2 instanceof Double) {
583 double d1 = (Double)v1;
584 double d2 = (Double)v2;
585 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
587 } else if (v1 instanceof Float && v2 instanceof Float) {
588 float d1 = (Float)v1;
589 float d2 = (Float)v2;
590 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
599 private class FilterChangesRead implements Read<GraphChanges> {
600 private GraphChanges changes;
601 public FilterChangesRead(GraphChanges changes) {
602 this.changes = changes;
606 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
607 return filterChanges(graph, changes);
612 private class ModificationListContentProvider implements IStructuredContentProvider {
614 @SuppressWarnings("unchecked")
616 public Object[] getElements(Object inputElement) {
617 if (inputElement == null)
619 Collection<Pair<Statement, Statement>> coll = (Collection<Pair<Statement, Statement>>)inputElement;
620 return coll.toArray();
624 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
629 public void dispose() {
634 private class UpdateTreeContentProvider implements ITreeContentProvider {
636 public Object[] getElements(Object inputElement) {
637 if (inputElement instanceof UpdateTree)
638 return new Object[]{((UpdateTree)inputElement).getRootNode()};
639 if (inputElement instanceof UpdateNode) {
640 UpdateNode node = (UpdateNode)inputElement;
641 return node.getChildren().toArray();
643 return new Object[0];
647 public Object getParent(Object element) {
652 public Object[] getChildren(Object parentElement) {
653 UpdateNode node = (UpdateNode)parentElement;
654 return node.getChildren().toArray();
658 public boolean hasChildren(Object element) {
659 UpdateNode node = (UpdateNode)element;
660 return node.getChildren().size() > 0;
664 public void dispose() {
669 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
674 private class SelectionLabelProvider extends ColumnLabelProvider {
676 public SelectionLabelProvider() {
680 public String getText(Object element) {
685 public Image getImage(Object element) {
686 if (updateList == null)
688 if (updateList.getSelected().contains(element))
695 private class UpdateNodeLabelProvider extends ColumnLabelProvider {
698 public String getText(Object element) {
699 final UpdateNode node = (UpdateNode)element;
700 return node.getLabel();
704 public Image getImage(Object element) {
705 final UpdateNode node = (UpdateNode)element;
707 ImageDescriptor id = getSession().syncRequest(new Read<ImageDescriptor>() {
709 public ImageDescriptor perform(ReadGraph graph) throws DatabaseException {
710 return node.getImage(graph);
713 return manager.createImage(id);
714 } catch (Exception e) {
720 public String getToolTipText(Object element) {
721 final UpdateNode node = (UpdateNode)element;
722 if (node.getOp() != null) {
723 return node.getOp().toString();
730 public int getToolTipDisplayDelayTime(Object object) {
735 public int getToolTipTimeDisplayed(Object object) {
740 public Color getBackground(Object element) {
741 final UpdateNode node = (UpdateNode)element;
742 Status status = node.getStatus();
743 if (status == Status.CONTAINS)
744 return containsColor;
745 if (status == Status.DELETED)
747 if (status == Status.NEW)
753 private class SelectionEditingSupport extends EditingSupport {
756 @SuppressWarnings("rawtypes")
757 public SelectionEditingSupport(ColumnViewer viewer) {
763 protected boolean canEdit(Object element) {
768 protected CellEditor getCellEditor(Object element) {
769 return new CheckboxCellEditor(null, SWT.CHECK);
773 protected Object getValue(Object element) {
774 if (updateList == null)
776 return updateList.getSelected().contains(element);
779 @SuppressWarnings("unchecked")
781 protected void setValue(Object element, Object value) {
782 if (updateList == null)
784 if (Boolean.TRUE.equals(value))
785 updateList.addSelected((Pair<Statement, Statement>) element);
787 updateList.removeSelected((Pair<Statement, Statement>) element);
789 getViewer().refresh(element);