]> gerrit.simantics Code Review - simantics/interop.git/commitdiff
Separating model update logic from UI
authorMarko Luukkainen <marko.luukkainen@vtt.fi>
Tue, 3 Oct 2017 15:04:28 +0000 (18:04 +0300)
committerMarko Luukkainen <marko.luukkainen@vtt.fi>
Tue, 3 Oct 2017 15:04:28 +0000 (18:04 +0300)
org.simantics.interop.update/src/org/simantics/interop/update/editor/ModelUpdateEditor.java
org.simantics.interop.update/src/org/simantics/interop/update/model/ModelUpdate.java [new file with mode: 0644]

index 4c172997bd1068f490dd267b86f125760e3af021..4e30b221a68f668fe7fe08a0d0a58fcec7502a33 100644 (file)
@@ -4,7 +4,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map.Entry;
 import java.util.Stack;
 
 import org.eclipse.jface.resource.ImageDescriptor;
@@ -48,16 +47,16 @@ import org.simantics.db.Statement;
 import org.simantics.db.WriteGraph;
 import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.exception.DatabaseException;
-import org.simantics.db.layer0.util.Layer0Utils;
 import org.simantics.db.request.Read;
 import org.simantics.interop.test.GraphChanges;
-import org.simantics.interop.test.GraphComparator;
 import org.simantics.interop.update.Activator;
+import org.simantics.interop.update.model.ModelUpdate;
 import org.simantics.interop.update.model.UpdateList;
 import org.simantics.interop.update.model.UpdateNode;
 import org.simantics.interop.update.model.UpdateNode.Status;
 import org.simantics.interop.update.model.UpdateOp;
 import org.simantics.interop.update.model.UpdateTree;
+import org.simantics.interop.update.model.ModelUpdate.WarningListener;
 import org.simantics.interop.utils.TableUtils;
 import org.simantics.ui.SimanticsUI;
 import org.simantics.utils.datastructures.Callback;
@@ -71,22 +70,13 @@ import org.simantics.utils.ui.ExceptionUtils;
  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
  *
  */
-public abstract class ModelUpdateEditor extends Composite{
+public abstract class ModelUpdateEditor extends Composite implements WarningListener{
 
        private Composite errorComposite;
        
        private CheckboxTreeViewer changeBrowser;
        private TableViewer changeViewer;
        
-       private GraphChanges changes;
-       private UpdateTree updateTree;
-       private UpdateList updateList;
-       
-       private GraphChanges changes2;
-       private GraphChanges changes3;
-       private UpdateTree updateTree2;
-       private UpdateList updateList2;
-       
        private Button updateAllButton;
        private Button updateSelectedButton;
        
@@ -100,10 +90,10 @@ public abstract class ModelUpdateEditor extends Composite{
        private Color deletedColor;
        private Color addedColor;
 
+       private ModelUpdate update;
        
-       private HashSet<UpdateNode> selectedStructure = new HashSet<UpdateNode>();
        
-       private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
+       private HashSet<UpdateNode> selectedStructure = new HashSet<UpdateNode>();
        
        public ModelUpdateEditor(Composite parent) {
                super(parent,SWT.NONE);
@@ -222,12 +212,12 @@ public abstract class ModelUpdateEditor extends Composite{
                        selection.getColumn().addSelectionListener(new SelectionAdapter() {
                                @Override
                                public void widgetSelected(SelectionEvent e) {
-                                       if (updateList.getChanges().size() > 0) {
-                                               if (updateList.getSelected().size() > 0) {
-                                                       updateList.clearSelected();
+                                       if (update.getUpdateList().getChanges().size() > 0) {
+                                               if (update.getUpdateList().getSelected().size() > 0) {
+                                                       update.getUpdateList().clearSelected();
                                                } else {
-                                                       for (Pair<Statement, Statement> nr : updateList.getChanges())
-                                                               updateList.addSelected(nr);
+                                                       for (Pair<Statement, Statement> nr : update.getUpdateList().getChanges())
+                                                               update.getUpdateList().addSelected(nr);
                                                }
                                                changeViewer.refresh();
                                        }
@@ -299,26 +289,17 @@ public abstract class ModelUpdateEditor extends Composite{
        }
        
        protected abstract ColumnLabelProvider getLabelProvider(int i);
-       protected abstract Pair<GraphComparator,String> getChanges(Resource r1, Resource r2)  throws DatabaseException;
-       protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
-       protected UpdateList getUpdateList(GraphChanges changes) throws DatabaseException {
-               return new UpdateList(changes.getModifications());
-       }
-       
-       protected void addFilters(List<ChangeFilter> filters) {
-               
-       }
        
        public GraphChanges getChanges() {
-               return changes;
+               return update.getChanges();
        }
        
        public UpdateTree getUpdateTree() {
-               return updateTree;
+               return update.getUpdateTree();
        }
        
        public UpdateList getUpdateList() {
-               return updateList;
+               return update.getUpdateList();
        }
        
        public CheckboxTreeViewer getChangeBrowser() {
@@ -329,7 +310,7 @@ public abstract class ModelUpdateEditor extends Composite{
                return changeViewer;
        }
        
-       private void showWarning(String text) {
+       public void showWarning(ModelUpdate update, String text) {
                errorComposite.setVisible(true);
                
                Label label = new Label(errorComposite, SWT.NONE);
@@ -354,7 +335,7 @@ public abstract class ModelUpdateEditor extends Composite{
        
        public void refreshChecked() {
                Stack<UpdateNode> nodeStack = new Stack<UpdateNode>();
-               nodeStack.push((UpdateNode)updateTree.getRootNode());
+               nodeStack.push((UpdateNode)update.getUpdateTree().getRootNode());
                while (!nodeStack.isEmpty()) {
                        UpdateNode n = nodeStack.pop();
                        if (n.getOp() != null) {
@@ -396,88 +377,22 @@ public abstract class ModelUpdateEditor extends Composite{
                changeViewer.refresh();
        }
        
+       protected abstract ModelUpdate createUpdate();
+       
        
        
        public void load(UpdateEditorInput uei) {
-               
-               addFilters(filters);
 
                Resource oldModel = uei.getR1(); // old model that is being updated, contains user made changes
                Resource newModel = uei.getR2(); // new model, 
                Resource originalModel = uei.getR3(); // original old model without user made changes 
-
+               boolean newDistinct = uei.isNewDistinct();
                try {
-                       
-                       if (originalModel != null) {
-                               // tree way comparison
-                               // compare the original and the old model
-                               Pair<GraphComparator,String> result2 = getChanges(originalModel, oldModel);
-                               GraphComparator comparator2  = result2.first;
-                               if (result2.second != null)
-                                       showWarning(result2.second);
-                               comparator2.test(getSession());
-                               changes2 = comparator2.getChanges();
-                               changes2 = getSession().syncRequest(new FilterChangesRead(changes2));
-                               updateTree2 = getUpdateTree(changes2);
-                               updateList2 = getUpdateList(changes2);
-                               
-                               // compare the original and the new model
-                               Pair<GraphComparator,String> result3 = getChanges(originalModel,newModel);
-                               GraphComparator comparator3  = result3.first;
-                               if (result3.second != null)
-                                       showWarning(result3.second);
-                               comparator3.test(getSession());
-                               changes3 = comparator3.getChanges();
-                               changes3 = getSession().syncRequest(new FilterChangesRead(changes3));
-                       }
-                       
-                       Pair<GraphComparator,String> result = getChanges(oldModel,newModel);
-                       GraphComparator comparator  = result.first;
-                       if (result.second != null)
-                               showWarning(result.second);
-                       if (originalModel != null) {
-                               // three-way comparison: use change information to configure
-                               // the comparison between the old and the new model.
-                               
-                               // 1. map comparable resources
-                               for (Entry<Resource, Resource> origToOld : changes2.getComparable().getEntries()) {
-                                       Resource oldR = origToOld.getValue();
-                                       Resource newR = changes3.getComparable().getRight(origToOld.getKey());
-                                       if (newR != null) {
-                                               comparator.addComparableResources(oldR, newR);
-                                       }
-                               }
-                               // 2. mark removed resources as distinct, so that comparison does not pair them
-                               for (Statement s : changes2.getDeletions()) {
-                                       if (changes3.getComparable().containsLeft(s.getObject()))
-                                               comparator.addNonMatchedRight(changes3.getComparable().getRight(s.getObject()));
-                               }
-                               
-                               for (Statement s : changes3.getDeletions()) {
-                                       if (changes2.getComparable().containsLeft(s.getObject()))
-                                               comparator.addNonMatchedLeft(changes2.getComparable().getRight(s.getObject()));
-                               }
-                               if (uei.isNewDistinct()) {
-                                       // 3. mark added resources as distinct, so that comparison does not pair them
-                                       for (Statement s : changes2.getAdditions()) {
-                                               comparator.addNonMatchedLeft(s.getObject());
-                                       }
-                                       
-                                       for (Statement s : changes3.getAdditions()) {
-                                               comparator.addNonMatchedRight(s.getObject());
-                                       }
-                               }
-                       }
-                       comparator.test(getSession());
-                       changes = comparator.getChanges();
-                       changes = getSession().syncRequest(new FilterChangesRead(changes));
-                       updateTree = getUpdateTree(changes);
-                       updateList = getUpdateList(changes);
-                       
-                       if (originalModel != null) {
-                               createDefaultSelections();
-                       }
-                       
+                       if (update != null)
+                               update.removeListener(this);
+                       update = createUpdate();
+                       update.addListener(this);
+                       update.setInput(oldModel, newModel, originalModel, newDistinct);                        
                } catch (DatabaseException e) {
                        Text text = new Text(this, SWT.MULTI);
                        text.setText(e.getMessage());
@@ -491,8 +406,8 @@ public abstract class ModelUpdateEditor extends Composite{
        }
        
        protected void setInputs() {
-               changeViewer.setInput(updateList.getChanges());
-               changeBrowser.setInput(updateTree);
+               changeViewer.setInput(update.getUpdateList().getChanges());
+               changeBrowser.setInput(update.getUpdateTree());
        }
        
        private void applyAll() {
@@ -501,14 +416,7 @@ public abstract class ModelUpdateEditor extends Composite{
                getSession().asyncRequest(new WriteRequest(){
                        @Override
                        public void perform(WriteGraph graph) throws DatabaseException {
-                               Layer0Utils.addCommentMetadata(graph, "Apply all model updates");
-                               graph.markUndoPoint();
-                               for (Pair<Statement, Statement> mod : updateList.getChanges()) {
-                                       applyLiteralChange(graph, mod);
-                               }
-                               updateList.clear();
-                               
-                               updateTree.getUpdateOps().applyAll(graph);
+                               update.applyAll(graph);
                                
                                Display.getDefault().asyncExec(new Runnable() {
                                        
@@ -533,38 +441,13 @@ public abstract class ModelUpdateEditor extends Composite{
                });
        }
        
-       protected void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DatabaseException {
-               if (mod.second == null) {
-                       graph.deny(mod.first);
-                       return;
-               } 
-               Resource s = changes.getComparable().getLeft(mod.second.getSubject());
-               Resource pred = mod.second.getPredicate();
-               if (graph.hasValue(mod.second.getObject())) {
-                       Object value = graph.getValue(mod.second.getObject());
-                       graph.claimLiteral(s, pred, value);
-               } else {
-                       graph.deny(s,pred);
-               }
-               
-               
-       }
-       
        private void applySelected() {
                updateAllButton.setEnabled(false);
                updateSelectedButton.setEnabled(false);
                getSession().asyncRequest(new WriteRequest(){
                        @Override
                        public void perform(WriteGraph graph) throws DatabaseException {
-                               Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
-                               graph.markUndoPoint();
-                               HashSet<Pair<Statement, Statement>> changes = new HashSet<>(updateList.getSelected());
-                               for (Pair<Statement, Statement> mod : changes) {
-                                       updateList.removeChange(mod);
-                                       applyLiteralChange(graph, mod);
-                               }
-                               
-                               updateTree.getUpdateOps().applySelected(graph);
+                               update.applySelected(graph);
                                
                                Display.getDefault().asyncExec(new Runnable() {
                                        
@@ -578,142 +461,7 @@ public abstract class ModelUpdateEditor extends Composite{
                                });
                        }
                });
-       }
-       
-       protected void createDefaultSelections() {
-               // select all changes
-               for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
-                       op.getValue().select(true);
-               }
-               
-               
-               for (Pair<Statement, Statement> pair : updateList.getChanges()) {
-                       updateList.addSelected(pair);
-               }
-               
-               // preserve user-made changes (by removing selections)
-               for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
-                       UpdateOp op2 = updateTree2.getUpdateOps().getUpdateOp(op.getKey());
-                       if (op2 == null) {
-                               if (changes3.getComparable().containsRight(op.getKey())){
-                                       op2 = updateTree2.getUpdateOps().getUpdateOp(changes3.getComparable().getLeft(op.getKey()));
-                               }
-                       }
-                       if (op2 != null && op.getValue().getClass() == op2.getClass()) {
-                               op.getValue().select(false);
-                       }
-               }
-               
-               for (Pair<Statement, Statement> pair : updateList.getChanges()) {
-                       if (pair.first != null) {
-                               boolean found = false;
-                               for (Pair<Statement, Statement> pair2 : updateList2.getChanges()) {
-                                       if (pair.first.equals(pair2.first)) {
-                                               found = true;
-                                               break;
-                                       }
-                               }
-                               if (found) {
-                                       updateList.removeSelected(pair);
-                               }
-                       }
-               }
-               
-       }
-
-       
-       /**
-        * Filters changes:
-        * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
-        * 2. Runs custom filters for value changes. 
-        * 
-        * @param g
-        * @param changes
-        * @return
-        * @throws DatabaseException
-        */
-       protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException 
-    {
-               
-               List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
-               
-       for (Pair<Statement, Statement> mod : changes.getModifications()) {
-               
-               boolean accept = true;
-               for (ChangeFilter filter : filters) {
-                       if (!filter.accept(g, mod)) {
-                               accept = false;
-                               break;
-                       }       
-               }
-               if (accept)
-                       modifications.add(mod);
-       }
-       GraphChanges newChanges =  new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
-       return newChanges;
-    }
-       
-       public interface ChangeFilter {
-               public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
-       }
-
-       
-       /**
-        * 
-        * Filters floating point value changes (default filter is set filter when the change is less than 1%)  
-        *
-        */
-       protected class FPValueFilter implements ChangeFilter  {
-               
-               private double percentage = 0.01;
-               
-               public FPValueFilter() {
-                       
-               }
-               
-               public FPValueFilter(double percentage) {
-                       if (percentage < 0.0 || percentage > 1.0)
-                               throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
-                       this.percentage = percentage;
-               }
-               
-               @Override
-               public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
-                       //filter floating point values that have less than 1% difference.
-                       if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
-                               return true;
-               Object v1 = g.getValue(change.first.getObject());
-               Object v2 = g.getValue(change.second.getObject());
-               
-               if (v1 instanceof Double && v2 instanceof Double) {
-                       double d1 = (Double)v1;
-                       double d2 = (Double)v2;
-                       if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
-                               return false;
-               } else if (v1 instanceof Float && v2 instanceof Float) {
-                       float d1 = (Float)v1;
-                       float d2 = (Float)v2;
-                       if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
-                               return false;
-               }
-
-               return true;
-               }
-       }
-
-       
-       private class FilterChangesRead implements Read<GraphChanges> {
-               private GraphChanges changes;
-               public FilterChangesRead(GraphChanges changes) {
-                       this.changes = changes;
-               }
-               
-               @Override
-               public GraphChanges perform(ReadGraph graph) throws DatabaseException {
-                       return filterChanges(graph, changes);
-               }
-       }
-       
+       }       
        
        private class ModificationListContentProvider implements IStructuredContentProvider {
                
@@ -789,9 +537,11 @@ public abstract class ModelUpdateEditor extends Composite{
                
                @Override
                public Image getImage(Object element) {
-                       if (updateList == null)
+                       if (update == null || !update.isInit())
                                return null;
-                       if (updateList.isSelected((Pair<Statement, Statement>) element))
+//                     if (update.getUpdateList() == null)
+//                             return null;
+                       if (update.getUpdateList().isSelected((Pair<Statement, Statement>) element))
                                return checked;
                        else
                                return unchecked;
@@ -877,20 +627,20 @@ public abstract class ModelUpdateEditor extends Composite{
                
                @Override
                protected Object getValue(Object element) {
-                       if (updateList == null)
+                       if (update == null || !update.isInit())
                                return false;
-                       return updateList.isSelected((Pair<Statement, Statement>) element);
+                       return update.getUpdateList().isSelected((Pair<Statement, Statement>) element);
                }
                
                @SuppressWarnings("unchecked")
                @Override
                protected void setValue(Object element, Object value) {
-                       if (updateList == null)
+                       if (update == null || !update.isInit())
                                return;
                        if (Boolean.TRUE.equals(value))
-                               updateList.addSelected((Pair<Statement, Statement>) element);
+                               update.getUpdateList().addSelected((Pair<Statement, Statement>) element);
                        else
-                               updateList.removeSelected((Pair<Statement, Statement>) element);
+                               update.getUpdateList().removeSelected((Pair<Statement, Statement>) element);
                        
                        getViewer().refresh(element);
                }
diff --git a/org.simantics.interop.update/src/org/simantics/interop/update/model/ModelUpdate.java b/org.simantics.interop.update/src/org/simantics/interop/update/model/ModelUpdate.java
new file mode 100644 (file)
index 0000000..0ddd4e4
--- /dev/null
@@ -0,0 +1,350 @@
+package org.simantics.interop.update.model;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.Statement;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.util.Layer0Utils;
+import org.simantics.db.request.Read;
+import org.simantics.interop.test.GraphChanges;
+import org.simantics.interop.test.GraphComparator;
+import org.simantics.utils.datastructures.Pair;
+
+public abstract class ModelUpdate {
+       
+       private GraphChanges changes;
+       private UpdateTree updateTree;
+       private UpdateList updateList;
+       
+       private GraphChanges changes2;
+       private GraphChanges changes3;
+       private UpdateTree updateTree2;
+       private UpdateList updateList2;
+       
+       private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
+       
+       boolean init = false;
+       
+       public void setInput(Resource oldModel, Resource newModel) throws DatabaseException {
+               setInput(oldModel, newModel, null, false);
+       }
+       
+       public void setInput(Resource oldModel, Resource newModel, Resource originalModel, boolean newDistinct) throws DatabaseException{
+               addFilters(filters);
+               if (originalModel != null) {
+                       // tree way comparison
+                       // compare the original and the old model
+                       Pair<GraphComparator,String> result2 = getChanges(originalModel, oldModel);
+                       GraphComparator comparator2  = result2.first;
+                       if (result2.second != null)
+                               showWarning(result2.second);
+                       comparator2.test(getSession());
+                       changes2 = comparator2.getChanges();
+                       changes2 = getSession().syncRequest(new FilterChangesRead(changes2));
+                       updateTree2 = getUpdateTree(changes2);
+                       updateList2 = getUpdateList(changes2);
+                       
+                       // compare the original and the new model
+                       Pair<GraphComparator,String> result3 = getChanges(originalModel,newModel);
+                       GraphComparator comparator3  = result3.first;
+                       if (result3.second != null)
+                               showWarning(result3.second);
+                       comparator3.test(getSession());
+                       changes3 = comparator3.getChanges();
+                       changes3 = getSession().syncRequest(new FilterChangesRead(changes3));
+               }
+               
+               Pair<GraphComparator,String> result = getChanges(oldModel,newModel);
+               GraphComparator comparator  = result.first;
+               if (result.second != null)
+                       showWarning(result.second);
+               if (originalModel != null) {
+                       // three-way comparison: use change information to configure
+                       // the comparison between the old and the new model.
+                       
+                       // 1. map comparable resources
+                       for (Entry<Resource, Resource> origToOld : changes2.getComparable().getEntries()) {
+                               Resource oldR = origToOld.getValue();
+                               Resource newR = changes3.getComparable().getRight(origToOld.getKey());
+                               if (newR != null) {
+                                       comparator.addComparableResources(oldR, newR);
+                               }
+                       }
+                       // 2. mark removed resources as distinct, so that comparison does not pair them
+                       for (Statement s : changes2.getDeletions()) {
+                               if (changes3.getComparable().containsLeft(s.getObject()))
+                                       comparator.addNonMatchedRight(changes3.getComparable().getRight(s.getObject()));
+                       }
+                       
+                       for (Statement s : changes3.getDeletions()) {
+                               if (changes2.getComparable().containsLeft(s.getObject()))
+                                       comparator.addNonMatchedLeft(changes2.getComparable().getRight(s.getObject()));
+                       }
+                       if (newDistinct) {
+                               // 3. mark added resources as distinct, so that comparison does not pair them
+                               for (Statement s : changes2.getAdditions()) {
+                                       comparator.addNonMatchedLeft(s.getObject());
+                               }
+                               
+                               for (Statement s : changes3.getAdditions()) {
+                                       comparator.addNonMatchedRight(s.getObject());
+                               }
+                       }
+               }
+               comparator.test(getSession());
+               changes = comparator.getChanges();
+               changes = getSession().syncRequest(new FilterChangesRead(changes));
+               updateTree = getUpdateTree(changes);
+               updateList = getUpdateList(changes);
+               
+               
+               if (originalModel != null) {
+                       createDefaultSelections();
+               }
+               
+               init = true;
+       }
+       
+       
+       
+       protected abstract Pair<GraphComparator,String> getChanges(Resource r1, Resource r2)  throws DatabaseException;
+       protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
+       protected UpdateList getUpdateList(GraphChanges changes) throws DatabaseException {
+               return new UpdateList(changes.getModifications());
+       }
+       
+       protected void addFilters(List<ChangeFilter> filters) {
+               
+       }
+       
+       public boolean isInit() {
+               return init;
+       }
+       
+       public GraphChanges getChanges() {
+               return changes;
+       }
+       public UpdateTree getUpdateTree() {
+               return updateTree;
+       }
+       public UpdateList getUpdateList() {
+               return updateList;
+       }
+       public GraphChanges getChanges2() {
+               return changes2;
+       }
+       public GraphChanges getChanges3() {
+               return changes3;
+       }
+       public UpdateTree getUpdateTree2() {
+               return updateTree2;
+       }
+       public UpdateList getUpdateList2() {
+               return updateList2;
+       }
+
+       
+       public void applyAll(WriteGraph graph) throws DatabaseException {
+               Layer0Utils.addCommentMetadata(graph, "Apply all model updates");
+               graph.markUndoPoint();
+               for (Pair<Statement, Statement> mod : updateList.getChanges()) {
+                       applyLiteralChange(graph, mod);
+               }
+               updateList.clear();
+               
+               updateTree.getUpdateOps().applyAll(graph);
+       }
+       
+       public void applySelected(WriteGraph graph) throws DatabaseException {
+               Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
+               graph.markUndoPoint();
+               HashSet<Pair<Statement, Statement>> changes = new HashSet<>(updateList.getSelected());
+               for (Pair<Statement, Statement> mod : changes) {
+                       updateList.removeChange(mod);
+                       applyLiteralChange(graph, mod);
+               }
+               
+               updateTree.getUpdateOps().applySelected(graph);
+       }
+       
+       protected void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DatabaseException {
+               if (mod.second == null) {
+                       graph.deny(mod.first);
+                       return;
+               } 
+               Resource s = changes.getComparable().getLeft(mod.second.getSubject());
+               Resource pred = mod.second.getPredicate();
+               if (graph.hasValue(mod.second.getObject())) {
+                       Object value = graph.getValue(mod.second.getObject());
+                       graph.claimLiteral(s, pred, value);
+               } else {
+                       graph.deny(s,pred);
+               }
+       }
+       
+       
+       protected Session getSession() {
+               return Simantics.getSession();
+       }
+       
+       
+       
+       private class FilterChangesRead implements Read<GraphChanges> {
+               private GraphChanges changes;
+               public FilterChangesRead(GraphChanges changes) {
+                       this.changes = changes;
+               }
+               
+               @Override
+               public GraphChanges perform(ReadGraph graph) throws DatabaseException {
+                       return filterChanges(graph, changes);
+               }
+       }
+       
+       /**
+        * Filters changes:
+        * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
+        * 2. Runs custom filters for value changes. 
+        * 
+        * @param g
+        * @param changes
+        * @return
+        * @throws DatabaseException
+        */
+       protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException 
+    {
+               
+               List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
+               
+       for (Pair<Statement, Statement> mod : changes.getModifications()) {
+               
+               boolean accept = true;
+               for (ChangeFilter filter : filters) {
+                       if (!filter.accept(g, mod)) {
+                               accept = false;
+                               break;
+                       }       
+               }
+               if (accept)
+                       modifications.add(mod);
+       }
+       GraphChanges newChanges =  new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
+       return newChanges;
+    }
+       
+       public interface ChangeFilter {
+               public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
+       }
+
+       
+       /**
+        * 
+        * Filters floating point value changes (default filter is set filter when the change is less than 1%)  
+        *
+        */
+       protected class FPValueFilter implements ChangeFilter  {
+               
+               private double percentage = 0.01;
+               
+               public FPValueFilter() {
+                       
+               }
+               
+               public FPValueFilter(double percentage) {
+                       if (percentage < 0.0 || percentage > 1.0)
+                               throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
+                       this.percentage = percentage;
+               }
+               
+               @Override
+               public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
+                       //filter floating point values that have less than 1% difference.
+                       if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
+                               return true;
+               Object v1 = g.getValue(change.first.getObject());
+               Object v2 = g.getValue(change.second.getObject());
+               
+               if (v1 instanceof Double && v2 instanceof Double) {
+                       double d1 = (Double)v1;
+                       double d2 = (Double)v2;
+                       if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
+                               return false;
+               } else if (v1 instanceof Float && v2 instanceof Float) {
+                       float d1 = (Float)v1;
+                       float d2 = (Float)v2;
+                       if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
+                               return false;
+               }
+
+               return true;
+               }
+       }
+       
+       protected void createDefaultSelections() {
+               // select all changes
+               for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
+                       op.getValue().select(true);
+               }
+               
+               
+               for (Pair<Statement, Statement> pair : updateList.getChanges()) {
+                       updateList.addSelected(pair);
+               }
+               
+               // preserve user-made changes (by removing selections)
+               for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
+                       UpdateOp op2 = updateTree2.getUpdateOps().getUpdateOp(op.getKey());
+                       if (op2 == null) {
+                               if (changes3.getComparable().containsRight(op.getKey())){
+                                       op2 = updateTree2.getUpdateOps().getUpdateOp(changes3.getComparable().getLeft(op.getKey()));
+                               }
+                       }
+                       if (op2 != null && op.getValue().getClass() == op2.getClass()) {
+                               op.getValue().select(false);
+                       }
+               }
+               
+               for (Pair<Statement, Statement> pair : updateList.getChanges()) {
+                       if (pair.first != null) {
+                               boolean found = false;
+                               for (Pair<Statement, Statement> pair2 : updateList2.getChanges()) {
+                                       if (pair.first.equals(pair2.first)) {
+                                               found = true;
+                                               break;
+                                       }
+                               }
+                               if (found) {
+                                       updateList.removeSelected(pair);
+                               }
+                       }
+               }
+               
+       }
+       
+       private void showWarning(String string) {
+               for (WarningListener l : warningListeners)
+                       l.showWarning(this, string);
+       }
+       
+       private List<WarningListener> warningListeners = new ArrayList<>();
+       
+       public static interface WarningListener {
+               void showWarning(ModelUpdate update, String warning);
+       }
+       
+       public void addListener(WarningListener listener) {
+               warningListeners.add(listener);
+       }
+       
+       public void removeListener(WarningListener listener) {
+               warningListeners.remove(listener);
+       }
+}