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.NoSingleResultException;
53 import org.simantics.db.exception.ServiceException;
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.test.NameComparator;
58 import org.simantics.interop.update.Activator;
59 import org.simantics.interop.update.model.UpdateNode;
60 import org.simantics.interop.update.model.UpdateNode.Status;
61 import org.simantics.interop.update.model.UpdateTree;
62 import org.simantics.interop.utils.TableUtils;
63 import org.simantics.layer0.Layer0;
64 import org.simantics.ui.workbench.ResourceEditorPart2;
65 import org.simantics.utils.datastructures.BijectionMap;
66 import org.simantics.utils.datastructures.Callback;
67 import org.simantics.utils.datastructures.Pair;
68 import org.simantics.utils.ui.ExceptionUtils;
72 * Editor for updating models.
74 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
77 public abstract class ModelUpdateEditor extends ResourceEditorPart2 {
80 private Composite composite;
81 private Composite errorComposite;
83 private CheckboxTreeViewer changeBrowser;
84 private TableViewer changeViewer;
86 private GraphChanges changes;
87 private UpdateTree updateTree;
89 private Button updateAllButton;
90 private Button updateSelectedButton;
92 private LocalResourceManager manager = new LocalResourceManager(JFaceResources.getResources());
94 private Image checked;
95 private Image unchecked;
96 private Image warning;
98 private Color containsColor;
99 private Color deletedColor;
100 private Color addedColor;
102 private HashSet<Pair<Statement, Statement>> selected = new HashSet<Pair<Statement,Statement>>();
104 private HashSet<UpdateNode> selectedStructure = new HashSet<UpdateNode>();
106 private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
108 public ModelUpdateEditor() {
109 checked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/tick.png"));
110 unchecked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/cross.png"));
111 warning = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/error.png"));
115 public void createPartControl(Composite parent) {
117 containsColor = new Color(parent.getDisplay(), new RGB(255,255,220));
118 deletedColor = new Color(parent.getDisplay(), new RGB(255,220,220));
119 addedColor = new Color(parent.getDisplay(), new RGB(220,255,220));
121 composite = new Composite(parent, SWT.NONE);
122 composite.setLayout(new GridLayout(1,false));
124 errorComposite = new Composite(composite, SWT.BORDER);
125 GridData data = new GridData();
126 data.grabExcessHorizontalSpace = true;
127 data.grabExcessVerticalSpace = false;
128 data.horizontalAlignment = SWT.FILL;
129 data.verticalAlignment = SWT.TOP;
130 errorComposite.setLayoutData(data);
131 errorComposite.setLayout(new GridLayout(2, false));
133 errorComposite.setVisible(false);
135 IEditorInput input = getEditorInput();
136 if (!(input instanceof UpdateEditorInput)) {
137 Label label = new Label(composite, SWT.NONE);
138 label.setText("Unknown input.");
142 Composite fillComposite = new Composite(composite, SWT.NONE);
143 data = new GridData();
144 data.grabExcessHorizontalSpace = true;
145 data.grabExcessVerticalSpace = true;
146 data.horizontalAlignment = SWT.FILL;
147 data.verticalAlignment = SWT.FILL;
148 fillComposite.setLayoutData(data);
149 fillComposite.setLayout(new FillLayout(SWT.VERTICAL));
152 changeBrowser = new CheckboxTreeViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION );
154 changeBrowser.setContentProvider(new UpdateTreeContentProvider());
156 changeBrowser.getTree().setHeaderVisible(true);
158 ColumnViewerToolTipSupport.enableFor(changeBrowser);
161 TreeViewerColumn dataColumn = TableUtils.addColumn(changeBrowser, "Data", true, 600);
163 dataColumn.setLabelProvider(new UpdateNodeLabelProvicer());
165 changeBrowser.addCheckStateListener(new ICheckStateListener() {
168 public void checkStateChanged(CheckStateChangedEvent event) {
169 UpdateNode node = (UpdateNode) event.getElement();
170 if (node.getOp() != null) {
171 node.getOp().select(Boolean.TRUE.equals(event.getChecked()));
178 changeBrowser.addTreeListener(new ITreeViewerListener() {
181 public void treeExpanded(TreeExpansionEvent event) {
182 event.getTreeViewer().getControl().getDisplay().asyncExec(new Runnable() {
193 public void treeCollapsed(TreeExpansionEvent event) {
197 changeBrowser.setUseHashlookup(true);
201 changeViewer = new TableViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION);
203 changeViewer.getTable().setHeaderVisible(true);
204 changeViewer.getTable().setLinesVisible(true);
206 changeViewer.setContentProvider(new ModificationListContentProvider());
208 changeViewer.setUseHashlookup(true);
210 TableViewerColumn selection = TableUtils.addColumn(changeViewer, getColumntTitle(0), false, false, 20);
211 TableViewerColumn diagram = TableUtils.addColumn(changeViewer, getColumntTitle(1), true, true, 100);
212 TableViewerColumn symbol = TableUtils.addColumn(changeViewer, getColumntTitle(2), true, true, 100);
213 TableViewerColumn property = TableUtils.addColumn(changeViewer, getColumntTitle(3), true, true, 100);
214 TableViewerColumn oldValue = TableUtils.addColumn(changeViewer, getColumntTitle(4), true, true, 100);
215 TableViewerColumn newValue = TableUtils.addColumn(changeViewer, getColumntTitle(5), true, true, 100);
217 diagram.setLabelProvider(getLabelProvider(1));
218 symbol.setLabelProvider(getLabelProvider(2));
219 property.setLabelProvider(getLabelProvider(3));
220 oldValue.setLabelProvider(getLabelProvider(4));
221 newValue.setLabelProvider(getLabelProvider(5));
223 selection.setLabelProvider(new SelectionLabelProvider(selected));
224 selection.getColumn().addSelectionListener(new SelectionAdapter() {
226 public void widgetSelected(SelectionEvent e) {
227 if (changes.getModifications().size() > 0) {
228 if (selected.contains(changes.getModifications().get(0))) {
229 for (Pair<Statement, Statement> nr : changes.getModifications())
232 for (Pair<Statement, Statement> nr : changes.getModifications())
235 changeViewer.refresh();
239 selection.setEditingSupport(new SelectionEditingSupport(changeViewer, selected));
242 Composite buttonComposite = new Composite(composite, SWT.NONE);
244 data = new GridData();
245 data.grabExcessHorizontalSpace = true;
246 data.grabExcessVerticalSpace = false;
247 data.horizontalAlignment = SWT.FILL;
248 data.verticalAlignment = SWT.BOTTOM;
250 buttonComposite.setLayoutData(data);
252 buttonComposite.setLayout(new GridLayout(3, false));
254 Label label = new Label(buttonComposite, SWT.NONE);
255 data = new GridData();
256 data.grabExcessHorizontalSpace = true;
257 data.grabExcessVerticalSpace = false;
258 data.horizontalAlignment = SWT.FILL;
259 data.verticalAlignment = SWT.CENTER;
260 label.setLayoutData(data);
262 updateAllButton = new Button(buttonComposite, SWT.PUSH);
263 updateAllButton.setText("Update All");
264 updateAllButton.addSelectionListener(new SelectionAdapter() {
266 public void widgetSelected(SelectionEvent e) {
270 updateSelectedButton = new Button(buttonComposite, SWT.PUSH);
271 updateSelectedButton.setText("Update Selected");
272 updateSelectedButton.addSelectionListener(new SelectionAdapter() {
274 public void widgetSelected(SelectionEvent e) {
283 protected String getColumntTitle(int i) {
298 throw new RuntimeException("Unknown column index" + i);
303 protected abstract ColumnLabelProvider getLabelProvider(int i);
304 protected abstract Pair<GraphComparator,Boolean> getChanges(Resource r1, Resource r2) throws DatabaseException;
305 protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
307 protected void addFilters(List<ChangeFilter> filters) {
311 public GraphChanges getChanges() {
315 private void showWarning(String text) {
316 errorComposite.setVisible(true);
318 Label label = new Label(errorComposite, SWT.NONE);
319 label.setImage(warning);
320 label = new Label(errorComposite, SWT.NONE);
322 //this.setStatusMessage("Update contains structural changes (new or deleted symbols), please create a new model.");
323 composite.layout(true);
327 private void updateSelection() {
328 Stack<UpdateNode> nodeStack = new Stack<UpdateNode>();
329 nodeStack.push((UpdateNode)updateTree.getRootNode());
330 while (!nodeStack.isEmpty()) {
331 UpdateNode n = nodeStack.pop();
332 if (n.getOp() != null) {
333 boolean applied = n.getOp().applied();
335 changeBrowser.setChecked(n, true);
336 changeBrowser.setGrayed(n, true);
337 selectedStructure.remove(n);
339 boolean sel = n.getOp().selected();
341 selectedStructure.add(n);
344 selectedStructure.remove(n);
346 changeBrowser.setChecked(n, sel);
347 changeBrowser.setGrayed(n, false);
350 changeBrowser.setGrayed(n, true);
351 changeBrowser.setChecked(n, true);
353 for (UpdateNode c : n.getChildren()) {
354 nodeStack.add((UpdateNode)c);
358 changeBrowser.refresh();
363 private void load() {
367 UpdateEditorInput uei = (UpdateEditorInput)getEditorInput();
368 Resource r1 = uei.getR1();
369 Resource r2 = uei.getR2();
374 Pair<GraphComparator,Boolean> result = getChanges(r1,r2);
375 GraphComparator comparator = result.first;
377 showWarning("Structural symbols have been changed. Model update is not able to update these, please create a new model.");
378 comparator.test(getSession());
379 changes = comparator.getChanges();
380 changes = getSession().syncRequest(new FilterChangesRead(changes));
381 updateTree = getUpdateTree(changes);
382 } catch (DatabaseException e) {
383 Text text = new Text(composite, SWT.MULTI);
384 text.setText(e.getMessage());
391 changeViewer.setInput(changes.getModifications());
392 changeBrowser.setInput(updateTree);
398 private void updateAll() {
399 updateAllButton.setEnabled(false);
400 updateSelectedButton.setEnabled(false);
401 getSession().asyncRequest(new WriteRequest(){
403 public void perform(WriteGraph graph) throws DatabaseException {
404 for (Pair<Statement, Statement> mod : changes.getModifications()) {
405 applyLiteralChange(graph, mod);
408 changes.getModifications().clear();
410 updateTree.getUpdateOps().applyAll(graph);
412 Display.getDefault().asyncExec(new Runnable() {
417 updateAllButton.setEnabled(true);
418 updateSelectedButton.setEnabled(true);
420 changeViewer.refresh();
426 }, new Callback<DatabaseException>() {
428 public void run(DatabaseException parameter) {
429 if (parameter != null)
430 ExceptionUtils.logAndShowError("Cannot update model", parameter);
435 private void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DoesNotContainValueException, ServiceException, ManyObjectsForFunctionalRelationException {
437 Resource s = changes.getComparable().getLeft(mod.second.getSubject());
438 Resource pred = mod.first.getPredicate();
439 if (graph.hasValue(mod.second.getObject())) {
440 Object value = graph.getValue(mod.second.getObject());
441 graph.claimLiteral(s, pred, value);
449 private void updateSelected() {
450 updateAllButton.setEnabled(false);
451 updateSelectedButton.setEnabled(false);
452 getSession().asyncRequest(new WriteRequest(){
454 public void perform(WriteGraph graph) throws DatabaseException {
455 for (Pair<Statement, Statement> mod : selected) {
456 changes.getModifications().remove(mod);
457 applyLiteralChange(graph, mod);
461 updateTree.getUpdateOps().applySelected(graph);
463 Display.getDefault().asyncExec(new Runnable() {
467 changeViewer.refresh();
468 updateAllButton.setEnabled(true);
469 updateSelectedButton.setEnabled(true);
478 public void setFocus() {
479 composite.setFocus();
485 * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
486 * 2. Runs custom filters for value changes.
491 * @throws DatabaseException
493 protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException
496 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
498 for (Pair<Statement, Statement> mod : changes.getModifications()) {
500 boolean accept = true;
501 for (ChangeFilter filter : filters) {
502 if (!filter.accept(g, mod)) {
508 modifications.add(mod);
510 GraphChanges newChanges = new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
514 public interface ChangeFilter {
515 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
523 * Filters floating point value changes (default filter is set filter when the change is less than 1%)
526 protected class FPValueFilter implements ChangeFilter {
528 private double percentage = 0.01;
530 public FPValueFilter() {
534 public FPValueFilter(double percentage) {
535 if (percentage < 0.0 || percentage > 1.0)
536 throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
537 this.percentage = percentage;
541 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
542 //filter floating point values that have less than 1% difference.
543 if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
545 Object v1 = g.getValue(change.first.getObject());
546 Object v2 = g.getValue(change.second.getObject());
548 if (v1 instanceof Double && v2 instanceof Double) {
549 double d1 = (Double)v1;
550 double d2 = (Double)v2;
551 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
553 } else if (v1 instanceof Float && v2 instanceof Float) {
554 float d1 = (Float)v1;
555 float d2 = (Float)v2;
556 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
565 private class FilterChangesRead implements Read<GraphChanges> {
566 private GraphChanges changes;
567 public FilterChangesRead(GraphChanges changes) {
568 this.changes = changes;
572 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
573 return filterChanges(graph, changes);
578 private class ModificationListContentProvider implements IStructuredContentProvider {
580 @SuppressWarnings("unchecked")
582 public Object[] getElements(Object inputElement) {
583 if (inputElement == null)
585 Collection<Pair<Statement, Statement>> coll = (Collection<Pair<Statement, Statement>>)inputElement;
586 return coll.toArray();
590 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
595 public void dispose() {
600 private class UpdateTreeContentProvider implements ITreeContentProvider {
602 public Object[] getElements(Object inputElement) {
603 if (inputElement instanceof UpdateTree)
604 return new Object[]{((UpdateTree)inputElement).getRootNode()};
605 if (inputElement instanceof UpdateNode) {
606 UpdateNode node = (UpdateNode)inputElement;
607 return node.getChildren().toArray();
609 return new Object[0];
613 public Object getParent(Object element) {
618 public Object[] getChildren(Object parentElement) {
619 UpdateNode node = (UpdateNode)parentElement;
620 return node.getChildren().toArray();
624 public boolean hasChildren(Object element) {
625 UpdateNode node = (UpdateNode)element;
626 return node.getChildren().size() > 0;
630 public void dispose() {
635 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
640 private class SelectionLabelProvider extends ColumnLabelProvider {
642 Collection<?> selected;
643 public SelectionLabelProvider(Collection<?> selected) {
644 this.selected = selected;
647 public String getText(Object element) {
652 public Image getImage(Object element) {
653 if (selected.contains(element))
660 private class UpdateNodeLabelProvicer extends ColumnLabelProvider {
663 public String getText(Object element) {
664 final UpdateNode node = (UpdateNode)element;
666 return getSession().syncRequest(new Read<String>() {
668 public String perform(ReadGraph graph) throws DatabaseException {
669 return node.getLabel(graph);
672 } catch (Exception e) {
673 return e.getMessage();
678 public Image getImage(Object element) {
679 final UpdateNode node = (UpdateNode)element;
681 ImageDescriptor id = getSession().syncRequest(new Read<ImageDescriptor>() {
683 public ImageDescriptor perform(ReadGraph graph) throws DatabaseException {
684 return node.getImage(graph);
687 return manager.createImage(id);
688 } catch (Exception e) {
694 public String getToolTipText(Object element) {
695 final UpdateNode node = (UpdateNode)element;
696 if (node.getOp() != null) {
697 return node.getOp().toString();
704 public int getToolTipDisplayDelayTime(Object object) {
709 public int getToolTipTimeDisplayed(Object object) {
714 public Color getBackground(Object element) {
715 final UpdateNode node = (UpdateNode)element;
716 Status status = node.getStatus();
717 if (status == Status.CONTAINS)
718 return containsColor;
719 if (status == Status.DELETED)
721 if (status == Status.NEW)
727 private class SelectionEditingSupport extends EditingSupport {
729 @SuppressWarnings("rawtypes")
733 @SuppressWarnings("rawtypes")
734 public SelectionEditingSupport(ColumnViewer viewer, Collection selected) {
736 this.selected = selected;
741 protected boolean canEdit(Object element) {
746 protected CellEditor getCellEditor(Object element) {
747 return new CheckboxCellEditor(null, SWT.CHECK);
751 protected Object getValue(Object element) {
752 return selected.contains(element);
755 @SuppressWarnings("unchecked")
757 protected void setValue(Object element, Object value) {
758 if (Boolean.TRUE.equals(value))
759 selected.add(element);
761 selected.remove(element);
763 getViewer().refresh(element);