package org.simantics.interop.update.model;
+import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.Collections;
+import java.util.Deque;
import java.util.List;
import java.util.Map.Entry;
import org.simantics.db.Session;
import org.simantics.db.Statement;
import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.ReadRequest;
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.GraphChanges.Modification;
import org.simantics.interop.test.GraphComparator;
import org.simantics.utils.datastructures.Pair;
public abstract class ModelUpdate {
- private GraphChanges changes;
+ private Resource oldModel; // old model that is going to be updated (User modified model)
+ private Resource newModel; // new model that contains the updates (New design model)
+ private Resource originalModel; // original model (optional) that is used for detecting and retaining user made changes (Old design model)
+
+ private GraphChanges changes; // changes between old /new
private UpdateTree updateTree;
private UpdateList updateList;
- private GraphChanges changes2;
+ private GraphChanges changes2; // changes between original / old
private UpdateTree updateTree2;
private UpdateList updateList2;
- private GraphChanges changes3;
+ private GraphChanges changes3; // changes between original / new
private UpdateTree updateTree3;
private UpdateList updateList3;
+ private UpdateNode3 updateNode3;
+
private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
+ private List<ChangeFilter2> userFilters = new ArrayList<ChangeFilter2>();
boolean init = false;
setInput(oldModel, newModel, null, false);
}
+ /**
+ * Initialises the ModelUpdate with given input
+ * @param oldModel the model that is going to be updated (User modified model)
+ * @param newModel the model containing updates (New design model)
+ * @param originalModel the model that is used for detecting and retaining user made changes (Old design model). Parameter can be null.
+ * @param newDistinct when originalModel is given, additions to the old and the new model (when compared to the original model) are forced to be distinct.
+ * @throws DatabaseException
+ */
public void setInput(Resource oldModel, Resource newModel, Resource originalModel, boolean newDistinct) throws DatabaseException{
- addFilters(filters);
+ this.oldModel = oldModel;
+ this.newModel = newModel;
+ this.originalModel = originalModel;
+// addFilters(filters);
if (originalModel != null) {
// tree way comparison
// compare the original and the old model
showWarning(result2.second);
comparator2.test(getSession());
changes2 = comparator2.getChanges();
- changes2 = getSession().syncRequest(new FilterChangesRead(changes2));
- updateTree2 = getUpdateTree(changes2);
- updateList2 = getUpdateList(changes2);
+ changes2 = getSession().syncRequest(createFilterRead(changes2, filters));
+ Pair<UpdateTree, UpdateList> chg2 = createChangeObjects(changes2);
+ updateTree2 = chg2.first;
+ updateList2 = chg2.second;
// compare the original and the new model
Pair<GraphComparator,String> result3 = getChanges(originalModel,newModel);
showWarning(result3.second);
comparator3.test(getSession());
changes3 = comparator3.getChanges();
- changes3 = getSession().syncRequest(new FilterChangesRead(changes3));
+ changes3 = getSession().syncRequest(createFilterRead(changes3, filters));
}
Pair<GraphComparator,String> result = getChanges(oldModel,newModel);
}
comparator.test(getSession());
changes = comparator.getChanges();
- changes = getSession().syncRequest(new FilterChangesRead(changes));
- updateTree = getUpdateTree(changes);
- updateList = getUpdateList(changes);
+ changes = getSession().syncRequest(createFilterRead(changes, filters));
+ Pair<UpdateTree, UpdateList> chg = createChangeObjects(changes);
+ updateTree = chg.first;
+ updateList = chg.second;
+ if (userFilters.size() != 0) {
+ refreshUserFilters();
+ }
if (originalModel != null) {
- createDefaultSelections();
+ defaultSelections();
}
init = true;
}
+ public void addFilter(ChangeFilter filter) {
+ if (init)
+ throw new IllegalStateException("ModelUpdate has been initialized, adjusting filters is no longer possible.");
+ filters.add(filter);
+
+ }
+
+ public List<ChangeFilter> getFilters() {
+ return Collections.unmodifiableList(filters);
+ }
+
+ /**
+ * Adds an user filter. Use refreshUserFilters() to apply the changes.
+ * @param filter
+ */
+ public void addUserFilter(ChangeFilter2 filter) {
+ userFilters.add(filter);
+ }
+
+ /**
+ * Removes an user filter. Use refreshUserFilters() to apply the changes.
+ * @param filter
+ */
+ public void removeUserFilter(ChangeFilter2 filter) {
+ userFilters.remove(filter);
+ }
+
+ /**
+ * Clears user filters. Use refreshUserFilters() to apply the changes.
+ */
+ public void clearUserFilters() {
+ userFilters.clear();
+ }
+
+ public List<ChangeFilter2> getUserFilters() {
+ return userFilters;
+ }
+ public void refreshUserFilters() throws DatabaseException{
+ // use user filters to set visible flags of changes.
+ // First, set all changes visible.
+ Deque<UpdateNode> stack = new ArrayDeque<>();
+ stack.push(updateTree.getRootNode());
+ while (!stack.isEmpty()) {
+ UpdateNode n = stack.pop();
+ n.setVisible(true);
+ stack.addAll(n.getChildren());
+ }
+ for (PropertyChange pc : updateList.getChanges()) {
+ pc.setVisible(true);
+ }
+ if (userFilters.size() > 0) {
+ if (changes2 != null && changes3 != null) {
+ getUpdateTree3();
+ }
+ getSession().syncRequest(new ReadRequest() {
+
+ @Override
+ public void run(ReadGraph graph) throws DatabaseException {
+ for (PropertyChange change : updateList.getChanges()) {
+ boolean visible = true;
+ for (ChangeFilter2 filter : userFilters) {
+ if (!filter.accept(graph, change)) {
+ visible = false;
+ break;
+ }
+ }
+ change.setVisible(visible);
+ }
+ if (updateTree3 != null) {
+ Deque<UpdateNode3> stack = new ArrayDeque<>();
+ stack.add(getUpdateNode3());
+ while (!stack.isEmpty()) {
+ UpdateNode3 n = stack.pop();
+ boolean visible = true;
+ for (ChangeFilter2 filter : userFilters) {
+ if (!filter.accept(graph, n)) {
+ visible = false;
+ break;
+ }
+ }
+ n.setVisible(visible);
+ for (UpdateNode3 c : n.getChildren())
+ stack.push(c);
+ }
+ } else {
+
+ Deque<UpdateNode> stack = new ArrayDeque<>();
+ stack.add(updateTree.getRootNode());
+ while (!stack.isEmpty()) {
+ UpdateNode n = stack.pop();
+ boolean visible = true;
+ for (ChangeFilter2 filter : userFilters) {
+ if (!filter.accept(graph, n)) {
+ visible = false;
+ break;
+ }
+ }
+ n.setVisible(visible);
+ for (UpdateNode c : n.getChildren())
+ stack.push(c);
+ }
+ }
+ }
+ });
+ }
+ }
protected abstract Pair<GraphComparator,String> getChanges(Resource r1, Resource r2) throws DatabaseException;
+
+ protected Pair<UpdateTree, UpdateList> createChangeObjects(GraphChanges changes) throws DatabaseException{
+ UpdateTree updateTree = getUpdateTree(changes);
+ UpdateList updateList = getUpdateList(changes);
+ postProcess(updateTree, updateList);
+ return new Pair<UpdateTree, UpdateList>(updateTree, updateList);
+ }
+
protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
protected UpdateList getUpdateList(GraphChanges changes) throws DatabaseException {
- return new UpdateList(changes.getModifications());
+ return new UpdateList(changes, changes.getModifications());
}
- protected void addFilters(List<ChangeFilter> filters) {
-
+
+ protected void postProcess(UpdateTree updateTree, UpdateList updateList) throws DatabaseException{
+
+ }
+
+ public Resource getOldModel() {
+ return oldModel;
+ }
+
+ public Resource getNewModel() {
+ return newModel;
+ }
+
+ public Resource getOriginalModel() {
+ return originalModel;
}
public boolean isInit() {
}
public UpdateTree getUpdateTree3() throws DatabaseException{
- if (updateTree3 == null && changes3 != null)
- updateTree3 = getUpdateTree(changes3);
+ if (updateTree3 == null && changes3 != null) {
+ Pair<UpdateTree, UpdateList> chg3 = createChangeObjects(changes3);
+ updateTree3 = chg3.first;
+ updateList3 = chg3.second;
+ }
return updateTree3;
}
public UpdateList getUpdateList3() throws DatabaseException {
- if (updateList3 == null && changes3 != null)
- updateList3 = getUpdateList(changes3);
+ if (updateList3 == null && changes3 != null) {
+ Pair<UpdateTree, UpdateList> chg3 = createChangeObjects(changes3);
+ updateTree3 = chg3.first;
+ updateList3 = chg3.second;
+ }
return updateList3;
}
+
+ public UpdateNode3 getUpdateNode3() throws DatabaseException {
+ if (updateNode3 == null && changes2 != null && changes3 != null) {
+ updateNode3 = UpdateNode3.getCombinedTree(this);
+ }
+ return updateNode3;
+ }
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);
+ for (PropertyChange mod : updateList.getChanges()) {
+ mod.apply(graph);
}
- 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);
+ for (PropertyChange mod : updateList.getChanges()) {
+ if (mod.selected())
+ mod.apply(graph);
}
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();
}
+ public Read<GraphChanges> createFilterRead(GraphChanges changes, List<ChangeFilter> filters) {
+ return new FilterChangesRead(changes, filters);
+ }
- private class FilterChangesRead implements Read<GraphChanges> {
+
+ public static class FilterChangesRead implements Read<GraphChanges> {
private GraphChanges changes;
- public FilterChangesRead(GraphChanges changes) {
+ private List<ChangeFilter> filters;
+
+ public FilterChangesRead(GraphChanges changes, List<ChangeFilter> filters) {
this.changes = changes;
+ this.filters = filters;
}
@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 exactly 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<Modification> modifications = new ArrayList<Modification>();
+
+ for (Modification mod : changes.getModifications()) {
+
+ boolean accept = true;
+ for (ChangeFilter filter : filters) {
+ if (!filter.accept(g, mod)) {
+ accept = false;
+ break;
+ }
+ }
+ if (accept)
+ modifications.add(mod);
+ }
+ List<Statement> deletions = new ArrayList<Statement>();
+ for (Statement del : changes.getDeletions()) {
+
+ boolean accept = true;
+ for (ChangeFilter filter : filters) {
+ if (!filter.acceptDeletion(g, del)) {
+ accept = false;
+ break;
+ }
+ }
+ if (accept)
+ deletions.add(del);
+ }
+ List<Statement> additions = new ArrayList<Statement>();
+ for (Statement del : changes.getAdditions()) {
+
+ boolean accept = true;
+ for (ChangeFilter filter : filters) {
+ if (!filter.acceptAddition(g, del)) {
+ accept = false;
+ break;
+ }
+ }
+ if (accept)
+ additions.add(del);
+ }
+
+ GraphChanges newChanges = new GraphChanges(changes.getResource1(),changes.getResource2(),deletions, additions, modifications, changes.getComparable());
+ return newChanges;
+ }
+ }
+
+ /**
+ * Interface for built-in filters that are used for processing raw change data before forming UpdateTree + UpdateList
+ * @author luukkainen
+ *
+ */
+ public interface ChangeFilter {
+ public boolean accept(ReadGraph g, Modification change) throws DatabaseException;
+ public boolean acceptAddition(ReadGraph g, Statement addition) throws DatabaseException;
+ public boolean acceptDeletion(ReadGraph g, Statement deletion) throws DatabaseException;
}
/**
- * 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.
+ * Interface for user defined filters.
*
- * @param g
- * @param changes
- * @return
- * @throws DatabaseException
+ * This filter only affects visible flags.
+ *
+ * @author luukkainen
+ *
*/
- 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;
+ public interface ChangeFilter2 {
+ public boolean accept(ReadGraph g, PropertyChange change) throws DatabaseException;
+ public boolean accept(ReadGraph g, UpdateNode change) throws DatabaseException;
+ public boolean accept(ReadGraph g, UpdateNode3 change) throws DatabaseException;
}
}
@Override
- public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
+ public boolean accept(ReadGraph g, Modification change) throws DatabaseException {
//filter floating point values that have less than 1% difference.
- if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
+ if (!g.hasValue(change.getLeftStm().getObject()) || !g.hasValue(change.getRightStm().getObject()))
return true;
- Object v1 = g.getValue(change.first.getObject());
- Object v2 = g.getValue(change.second.getObject());
+ Object v1 = g.getValue(change.getLeftStm().getObject());
+ Object v2 = g.getValue(change.getRightStm().getObject());
if (v1 instanceof Double && v2 instanceof Double) {
double d1 = (Double)v1;
return true;
}
+
+ @Override
+ public boolean acceptAddition(ReadGraph g, Statement addition) throws DatabaseException {
+ return true;
+ }
+
+ @Override
+ public boolean acceptDeletion(ReadGraph g, Statement deletion) throws DatabaseException {
+ return true;
+ }
}
- protected void createDefaultSelections() {
+ public void defaultSelections() {
+ if (changes3 == null) {
+ return;
+ }
// 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);
+ for (PropertyChange pair : updateList.getChanges()) {
+ pair.select(true);
}
// preserve user-made changes (by removing selections)
}
}
- for (Pair<Statement, Statement> pair : updateList.getChanges()) {
- if (pair.first != null) {
+ for (PropertyChange pair : updateList.getChanges()) {
+ if (pair.getFirst() != null) {
boolean found = false;
- for (Pair<Statement, Statement> pair2 : updateList2.getChanges()) {
- if (pair.first.equals(pair2.first)) {
+ for (PropertyChange pair2 : updateList2.getChanges()) {
+ if (pair.getFirst() != null && pair.getFirst().equals(pair2.getSecond())) {
found = true;
break;
}
}
if (found) {
- updateList.removeSelected(pair);
+ pair.select(false);
}
}
}