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.eclipse.ui.IEditorInput;
44 import org.simantics.db.ReadGraph;
45 import org.simantics.db.Resource;
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.workbench.ResourceEditorPart2;
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 ResourceEditorPart2 {
78 private Composite composite;
79 private Composite errorComposite;
81 private CheckboxTreeViewer changeBrowser;
82 private TableViewer changeViewer;
84 private GraphChanges changes;
85 private UpdateTree updateTree;
87 private Button updateAllButton;
88 private Button updateSelectedButton;
90 private LocalResourceManager manager = new LocalResourceManager(JFaceResources.getResources());
92 private Image checked;
93 private Image unchecked;
94 private Image warning;
96 private Color containsColor;
97 private Color deletedColor;
98 private Color addedColor;
100 private HashSet<Pair<Statement, Statement>> selected = new HashSet<Pair<Statement,Statement>>();
102 private HashSet<UpdateNode> selectedStructure = new HashSet<UpdateNode>();
104 private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
106 public ModelUpdateEditor() {
107 checked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/tick.png"));
108 unchecked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/cross.png"));
109 warning = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/error.png"));
113 public void createPartControl(Composite parent) {
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));
119 composite = new Composite(parent, SWT.NONE);
120 composite.setLayout(new GridLayout(1,false));
122 errorComposite = new Composite(composite, SWT.BORDER);
123 GridData data = new GridData();
124 data.grabExcessHorizontalSpace = true;
125 data.grabExcessVerticalSpace = false;
126 data.horizontalAlignment = SWT.FILL;
127 data.verticalAlignment = SWT.TOP;
128 errorComposite.setLayoutData(data);
129 errorComposite.setLayout(new GridLayout(2, false));
131 errorComposite.setVisible(false);
133 IEditorInput input = getEditorInput();
134 if (!(input instanceof UpdateEditorInput)) {
135 Label label = new Label(composite, SWT.NONE);
136 label.setText("Unknown input.");
140 Composite fillComposite = new Composite(composite, SWT.NONE);
141 data = new GridData();
142 data.grabExcessHorizontalSpace = true;
143 data.grabExcessVerticalSpace = true;
144 data.horizontalAlignment = SWT.FILL;
145 data.verticalAlignment = SWT.FILL;
146 fillComposite.setLayoutData(data);
147 fillComposite.setLayout(new FillLayout(SWT.VERTICAL));
150 changeBrowser = new CheckboxTreeViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION );
152 changeBrowser.setContentProvider(new UpdateTreeContentProvider());
154 changeBrowser.getTree().setHeaderVisible(true);
156 ColumnViewerToolTipSupport.enableFor(changeBrowser);
159 TreeViewerColumn dataColumn = TableUtils.addColumn(changeBrowser, "Data", true, 600);
161 dataColumn.setLabelProvider(new UpdateNodeLabelProvider());
163 changeBrowser.addCheckStateListener(new ICheckStateListener() {
166 public void checkStateChanged(CheckStateChangedEvent event) {
167 UpdateNode node = (UpdateNode) event.getElement();
168 if (node.getOp() != null) {
169 node.getOp().select(Boolean.TRUE.equals(event.getChecked()));
176 changeBrowser.addTreeListener(new ITreeViewerListener() {
179 public void treeExpanded(TreeExpansionEvent event) {
180 event.getTreeViewer().getControl().getDisplay().asyncExec(new Runnable() {
191 public void treeCollapsed(TreeExpansionEvent event) {
195 changeBrowser.setUseHashlookup(true);
199 changeViewer = new TableViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION);
201 changeViewer.getTable().setHeaderVisible(true);
202 changeViewer.getTable().setLinesVisible(true);
204 changeViewer.setContentProvider(new ModificationListContentProvider());
206 changeViewer.setUseHashlookup(true);
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);
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));
221 selection.setLabelProvider(new SelectionLabelProvider(selected));
222 selection.getColumn().addSelectionListener(new SelectionAdapter() {
224 public void widgetSelected(SelectionEvent e) {
225 if (changes.getModifications().size() > 0) {
226 if (selected.contains(changes.getModifications().get(0))) {
227 for (Pair<Statement, Statement> nr : changes.getModifications())
230 for (Pair<Statement, Statement> nr : changes.getModifications())
233 changeViewer.refresh();
237 selection.setEditingSupport(new SelectionEditingSupport(changeViewer, selected));
240 Composite buttonComposite = new Composite(composite, SWT.NONE);
242 data = new GridData();
243 data.grabExcessHorizontalSpace = true;
244 data.grabExcessVerticalSpace = false;
245 data.horizontalAlignment = SWT.FILL;
246 data.verticalAlignment = SWT.BOTTOM;
248 buttonComposite.setLayoutData(data);
250 buttonComposite.setLayout(new GridLayout(3, false));
252 Label label = new Label(buttonComposite, SWT.NONE);
253 data = new GridData();
254 data.grabExcessHorizontalSpace = true;
255 data.grabExcessVerticalSpace = false;
256 data.horizontalAlignment = SWT.FILL;
257 data.verticalAlignment = SWT.CENTER;
258 label.setLayoutData(data);
260 updateAllButton = new Button(buttonComposite, SWT.PUSH);
261 updateAllButton.setText("Update All");
262 updateAllButton.addSelectionListener(new SelectionAdapter() {
264 public void widgetSelected(SelectionEvent e) {
268 updateSelectedButton = new Button(buttonComposite, SWT.PUSH);
269 updateSelectedButton.setText("Update Selected");
270 updateSelectedButton.addSelectionListener(new SelectionAdapter() {
272 public void widgetSelected(SelectionEvent e) {
281 protected String getColumntTitle(int i) {
296 throw new RuntimeException("Unknown column index" + i);
301 protected abstract ColumnLabelProvider getLabelProvider(int i);
302 protected abstract Pair<GraphComparator,Boolean> getChanges(Resource r1, Resource r2) throws DatabaseException;
303 protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
305 protected void addFilters(List<ChangeFilter> filters) {
309 public GraphChanges getChanges() {
313 private void showWarning(String text) {
314 errorComposite.setVisible(true);
316 Label label = new Label(errorComposite, SWT.NONE);
317 label.setImage(warning);
318 label = new Label(errorComposite, SWT.NONE);
320 //this.setStatusMessage("Update contains structural changes (new or deleted symbols), please create a new model.");
321 composite.layout(true);
325 private void updateSelection() {
326 Stack<UpdateNode> nodeStack = new Stack<UpdateNode>();
327 nodeStack.push((UpdateNode)updateTree.getRootNode());
328 while (!nodeStack.isEmpty()) {
329 UpdateNode n = nodeStack.pop();
330 if (n.getOp() != null) {
331 UpdateOp op = n.getOp();
332 if (!op.isAdd() && !op.isDelete()) {
333 changeBrowser.setGrayed(n, true);
334 changeBrowser.setChecked(n, true);
336 boolean applied = op.applied();
338 changeBrowser.setChecked(n, true);
339 changeBrowser.setGrayed(n, true);
340 selectedStructure.remove(n);
342 boolean sel = op.selected();
344 selectedStructure.add(n);
347 selectedStructure.remove(n);
349 changeBrowser.setChecked(n, sel);
350 changeBrowser.setGrayed(n, false);
354 changeBrowser.setGrayed(n, true);
355 changeBrowser.setChecked(n, true);
357 for (UpdateNode c : n.getChildren()) {
358 nodeStack.add((UpdateNode)c);
362 changeBrowser.refresh();
367 private void load() {
371 UpdateEditorInput uei = (UpdateEditorInput)getEditorInput();
372 Resource r1 = uei.getR1();
373 Resource r2 = uei.getR2();
378 Pair<GraphComparator,Boolean> result = getChanges(r1,r2);
379 GraphComparator comparator = result.first;
381 showWarning("Structural symbols have been changed. Model update is not able to update these, please create a new model.");
382 comparator.test(getSession());
383 changes = comparator.getChanges();
384 changes = getSession().syncRequest(new FilterChangesRead(changes));
385 updateTree = getUpdateTree(changes);
386 } catch (DatabaseException e) {
387 Text text = new Text(composite, SWT.MULTI);
388 text.setText(e.getMessage());
395 changeViewer.setInput(changes.getModifications());
396 changeBrowser.setInput(updateTree);
402 private void updateAll() {
403 updateAllButton.setEnabled(false);
404 updateSelectedButton.setEnabled(false);
405 getSession().asyncRequest(new WriteRequest(){
407 public void perform(WriteGraph graph) throws DatabaseException {
408 Layer0Utils.addCommentMetadata(graph, "Apply all model updates");
409 graph.markUndoPoint();
410 for (Pair<Statement, Statement> mod : changes.getModifications()) {
411 applyLiteralChange(graph, mod);
414 changes.getModifications().clear();
416 updateTree.getUpdateOps().applyAll(graph);
418 Display.getDefault().asyncExec(new Runnable() {
423 updateAllButton.setEnabled(true);
424 updateSelectedButton.setEnabled(true);
426 changeViewer.refresh();
432 }, new Callback<DatabaseException>() {
434 public void run(DatabaseException parameter) {
435 if (parameter != null)
436 ExceptionUtils.logAndShowError("Cannot update model", parameter);
441 private void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DoesNotContainValueException, ServiceException, ManyObjectsForFunctionalRelationException {
443 Resource s = changes.getComparable().getLeft(mod.second.getSubject());
444 Resource pred = mod.first.getPredicate();
445 if (graph.hasValue(mod.second.getObject())) {
446 Object value = graph.getValue(mod.second.getObject());
447 graph.claimLiteral(s, pred, value);
455 private void updateSelected() {
456 updateAllButton.setEnabled(false);
457 updateSelectedButton.setEnabled(false);
458 getSession().asyncRequest(new WriteRequest(){
460 public void perform(WriteGraph graph) throws DatabaseException {
461 Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
462 graph.markUndoPoint();
463 for (Pair<Statement, Statement> mod : selected) {
464 changes.getModifications().remove(mod);
465 applyLiteralChange(graph, mod);
469 updateTree.getUpdateOps().applySelected(graph);
471 Display.getDefault().asyncExec(new Runnable() {
475 changeViewer.refresh();
476 updateAllButton.setEnabled(true);
477 updateSelectedButton.setEnabled(true);
486 public void setFocus() {
487 composite.setFocus();
493 * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
494 * 2. Runs custom filters for value changes.
499 * @throws DatabaseException
501 protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException
504 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
506 for (Pair<Statement, Statement> mod : changes.getModifications()) {
508 boolean accept = true;
509 for (ChangeFilter filter : filters) {
510 if (!filter.accept(g, mod)) {
516 modifications.add(mod);
518 GraphChanges newChanges = new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
522 public interface ChangeFilter {
523 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
531 * Filters floating point value changes (default filter is set filter when the change is less than 1%)
534 protected class FPValueFilter implements ChangeFilter {
536 private double percentage = 0.01;
538 public FPValueFilter() {
542 public FPValueFilter(double percentage) {
543 if (percentage < 0.0 || percentage > 1.0)
544 throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
545 this.percentage = percentage;
549 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
550 //filter floating point values that have less than 1% difference.
551 if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
553 Object v1 = g.getValue(change.first.getObject());
554 Object v2 = g.getValue(change.second.getObject());
556 if (v1 instanceof Double && v2 instanceof Double) {
557 double d1 = (Double)v1;
558 double d2 = (Double)v2;
559 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
561 } else if (v1 instanceof Float && v2 instanceof Float) {
562 float d1 = (Float)v1;
563 float d2 = (Float)v2;
564 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
573 private class FilterChangesRead implements Read<GraphChanges> {
574 private GraphChanges changes;
575 public FilterChangesRead(GraphChanges changes) {
576 this.changes = changes;
580 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
581 return filterChanges(graph, changes);
586 private class ModificationListContentProvider implements IStructuredContentProvider {
588 @SuppressWarnings("unchecked")
590 public Object[] getElements(Object inputElement) {
591 if (inputElement == null)
593 Collection<Pair<Statement, Statement>> coll = (Collection<Pair<Statement, Statement>>)inputElement;
594 return coll.toArray();
598 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
603 public void dispose() {
608 private class UpdateTreeContentProvider implements ITreeContentProvider {
610 public Object[] getElements(Object inputElement) {
611 if (inputElement instanceof UpdateTree)
612 return new Object[]{((UpdateTree)inputElement).getRootNode()};
613 if (inputElement instanceof UpdateNode) {
614 UpdateNode node = (UpdateNode)inputElement;
615 return node.getChildren().toArray();
617 return new Object[0];
621 public Object getParent(Object element) {
626 public Object[] getChildren(Object parentElement) {
627 UpdateNode node = (UpdateNode)parentElement;
628 return node.getChildren().toArray();
632 public boolean hasChildren(Object element) {
633 UpdateNode node = (UpdateNode)element;
634 return node.getChildren().size() > 0;
638 public void dispose() {
643 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
648 private class SelectionLabelProvider extends ColumnLabelProvider {
650 Collection<?> selected;
651 public SelectionLabelProvider(Collection<?> selected) {
652 this.selected = selected;
655 public String getText(Object element) {
660 public Image getImage(Object element) {
661 if (selected.contains(element))
668 private class UpdateNodeLabelProvider extends ColumnLabelProvider {
671 public String getText(Object element) {
672 final UpdateNode node = (UpdateNode)element;
674 return getSession().syncRequest(new Read<String>() {
676 public String perform(ReadGraph graph) throws DatabaseException {
677 return node.getLabel(graph);
680 } catch (Exception e) {
681 return e.getMessage();
686 public Image getImage(Object element) {
687 final UpdateNode node = (UpdateNode)element;
689 ImageDescriptor id = getSession().syncRequest(new Read<ImageDescriptor>() {
691 public ImageDescriptor perform(ReadGraph graph) throws DatabaseException {
692 return node.getImage(graph);
695 return manager.createImage(id);
696 } catch (Exception e) {
702 public String getToolTipText(Object element) {
703 final UpdateNode node = (UpdateNode)element;
704 if (node.getOp() != null) {
705 return node.getOp().toString();
712 public int getToolTipDisplayDelayTime(Object object) {
717 public int getToolTipTimeDisplayed(Object object) {
722 public Color getBackground(Object element) {
723 final UpdateNode node = (UpdateNode)element;
724 Status status = node.getStatus();
725 if (status == Status.CONTAINS)
726 return containsColor;
727 if (status == Status.DELETED)
729 if (status == Status.NEW)
735 private class SelectionEditingSupport extends EditingSupport {
737 @SuppressWarnings("rawtypes")
741 @SuppressWarnings("rawtypes")
742 public SelectionEditingSupport(ColumnViewer viewer, Collection selected) {
744 this.selected = selected;
749 protected boolean canEdit(Object element) {
754 protected CellEditor getCellEditor(Object element) {
755 return new CheckboxCellEditor(null, SWT.CHECK);
759 protected Object getValue(Object element) {
760 return selected.contains(element);
763 @SuppressWarnings("unchecked")
765 protected void setValue(Object element, Object value) {
766 if (Boolean.TRUE.equals(value))
767 selected.add(element);
769 selected.remove(element);
771 getViewer().refresh(element);