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.UpdateTree;
61 import org.simantics.interop.utils.TableUtils;
62 import org.simantics.ui.workbench.ResourceEditorPart2;
63 import org.simantics.utils.datastructures.Callback;
64 import org.simantics.utils.datastructures.Pair;
65 import org.simantics.utils.ui.ExceptionUtils;
69 * Editor for updating models.
71 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
74 public abstract class ModelUpdateEditor extends ResourceEditorPart2 {
77 private Composite composite;
78 private Composite errorComposite;
80 private CheckboxTreeViewer changeBrowser;
81 private TableViewer changeViewer;
83 private GraphChanges changes;
84 private UpdateTree updateTree;
86 private Button updateAllButton;
87 private Button updateSelectedButton;
89 private LocalResourceManager manager = new LocalResourceManager(JFaceResources.getResources());
91 private Image checked;
92 private Image unchecked;
93 private Image warning;
95 private Color containsColor;
96 private Color deletedColor;
97 private Color addedColor;
99 private HashSet<Pair<Statement, Statement>> selected = new HashSet<Pair<Statement,Statement>>();
101 private HashSet<UpdateNode> selectedStructure = new HashSet<UpdateNode>();
103 private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
105 public ModelUpdateEditor() {
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"));
112 public void createPartControl(Composite parent) {
114 containsColor = new Color(parent.getDisplay(), new RGB(255,255,220));
115 deletedColor = new Color(parent.getDisplay(), new RGB(255,220,220));
116 addedColor = new Color(parent.getDisplay(), new RGB(220,255,220));
118 composite = new Composite(parent, SWT.NONE);
119 composite.setLayout(new GridLayout(1,false));
121 errorComposite = new Composite(composite, 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));
130 errorComposite.setVisible(false);
132 IEditorInput input = getEditorInput();
133 if (!(input instanceof UpdateEditorInput)) {
134 Label label = new Label(composite, SWT.NONE);
135 label.setText("Unknown input.");
139 Composite fillComposite = new Composite(composite, 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));
149 changeBrowser = new CheckboxTreeViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION );
151 changeBrowser.setContentProvider(new UpdateTreeContentProvider());
153 changeBrowser.getTree().setHeaderVisible(true);
155 ColumnViewerToolTipSupport.enableFor(changeBrowser);
158 TreeViewerColumn dataColumn = TableUtils.addColumn(changeBrowser, "Data", true, 600);
160 dataColumn.setLabelProvider(new UpdateNodeLabelProvicer());
162 changeBrowser.addCheckStateListener(new ICheckStateListener() {
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()));
175 changeBrowser.addTreeListener(new ITreeViewerListener() {
178 public void treeExpanded(TreeExpansionEvent event) {
179 event.getTreeViewer().getControl().getDisplay().asyncExec(new Runnable() {
190 public void treeCollapsed(TreeExpansionEvent event) {
194 changeBrowser.setUseHashlookup(true);
198 changeViewer = new TableViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION);
200 changeViewer.getTable().setHeaderVisible(true);
201 changeViewer.getTable().setLinesVisible(true);
203 changeViewer.setContentProvider(new ModificationListContentProvider());
205 changeViewer.setUseHashlookup(true);
207 TableViewerColumn selection = TableUtils.addColumn(changeViewer, getColumntTitle(0), false, false, 20);
208 TableViewerColumn diagram = TableUtils.addColumn(changeViewer, getColumntTitle(1), true, true, 100);
209 TableViewerColumn symbol = TableUtils.addColumn(changeViewer, getColumntTitle(2), true, true, 100);
210 TableViewerColumn property = TableUtils.addColumn(changeViewer, getColumntTitle(3), true, true, 100);
211 TableViewerColumn oldValue = TableUtils.addColumn(changeViewer, getColumntTitle(4), true, true, 100);
212 TableViewerColumn newValue = TableUtils.addColumn(changeViewer, getColumntTitle(5), true, true, 100);
214 diagram.setLabelProvider(getLabelProvider(1));
215 symbol.setLabelProvider(getLabelProvider(2));
216 property.setLabelProvider(getLabelProvider(3));
217 oldValue.setLabelProvider(getLabelProvider(4));
218 newValue.setLabelProvider(getLabelProvider(5));
220 selection.setLabelProvider(new SelectionLabelProvider(selected));
221 selection.getColumn().addSelectionListener(new SelectionAdapter() {
223 public void widgetSelected(SelectionEvent e) {
224 if (changes.getModifications().size() > 0) {
225 if (selected.contains(changes.getModifications().get(0))) {
226 for (Pair<Statement, Statement> nr : changes.getModifications())
229 for (Pair<Statement, Statement> nr : changes.getModifications())
232 changeViewer.refresh();
236 selection.setEditingSupport(new SelectionEditingSupport(changeViewer, selected));
239 Composite buttonComposite = new Composite(composite, SWT.NONE);
241 data = new GridData();
242 data.grabExcessHorizontalSpace = true;
243 data.grabExcessVerticalSpace = false;
244 data.horizontalAlignment = SWT.FILL;
245 data.verticalAlignment = SWT.BOTTOM;
247 buttonComposite.setLayoutData(data);
249 buttonComposite.setLayout(new GridLayout(3, false));
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);
259 updateAllButton = new Button(buttonComposite, SWT.PUSH);
260 updateAllButton.setText("Update All");
261 updateAllButton.addSelectionListener(new SelectionAdapter() {
263 public void widgetSelected(SelectionEvent e) {
267 updateSelectedButton = new Button(buttonComposite, SWT.PUSH);
268 updateSelectedButton.setText("Update Selected");
269 updateSelectedButton.addSelectionListener(new SelectionAdapter() {
271 public void widgetSelected(SelectionEvent e) {
280 protected String getColumntTitle(int i) {
295 throw new RuntimeException("Unknown column index" + i);
300 protected abstract ColumnLabelProvider getLabelProvider(int i);
301 protected abstract Pair<GraphComparator,Boolean> getChanges(Resource r1, Resource r2) throws DatabaseException;
302 protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
304 protected void addFilters(List<ChangeFilter> filters) {
308 public GraphChanges getChanges() {
312 private void showWarning(String text) {
313 errorComposite.setVisible(true);
315 Label label = new Label(errorComposite, SWT.NONE);
316 label.setImage(warning);
317 label = new Label(errorComposite, SWT.NONE);
319 //this.setStatusMessage("Update contains structural changes (new or deleted symbols), please create a new model.");
320 composite.layout(true);
324 private void updateSelection() {
325 Stack<UpdateNode> nodeStack = new Stack<UpdateNode>();
326 nodeStack.push((UpdateNode)updateTree.getRootNode());
327 while (!nodeStack.isEmpty()) {
328 UpdateNode n = nodeStack.pop();
329 if (n.getOp() != null) {
330 boolean applied = n.getOp().applied();
332 changeBrowser.setChecked(n, true);
333 changeBrowser.setGrayed(n, true);
334 selectedStructure.remove(n);
336 boolean sel = n.getOp().selected();
338 selectedStructure.add(n);
341 selectedStructure.remove(n);
343 changeBrowser.setChecked(n, sel);
344 changeBrowser.setGrayed(n, false);
347 changeBrowser.setGrayed(n, true);
348 changeBrowser.setChecked(n, true);
350 for (UpdateNode c : n.getChildren()) {
351 nodeStack.add((UpdateNode)c);
355 changeBrowser.refresh();
360 private void load() {
364 UpdateEditorInput uei = (UpdateEditorInput)getEditorInput();
365 Resource r1 = uei.getR1();
366 Resource r2 = uei.getR2();
371 Pair<GraphComparator,Boolean> result = getChanges(r1,r2);
372 GraphComparator comparator = result.first;
374 showWarning("Structural symbols have been changed. Model update is not able to update these, please create a new model.");
375 comparator.test(getSession());
376 changes = comparator.getChanges();
377 changes = getSession().syncRequest(new FilterChangesRead(changes));
378 updateTree = getUpdateTree(changes);
379 } catch (DatabaseException e) {
380 Text text = new Text(composite, SWT.MULTI);
381 text.setText(e.getMessage());
388 changeViewer.setInput(changes.getModifications());
389 changeBrowser.setInput(updateTree);
395 private void updateAll() {
396 updateAllButton.setEnabled(false);
397 updateSelectedButton.setEnabled(false);
398 getSession().asyncRequest(new WriteRequest(){
400 public void perform(WriteGraph graph) throws DatabaseException {
401 Layer0Utils.addCommentMetadata(graph, "Apply all model updates");
402 graph.markUndoPoint();
403 for (Pair<Statement, Statement> mod : changes.getModifications()) {
404 applyLiteralChange(graph, mod);
407 changes.getModifications().clear();
409 updateTree.getUpdateOps().applyAll(graph);
411 Display.getDefault().asyncExec(new Runnable() {
416 updateAllButton.setEnabled(true);
417 updateSelectedButton.setEnabled(true);
419 changeViewer.refresh();
425 }, new Callback<DatabaseException>() {
427 public void run(DatabaseException parameter) {
428 if (parameter != null)
429 ExceptionUtils.logAndShowError("Cannot update model", parameter);
434 private void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DoesNotContainValueException, ServiceException, ManyObjectsForFunctionalRelationException {
436 Resource s = changes.getComparable().getLeft(mod.second.getSubject());
437 Resource pred = mod.first.getPredicate();
438 if (graph.hasValue(mod.second.getObject())) {
439 Object value = graph.getValue(mod.second.getObject());
440 graph.claimLiteral(s, pred, value);
448 private void updateSelected() {
449 updateAllButton.setEnabled(false);
450 updateSelectedButton.setEnabled(false);
451 getSession().asyncRequest(new WriteRequest(){
453 public void perform(WriteGraph graph) throws DatabaseException {
454 Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
455 graph.markUndoPoint();
456 for (Pair<Statement, Statement> mod : selected) {
457 changes.getModifications().remove(mod);
458 applyLiteralChange(graph, mod);
462 updateTree.getUpdateOps().applySelected(graph);
464 Display.getDefault().asyncExec(new Runnable() {
468 changeViewer.refresh();
469 updateAllButton.setEnabled(true);
470 updateSelectedButton.setEnabled(true);
479 public void setFocus() {
480 composite.setFocus();
486 * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
487 * 2. Runs custom filters for value changes.
492 * @throws DatabaseException
494 protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException
497 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
499 for (Pair<Statement, Statement> mod : changes.getModifications()) {
501 boolean accept = true;
502 for (ChangeFilter filter : filters) {
503 if (!filter.accept(g, mod)) {
509 modifications.add(mod);
511 GraphChanges newChanges = new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
515 public interface ChangeFilter {
516 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
524 * Filters floating point value changes (default filter is set filter when the change is less than 1%)
527 protected class FPValueFilter implements ChangeFilter {
529 private double percentage = 0.01;
531 public FPValueFilter() {
535 public FPValueFilter(double percentage) {
536 if (percentage < 0.0 || percentage > 1.0)
537 throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
538 this.percentage = percentage;
542 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
543 //filter floating point values that have less than 1% difference.
544 if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
546 Object v1 = g.getValue(change.first.getObject());
547 Object v2 = g.getValue(change.second.getObject());
549 if (v1 instanceof Double && v2 instanceof Double) {
550 double d1 = (Double)v1;
551 double d2 = (Double)v2;
552 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
554 } else if (v1 instanceof Float && v2 instanceof Float) {
555 float d1 = (Float)v1;
556 float d2 = (Float)v2;
557 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
566 private class FilterChangesRead implements Read<GraphChanges> {
567 private GraphChanges changes;
568 public FilterChangesRead(GraphChanges changes) {
569 this.changes = changes;
573 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
574 return filterChanges(graph, changes);
579 private class ModificationListContentProvider implements IStructuredContentProvider {
581 @SuppressWarnings("unchecked")
583 public Object[] getElements(Object inputElement) {
584 if (inputElement == null)
586 Collection<Pair<Statement, Statement>> coll = (Collection<Pair<Statement, Statement>>)inputElement;
587 return coll.toArray();
591 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
596 public void dispose() {
601 private class UpdateTreeContentProvider implements ITreeContentProvider {
603 public Object[] getElements(Object inputElement) {
604 if (inputElement instanceof UpdateTree)
605 return new Object[]{((UpdateTree)inputElement).getRootNode()};
606 if (inputElement instanceof UpdateNode) {
607 UpdateNode node = (UpdateNode)inputElement;
608 return node.getChildren().toArray();
610 return new Object[0];
614 public Object getParent(Object element) {
619 public Object[] getChildren(Object parentElement) {
620 UpdateNode node = (UpdateNode)parentElement;
621 return node.getChildren().toArray();
625 public boolean hasChildren(Object element) {
626 UpdateNode node = (UpdateNode)element;
627 return node.getChildren().size() > 0;
631 public void dispose() {
636 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
641 private class SelectionLabelProvider extends ColumnLabelProvider {
643 Collection<?> selected;
644 public SelectionLabelProvider(Collection<?> selected) {
645 this.selected = selected;
648 public String getText(Object element) {
653 public Image getImage(Object element) {
654 if (selected.contains(element))
661 private class UpdateNodeLabelProvicer extends ColumnLabelProvider {
664 public String getText(Object element) {
665 final UpdateNode node = (UpdateNode)element;
667 return getSession().syncRequest(new Read<String>() {
669 public String perform(ReadGraph graph) throws DatabaseException {
670 return node.getLabel(graph);
673 } catch (Exception e) {
674 return e.getMessage();
679 public Image getImage(Object element) {
680 final UpdateNode node = (UpdateNode)element;
682 ImageDescriptor id = getSession().syncRequest(new Read<ImageDescriptor>() {
684 public ImageDescriptor perform(ReadGraph graph) throws DatabaseException {
685 return node.getImage(graph);
688 return manager.createImage(id);
689 } catch (Exception e) {
695 public String getToolTipText(Object element) {
696 final UpdateNode node = (UpdateNode)element;
697 if (node.getOp() != null) {
698 return node.getOp().toString();
705 public int getToolTipDisplayDelayTime(Object object) {
710 public int getToolTipTimeDisplayed(Object object) {
715 public Color getBackground(Object element) {
716 final UpdateNode node = (UpdateNode)element;
717 Status status = node.getStatus();
718 if (status == Status.CONTAINS)
719 return containsColor;
720 if (status == Status.DELETED)
722 if (status == Status.NEW)
728 private class SelectionEditingSupport extends EditingSupport {
730 @SuppressWarnings("rawtypes")
734 @SuppressWarnings("rawtypes")
735 public SelectionEditingSupport(ColumnViewer viewer, Collection selected) {
737 this.selected = selected;
742 protected boolean canEdit(Object element) {
747 protected CellEditor getCellEditor(Object element) {
748 return new CheckboxCellEditor(null, SWT.CHECK);
752 protected Object getValue(Object element) {
753 return selected.contains(element);
756 @SuppressWarnings("unchecked")
758 protected void setValue(Object element, Object value) {
759 if (Boolean.TRUE.equals(value))
760 selected.add(element);
762 selected.remove(element);
764 getViewer().refresh(element);