1 package org.simantics.interop.update.model;
3 import java.util.ArrayList;
4 import java.util.HashSet;
6 import java.util.Map.Entry;
8 import org.simantics.Simantics;
9 import org.simantics.db.ReadGraph;
10 import org.simantics.db.Resource;
11 import org.simantics.db.Session;
12 import org.simantics.db.Statement;
13 import org.simantics.db.WriteGraph;
14 import org.simantics.db.exception.DatabaseException;
15 import org.simantics.db.layer0.util.Layer0Utils;
16 import org.simantics.db.request.Read;
17 import org.simantics.interop.test.GraphChanges;
18 import org.simantics.interop.test.GraphComparator;
19 import org.simantics.utils.datastructures.Pair;
21 public abstract class ModelUpdate {
23 private GraphChanges changes;
24 private UpdateTree updateTree;
25 private UpdateList updateList;
27 private GraphChanges changes2;
28 private UpdateTree updateTree2;
29 private UpdateList updateList2;
31 private GraphChanges changes3;
32 private UpdateTree updateTree3;
33 private UpdateList updateList3;
35 private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
39 public void setInput(Resource oldModel, Resource newModel) throws DatabaseException {
40 setInput(oldModel, newModel, null, false);
43 public void setInput(Resource oldModel, Resource newModel, Resource originalModel, boolean newDistinct) throws DatabaseException{
45 if (originalModel != null) {
46 // tree way comparison
47 // compare the original and the old model
48 Pair<GraphComparator,String> result2 = getChanges(originalModel, oldModel);
49 GraphComparator comparator2 = result2.first;
50 if (result2.second != null)
51 showWarning(result2.second);
52 comparator2.test(getSession());
53 changes2 = comparator2.getChanges();
54 changes2 = getSession().syncRequest(new FilterChangesRead(changes2));
55 updateTree2 = getUpdateTree(changes2);
56 updateList2 = getUpdateList(changes2);
58 // compare the original and the new model
59 Pair<GraphComparator,String> result3 = getChanges(originalModel,newModel);
60 GraphComparator comparator3 = result3.first;
61 if (result3.second != null)
62 showWarning(result3.second);
63 comparator3.test(getSession());
64 changes3 = comparator3.getChanges();
65 changes3 = getSession().syncRequest(new FilterChangesRead(changes3));
68 Pair<GraphComparator,String> result = getChanges(oldModel,newModel);
69 GraphComparator comparator = result.first;
70 if (result.second != null)
71 showWarning(result.second);
72 if (originalModel != null) {
73 // three-way comparison: use change information to configure
74 // the comparison between the old and the new model.
76 // 1. map comparable resources
77 for (Entry<Resource, Resource> origToOld : changes2.getComparable().getEntries()) {
78 Resource oldR = origToOld.getValue();
79 Resource newR = changes3.getComparable().getRight(origToOld.getKey());
81 comparator.addComparableResources(oldR, newR);
84 // 2. mark removed resources as distinct, so that comparison does not pair them
85 for (Statement s : changes2.getDeletions()) {
86 if (changes3.getComparable().containsLeft(s.getObject()))
87 comparator.addNonMatchedRight(changes3.getComparable().getRight(s.getObject()));
90 for (Statement s : changes3.getDeletions()) {
91 if (changes2.getComparable().containsLeft(s.getObject()))
92 comparator.addNonMatchedLeft(changes2.getComparable().getRight(s.getObject()));
95 // 3. mark added resources as distinct, so that comparison does not pair them
96 for (Statement s : changes2.getAdditions()) {
97 comparator.addNonMatchedLeft(s.getObject());
100 for (Statement s : changes3.getAdditions()) {
101 comparator.addNonMatchedRight(s.getObject());
105 comparator.test(getSession());
106 changes = comparator.getChanges();
107 changes = getSession().syncRequest(new FilterChangesRead(changes));
108 updateTree = getUpdateTree(changes);
109 updateList = getUpdateList(changes);
112 if (originalModel != null) {
113 createDefaultSelections();
121 protected abstract Pair<GraphComparator,String> getChanges(Resource r1, Resource r2) throws DatabaseException;
122 protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
123 protected UpdateList getUpdateList(GraphChanges changes) throws DatabaseException {
124 return new UpdateList(changes.getModifications());
127 protected void addFilters(List<ChangeFilter> filters) {
131 public boolean isInit() {
135 public GraphChanges getChanges() {
138 public UpdateTree getUpdateTree() {
141 public UpdateList getUpdateList() {
144 public GraphChanges getChanges2() {
148 public UpdateTree getUpdateTree2() {
151 public UpdateList getUpdateList2() {
155 public GraphChanges getChanges3() {
159 public UpdateTree getUpdateTree3() throws DatabaseException{
160 if (updateTree3 == null && changes3 != null)
161 updateTree3 = getUpdateTree(changes3);
164 public UpdateList getUpdateList3() throws DatabaseException {
165 if (updateList3 == null && changes3 != null)
166 updateList3 = getUpdateList(changes3);
171 public void applyAll(WriteGraph graph) throws DatabaseException {
172 Layer0Utils.addCommentMetadata(graph, "Apply all model updates");
173 graph.markUndoPoint();
174 for (Pair<Statement, Statement> mod : updateList.getChanges()) {
175 applyLiteralChange(graph, mod);
179 updateTree.getUpdateOps().applyAll(graph);
182 public void applySelected(WriteGraph graph) throws DatabaseException {
183 Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
184 graph.markUndoPoint();
185 HashSet<Pair<Statement, Statement>> changes = new HashSet<>(updateList.getSelected());
186 for (Pair<Statement, Statement> mod : changes) {
187 updateList.removeChange(mod);
188 applyLiteralChange(graph, mod);
191 updateTree.getUpdateOps().applySelected(graph);
194 protected void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DatabaseException {
195 if (mod.second == null) {
196 graph.deny(mod.first);
199 Resource s = changes.getComparable().getLeft(mod.second.getSubject());
200 Resource pred = mod.second.getPredicate();
201 if (graph.hasValue(mod.second.getObject())) {
202 Object value = graph.getValue(mod.second.getObject());
203 graph.claimLiteral(s, pred, value);
210 protected Session getSession() {
211 return Simantics.getSession();
216 private class FilterChangesRead implements Read<GraphChanges> {
217 private GraphChanges changes;
218 public FilterChangesRead(GraphChanges changes) {
219 this.changes = changes;
223 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
224 return filterChanges(graph, changes);
230 * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
231 * 2. Runs custom filters for value changes.
236 * @throws DatabaseException
238 protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException
241 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
243 for (Pair<Statement, Statement> mod : changes.getModifications()) {
245 boolean accept = true;
246 for (ChangeFilter filter : filters) {
247 if (!filter.accept(g, mod)) {
253 modifications.add(mod);
255 GraphChanges newChanges = new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
259 public interface ChangeFilter {
260 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
266 * Filters floating point value changes (default filter is set filter when the change is less than 1%)
269 protected class FPValueFilter implements ChangeFilter {
271 private double percentage = 0.01;
273 public FPValueFilter() {
277 public FPValueFilter(double percentage) {
278 if (percentage < 0.0 || percentage > 1.0)
279 throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
280 this.percentage = percentage;
284 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
285 //filter floating point values that have less than 1% difference.
286 if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
288 Object v1 = g.getValue(change.first.getObject());
289 Object v2 = g.getValue(change.second.getObject());
291 if (v1 instanceof Double && v2 instanceof Double) {
292 double d1 = (Double)v1;
293 double d2 = (Double)v2;
294 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
296 } else if (v1 instanceof Float && v2 instanceof Float) {
297 float d1 = (Float)v1;
298 float d2 = (Float)v2;
299 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
307 protected void createDefaultSelections() {
308 // select all changes
309 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
310 op.getValue().select(true);
314 for (Pair<Statement, Statement> pair : updateList.getChanges()) {
315 updateList.addSelected(pair);
318 // preserve user-made changes (by removing selections)
319 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
320 UpdateOp op2 = updateTree2.getUpdateOps().getUpdateOp(op.getKey());
322 if (changes3.getComparable().containsRight(op.getKey())){
323 op2 = updateTree2.getUpdateOps().getUpdateOp(changes3.getComparable().getLeft(op.getKey()));
326 if (op2 != null && op.getValue().getClass() == op2.getClass()) {
327 op.getValue().select(false);
331 for (Pair<Statement, Statement> pair : updateList.getChanges()) {
332 if (pair.first != null) {
333 boolean found = false;
334 for (Pair<Statement, Statement> pair2 : updateList2.getChanges()) {
335 if (pair.first.equals(pair2.second)) {
341 updateList.removeSelected(pair);
348 private void showWarning(String string) {
349 for (WarningListener l : warningListeners)
350 l.showWarning(this, string);
353 private List<WarningListener> warningListeners = new ArrayList<>();
355 public static interface WarningListener {
356 void showWarning(ModelUpdate update, String warning);
359 public void addListener(WarningListener listener) {
360 warningListeners.add(listener);
363 public void removeListener(WarningListener listener) {
364 warningListeners.remove(listener);