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;
97 private HashSet<Pair<Statement, Statement>> selected = new HashSet<Pair<Statement,Statement>>();
99 private HashSet<UpdateNode> selectedStructure = new HashSet<UpdateNode>();
101 private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
103 public ModelUpdateEditor(Composite parent) {
104 super(parent,SWT.NONE);
105 checked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/tick.png"));
106 unchecked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/cross.png"));
107 warning = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/error.png"));
110 containsColor = new Color(parent.getDisplay(), new RGB(255,255,220));
111 deletedColor = new Color(parent.getDisplay(), new RGB(255,220,220));
112 addedColor = new Color(parent.getDisplay(), new RGB(220,255,220));
114 this.setLayout(new GridLayout(1,false));
116 errorComposite = new Composite(this, SWT.BORDER);
117 GridData data = new GridData();
118 data.grabExcessHorizontalSpace = true;
119 data.grabExcessVerticalSpace = false;
120 data.horizontalAlignment = SWT.FILL;
121 data.verticalAlignment = SWT.TOP;
122 errorComposite.setLayoutData(data);
123 errorComposite.setLayout(new GridLayout(2, false));
125 errorComposite.setVisible(false);
127 // IEditorInput input = getEditorInput();
128 // if (!(input instanceof UpdateEditorInput)) {
129 // Label label = new Label(composite, SWT.NONE);
130 // label.setText("Unknown input.");
134 Composite fillComposite = new Composite(this, SWT.NONE);
135 data = new GridData();
136 data.grabExcessHorizontalSpace = true;
137 data.grabExcessVerticalSpace = true;
138 data.horizontalAlignment = SWT.FILL;
139 data.verticalAlignment = SWT.FILL;
140 fillComposite.setLayoutData(data);
141 fillComposite.setLayout(new FillLayout(SWT.VERTICAL));
144 changeBrowser = new CheckboxTreeViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION );
146 changeBrowser.setContentProvider(new UpdateTreeContentProvider());
148 changeBrowser.getTree().setHeaderVisible(true);
150 ColumnViewerToolTipSupport.enableFor(changeBrowser);
153 TreeViewerColumn dataColumn = TableUtils.addColumn(changeBrowser, "Data", true, 600);
155 dataColumn.setLabelProvider(new UpdateNodeLabelProvider());
157 changeBrowser.addCheckStateListener(new ICheckStateListener() {
160 public void checkStateChanged(CheckStateChangedEvent event) {
161 UpdateNode node = (UpdateNode) event.getElement();
162 if (node.getOp() != null) {
163 node.getOp().select(Boolean.TRUE.equals(event.getChecked()));
170 changeBrowser.addTreeListener(new ITreeViewerListener() {
173 public void treeExpanded(TreeExpansionEvent event) {
174 event.getTreeViewer().getControl().getDisplay().asyncExec(new Runnable() {
178 // TreeViewer uses lazy load, checked states must be updated when the tree is expanded.
186 public void treeCollapsed(TreeExpansionEvent event) {
190 changeBrowser.setUseHashlookup(true);
194 changeViewer = new TableViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION);
196 changeViewer.getTable().setHeaderVisible(true);
197 changeViewer.getTable().setLinesVisible(true);
199 changeViewer.setContentProvider(new ModificationListContentProvider());
201 changeViewer.setUseHashlookup(true);
203 TableViewerColumn selection = TableUtils.addColumn(changeViewer, getColumntTitle(0), false, false, 20);
204 TableViewerColumn diagram = TableUtils.addColumn(changeViewer, getColumntTitle(1), true, true, 100);
205 TableViewerColumn symbol = TableUtils.addColumn(changeViewer, getColumntTitle(2), true, true, 100);
206 TableViewerColumn property = TableUtils.addColumn(changeViewer, getColumntTitle(3), true, true, 100);
207 TableViewerColumn oldValue = TableUtils.addColumn(changeViewer, getColumntTitle(4), true, true, 100);
208 TableViewerColumn newValue = TableUtils.addColumn(changeViewer, getColumntTitle(5), true, true, 100);
210 diagram.setLabelProvider(getLabelProvider(1));
211 symbol.setLabelProvider(getLabelProvider(2));
212 property.setLabelProvider(getLabelProvider(3));
213 oldValue.setLabelProvider(getLabelProvider(4));
214 newValue.setLabelProvider(getLabelProvider(5));
216 selection.setLabelProvider(new SelectionLabelProvider(selected));
217 selection.getColumn().addSelectionListener(new SelectionAdapter() {
219 public void widgetSelected(SelectionEvent e) {
220 if (updateList.getChanges().size() > 0) {
221 if (selected.contains(updateList.getChanges().iterator().next())) {
222 for (Pair<Statement, Statement> nr : updateList.getChanges())
225 for (Pair<Statement, Statement> nr : updateList.getChanges())
228 changeViewer.refresh();
232 selection.setEditingSupport(new SelectionEditingSupport(changeViewer, selected));
235 Composite buttonComposite = new Composite(this, SWT.NONE);
237 data = new GridData();
238 data.grabExcessHorizontalSpace = true;
239 data.grabExcessVerticalSpace = false;
240 data.horizontalAlignment = SWT.FILL;
241 data.verticalAlignment = SWT.BOTTOM;
243 buttonComposite.setLayoutData(data);
245 buttonComposite.setLayout(new GridLayout(3, false));
247 Label label = new Label(buttonComposite, SWT.NONE);
248 data = new GridData();
249 data.grabExcessHorizontalSpace = true;
250 data.grabExcessVerticalSpace = false;
251 data.horizontalAlignment = SWT.FILL;
252 data.verticalAlignment = SWT.CENTER;
253 label.setLayoutData(data);
255 updateAllButton = new Button(buttonComposite, SWT.PUSH);
256 updateAllButton.setText("Update All");
257 updateAllButton.addSelectionListener(new SelectionAdapter() {
259 public void widgetSelected(SelectionEvent e) {
263 updateSelectedButton = new Button(buttonComposite, SWT.PUSH);
264 updateSelectedButton.setText("Update Selected");
265 updateSelectedButton.addSelectionListener(new SelectionAdapter() {
267 public void widgetSelected(SelectionEvent e) {
273 protected Session getSession() {
274 return SimanticsUI.getSession();
277 protected String getColumntTitle(int i) {
292 throw new RuntimeException("Unknown column index" + i);
297 protected abstract ColumnLabelProvider getLabelProvider(int i);
298 protected abstract Pair<GraphComparator,Boolean> getChanges(Resource r1, Resource r2) throws DatabaseException;
299 protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
300 protected UpdateList getUpdateList(GraphChanges changes) throws DatabaseException {
301 return new UpdateList(changes.getModifications());
304 protected void addFilters(List<ChangeFilter> filters) {
308 public GraphChanges getChanges() {
312 public UpdateTree getUpdateTree() {
316 public UpdateList getUpdateList() {
320 public CheckboxTreeViewer getChangeBrowser() {
321 return changeBrowser;
324 public TableViewer getChangeViewer() {
328 private void showWarning(String text) {
329 errorComposite.setVisible(true);
331 Label label = new Label(errorComposite, SWT.NONE);
332 label.setImage(warning);
333 label = new Label(errorComposite, SWT.NONE);
335 //this.setStatusMessage("Update contains structural changes (new or deleted symbols), please create a new model.");
340 private List<ICheckStateListener> checkStateListeners = new ArrayList<>();
343 public void addCheckStateListener(ICheckStateListener listener) {
344 checkStateListeners.add(listener);
347 public void removeCheckStateListener(ICheckStateListener listener) {
348 checkStateListeners.remove(listener);
351 public void refreshChecked() {
352 Stack<UpdateNode> nodeStack = new Stack<UpdateNode>();
353 nodeStack.push((UpdateNode)updateTree.getRootNode());
354 while (!nodeStack.isEmpty()) {
355 UpdateNode n = nodeStack.pop();
356 if (n.getOp() != null) {
357 UpdateOp op = n.getOp();
358 if (!op.isAdd() && !op.isDelete()) {
359 changeBrowser.setGrayed(n, true);
360 changeBrowser.setChecked(n, true);
362 boolean applied = op.applied();
364 changeBrowser.setChecked(n, true);
365 changeBrowser.setGrayed(n, true);
366 selectedStructure.remove(n);
368 boolean sel = op.selected();
370 selectedStructure.add(n);
373 selectedStructure.remove(n);
375 changeBrowser.setChecked(n, sel);
376 changeBrowser.setGrayed(n, false);
380 changeBrowser.setGrayed(n, true);
381 changeBrowser.setChecked(n, true);
383 for (UpdateNode c : n.getChildren()) {
384 nodeStack.add((UpdateNode)c);
388 changeBrowser.refresh();
389 for (ICheckStateListener l : checkStateListeners) {
390 l.checkStateChanged(new CheckStateChangedEvent(changeBrowser, null, false));
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);
446 updateTree.getUpdateOps().applyAll(graph);
448 Display.getDefault().asyncExec(new Runnable() {
453 updateAllButton.setEnabled(true);
454 updateSelectedButton.setEnabled(true);
456 changeViewer.refresh();
462 }, new Callback<DatabaseException>() {
464 public void run(DatabaseException parameter) {
465 if (parameter != null)
466 ExceptionUtils.logAndShowError("Cannot update model", parameter);
471 protected void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DatabaseException {
472 if (mod.second == null) {
473 graph.deny(mod.first);
476 Resource s = changes.getComparable().getLeft(mod.second.getSubject());
477 Resource pred = mod.second.getPredicate();
478 if (graph.hasValue(mod.second.getObject())) {
479 Object value = graph.getValue(mod.second.getObject());
480 graph.claimLiteral(s, pred, value);
488 private void applySelected() {
489 updateAllButton.setEnabled(false);
490 updateSelectedButton.setEnabled(false);
491 getSession().asyncRequest(new WriteRequest(){
493 public void perform(WriteGraph graph) throws DatabaseException {
494 Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
495 graph.markUndoPoint();
496 for (Pair<Statement, Statement> mod : selected) {
497 updateList.removeChange(mod);
498 applyLiteralChange(graph, mod);
502 updateTree.getUpdateOps().applySelected(graph);
504 Display.getDefault().asyncExec(new Runnable() {
508 changeViewer.refresh();
509 updateAllButton.setEnabled(true);
510 updateSelectedButton.setEnabled(true);
521 * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
522 * 2. Runs custom filters for value changes.
527 * @throws DatabaseException
529 protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException
532 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
534 for (Pair<Statement, Statement> mod : changes.getModifications()) {
536 boolean accept = true;
537 for (ChangeFilter filter : filters) {
538 if (!filter.accept(g, mod)) {
544 modifications.add(mod);
546 GraphChanges newChanges = new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
550 public interface ChangeFilter {
551 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
559 * Filters floating point value changes (default filter is set filter when the change is less than 1%)
562 protected class FPValueFilter implements ChangeFilter {
564 private double percentage = 0.01;
566 public FPValueFilter() {
570 public FPValueFilter(double percentage) {
571 if (percentage < 0.0 || percentage > 1.0)
572 throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
573 this.percentage = percentage;
577 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
578 //filter floating point values that have less than 1% difference.
579 if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
581 Object v1 = g.getValue(change.first.getObject());
582 Object v2 = g.getValue(change.second.getObject());
584 if (v1 instanceof Double && v2 instanceof Double) {
585 double d1 = (Double)v1;
586 double d2 = (Double)v2;
587 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
589 } else if (v1 instanceof Float && v2 instanceof Float) {
590 float d1 = (Float)v1;
591 float d2 = (Float)v2;
592 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
601 private class FilterChangesRead implements Read<GraphChanges> {
602 private GraphChanges changes;
603 public FilterChangesRead(GraphChanges changes) {
604 this.changes = changes;
608 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
609 return filterChanges(graph, changes);
614 private class ModificationListContentProvider implements IStructuredContentProvider {
616 @SuppressWarnings("unchecked")
618 public Object[] getElements(Object inputElement) {
619 if (inputElement == null)
621 Collection<Pair<Statement, Statement>> coll = (Collection<Pair<Statement, Statement>>)inputElement;
622 return coll.toArray();
626 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
631 public void dispose() {
636 private class UpdateTreeContentProvider implements ITreeContentProvider {
638 public Object[] getElements(Object inputElement) {
639 if (inputElement instanceof UpdateTree)
640 return new Object[]{((UpdateTree)inputElement).getRootNode()};
641 if (inputElement instanceof UpdateNode) {
642 UpdateNode node = (UpdateNode)inputElement;
643 return node.getChildren().toArray();
645 return new Object[0];
649 public Object getParent(Object element) {
654 public Object[] getChildren(Object parentElement) {
655 UpdateNode node = (UpdateNode)parentElement;
656 return node.getChildren().toArray();
660 public boolean hasChildren(Object element) {
661 UpdateNode node = (UpdateNode)element;
662 return node.getChildren().size() > 0;
666 public void dispose() {
671 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
676 private class SelectionLabelProvider extends ColumnLabelProvider {
678 Collection<?> selected;
679 public SelectionLabelProvider(Collection<?> selected) {
680 this.selected = selected;
683 public String getText(Object element) {
688 public Image getImage(Object element) {
689 if (selected.contains(element))
696 private class UpdateNodeLabelProvider extends ColumnLabelProvider {
699 public String getText(Object element) {
700 final UpdateNode node = (UpdateNode)element;
701 return node.getLabel();
705 public Image getImage(Object element) {
706 final UpdateNode node = (UpdateNode)element;
708 ImageDescriptor id = getSession().syncRequest(new Read<ImageDescriptor>() {
710 public ImageDescriptor perform(ReadGraph graph) throws DatabaseException {
711 return node.getImage(graph);
714 return manager.createImage(id);
715 } catch (Exception e) {
721 public String getToolTipText(Object element) {
722 final UpdateNode node = (UpdateNode)element;
723 if (node.getOp() != null) {
724 return node.getOp().toString();
731 public int getToolTipDisplayDelayTime(Object object) {
736 public int getToolTipTimeDisplayed(Object object) {
741 public Color getBackground(Object element) {
742 final UpdateNode node = (UpdateNode)element;
743 Status status = node.getStatus();
744 if (status == Status.CONTAINS)
745 return containsColor;
746 if (status == Status.DELETED)
748 if (status == Status.NEW)
754 private class SelectionEditingSupport extends EditingSupport {
756 @SuppressWarnings("rawtypes")
760 @SuppressWarnings("rawtypes")
761 public SelectionEditingSupport(ColumnViewer viewer, Collection selected) {
763 this.selected = selected;
768 protected boolean canEdit(Object element) {
773 protected CellEditor getCellEditor(Object element) {
774 return new CheckboxCellEditor(null, SWT.CHECK);
778 protected Object getValue(Object element) {
779 return selected.contains(element);
782 @SuppressWarnings("unchecked")
784 protected void setValue(Object element, Object value) {
785 if (Boolean.TRUE.equals(value))
786 selected.add(element);
788 selected.remove(element);
790 getViewer().refresh(element);