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 GraphChanges changes3;
29 private UpdateTree updateTree2;
30 private UpdateList updateList2;
32 private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
36 public void setInput(Resource oldModel, Resource newModel) throws DatabaseException {
37 setInput(oldModel, newModel, null, false);
40 public void setInput(Resource oldModel, Resource newModel, Resource originalModel, boolean newDistinct) throws DatabaseException{
42 if (originalModel != null) {
43 // tree way comparison
44 // compare the original and the old model
45 Pair<GraphComparator,String> result2 = getChanges(originalModel, oldModel);
46 GraphComparator comparator2 = result2.first;
47 if (result2.second != null)
48 showWarning(result2.second);
49 comparator2.test(getSession());
50 changes2 = comparator2.getChanges();
51 changes2 = getSession().syncRequest(new FilterChangesRead(changes2));
52 updateTree2 = getUpdateTree(changes2);
53 updateList2 = getUpdateList(changes2);
55 // compare the original and the new model
56 Pair<GraphComparator,String> result3 = getChanges(originalModel,newModel);
57 GraphComparator comparator3 = result3.first;
58 if (result3.second != null)
59 showWarning(result3.second);
60 comparator3.test(getSession());
61 changes3 = comparator3.getChanges();
62 changes3 = getSession().syncRequest(new FilterChangesRead(changes3));
65 Pair<GraphComparator,String> result = getChanges(oldModel,newModel);
66 GraphComparator comparator = result.first;
67 if (result.second != null)
68 showWarning(result.second);
69 if (originalModel != null) {
70 // three-way comparison: use change information to configure
71 // the comparison between the old and the new model.
73 // 1. map comparable resources
74 for (Entry<Resource, Resource> origToOld : changes2.getComparable().getEntries()) {
75 Resource oldR = origToOld.getValue();
76 Resource newR = changes3.getComparable().getRight(origToOld.getKey());
78 comparator.addComparableResources(oldR, newR);
81 // 2. mark removed resources as distinct, so that comparison does not pair them
82 for (Statement s : changes2.getDeletions()) {
83 if (changes3.getComparable().containsLeft(s.getObject()))
84 comparator.addNonMatchedRight(changes3.getComparable().getRight(s.getObject()));
87 for (Statement s : changes3.getDeletions()) {
88 if (changes2.getComparable().containsLeft(s.getObject()))
89 comparator.addNonMatchedLeft(changes2.getComparable().getRight(s.getObject()));
92 // 3. mark added resources as distinct, so that comparison does not pair them
93 for (Statement s : changes2.getAdditions()) {
94 comparator.addNonMatchedLeft(s.getObject());
97 for (Statement s : changes3.getAdditions()) {
98 comparator.addNonMatchedRight(s.getObject());
102 comparator.test(getSession());
103 changes = comparator.getChanges();
104 changes = getSession().syncRequest(new FilterChangesRead(changes));
105 updateTree = getUpdateTree(changes);
106 updateList = getUpdateList(changes);
109 if (originalModel != null) {
110 createDefaultSelections();
118 protected abstract Pair<GraphComparator,String> getChanges(Resource r1, Resource r2) throws DatabaseException;
119 protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
120 protected UpdateList getUpdateList(GraphChanges changes) throws DatabaseException {
121 return new UpdateList(changes.getModifications());
124 protected void addFilters(List<ChangeFilter> filters) {
128 public boolean isInit() {
132 public GraphChanges getChanges() {
135 public UpdateTree getUpdateTree() {
138 public UpdateList getUpdateList() {
141 public GraphChanges getChanges2() {
144 public GraphChanges getChanges3() {
147 public UpdateTree getUpdateTree2() {
150 public UpdateList getUpdateList2() {
155 public void applyAll(WriteGraph graph) throws DatabaseException {
156 Layer0Utils.addCommentMetadata(graph, "Apply all model updates");
157 graph.markUndoPoint();
158 for (Pair<Statement, Statement> mod : updateList.getChanges()) {
159 applyLiteralChange(graph, mod);
163 updateTree.getUpdateOps().applyAll(graph);
166 public void applySelected(WriteGraph graph) throws DatabaseException {
167 Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
168 graph.markUndoPoint();
169 HashSet<Pair<Statement, Statement>> changes = new HashSet<>(updateList.getSelected());
170 for (Pair<Statement, Statement> mod : changes) {
171 updateList.removeChange(mod);
172 applyLiteralChange(graph, mod);
175 updateTree.getUpdateOps().applySelected(graph);
178 protected void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DatabaseException {
179 if (mod.second == null) {
180 graph.deny(mod.first);
183 Resource s = changes.getComparable().getLeft(mod.second.getSubject());
184 Resource pred = mod.second.getPredicate();
185 if (graph.hasValue(mod.second.getObject())) {
186 Object value = graph.getValue(mod.second.getObject());
187 graph.claimLiteral(s, pred, value);
194 protected Session getSession() {
195 return Simantics.getSession();
200 private class FilterChangesRead implements Read<GraphChanges> {
201 private GraphChanges changes;
202 public FilterChangesRead(GraphChanges changes) {
203 this.changes = changes;
207 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
208 return filterChanges(graph, changes);
214 * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
215 * 2. Runs custom filters for value changes.
220 * @throws DatabaseException
222 protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException
225 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
227 for (Pair<Statement, Statement> mod : changes.getModifications()) {
229 boolean accept = true;
230 for (ChangeFilter filter : filters) {
231 if (!filter.accept(g, mod)) {
237 modifications.add(mod);
239 GraphChanges newChanges = new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
243 public interface ChangeFilter {
244 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
250 * Filters floating point value changes (default filter is set filter when the change is less than 1%)
253 protected class FPValueFilter implements ChangeFilter {
255 private double percentage = 0.01;
257 public FPValueFilter() {
261 public FPValueFilter(double percentage) {
262 if (percentage < 0.0 || percentage > 1.0)
263 throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
264 this.percentage = percentage;
268 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
269 //filter floating point values that have less than 1% difference.
270 if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
272 Object v1 = g.getValue(change.first.getObject());
273 Object v2 = g.getValue(change.second.getObject());
275 if (v1 instanceof Double && v2 instanceof Double) {
276 double d1 = (Double)v1;
277 double d2 = (Double)v2;
278 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
280 } else if (v1 instanceof Float && v2 instanceof Float) {
281 float d1 = (Float)v1;
282 float d2 = (Float)v2;
283 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
291 protected void createDefaultSelections() {
292 // select all changes
293 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
294 op.getValue().select(true);
298 for (Pair<Statement, Statement> pair : updateList.getChanges()) {
299 updateList.addSelected(pair);
302 // preserve user-made changes (by removing selections)
303 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
304 UpdateOp op2 = updateTree2.getUpdateOps().getUpdateOp(op.getKey());
306 if (changes3.getComparable().containsRight(op.getKey())){
307 op2 = updateTree2.getUpdateOps().getUpdateOp(changes3.getComparable().getLeft(op.getKey()));
310 if (op2 != null && op.getValue().getClass() == op2.getClass()) {
311 op.getValue().select(false);
315 for (Pair<Statement, Statement> pair : updateList.getChanges()) {
316 if (pair.first != null) {
317 boolean found = false;
318 for (Pair<Statement, Statement> pair2 : updateList2.getChanges()) {
319 if (pair.first.equals(pair2.first)) {
325 updateList.removeSelected(pair);
332 private void showWarning(String string) {
333 for (WarningListener l : warningListeners)
334 l.showWarning(this, string);
337 private List<WarningListener> warningListeners = new ArrayList<>();
339 public static interface WarningListener {
340 void showWarning(ModelUpdate update, String warning);
343 public void addListener(WarningListener listener) {
344 warningListeners.add(listener);
347 public void removeListener(WarningListener listener) {
348 warningListeners.remove(listener);