1 package org.simantics.interop.update.model;
3 import java.util.ArrayList;
5 import java.util.Map.Entry;
7 import org.simantics.Simantics;
8 import org.simantics.db.ReadGraph;
9 import org.simantics.db.Resource;
10 import org.simantics.db.Session;
11 import org.simantics.db.Statement;
12 import org.simantics.db.WriteGraph;
13 import org.simantics.db.exception.DatabaseException;
14 import org.simantics.db.layer0.util.Layer0Utils;
15 import org.simantics.db.request.Read;
16 import org.simantics.interop.test.GraphChanges;
17 import org.simantics.interop.test.GraphComparator;
18 import org.simantics.utils.datastructures.Pair;
20 public abstract class ModelUpdate {
22 private Resource oldModel; // old model that is going to be updated (User modified model)
23 private Resource newModel; // new model that contains the updates (New design model)
24 private Resource originalModel; // original model (optional) that is used for detecting and retaining user made changes (Old design model)
26 private GraphChanges changes; // changes between old /new
27 private UpdateTree updateTree;
28 private UpdateList updateList;
30 private GraphChanges changes2; // changes between original / old
31 private UpdateTree updateTree2;
32 private UpdateList updateList2;
34 private GraphChanges changes3; // changes between original / new
35 private UpdateTree updateTree3;
36 private UpdateList updateList3;
38 private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
42 public void setInput(Resource oldModel, Resource newModel) throws DatabaseException {
43 setInput(oldModel, newModel, null, false);
47 * Initialises the ModelUpdate with given input
48 * @param oldModel the model that is going to be updated (User modified model)
49 * @param newModel the model containing updates (New design model)
50 * @param originalModel the model that is used for detecting and retaining user made changes (Old design model). Parameter can be null.
51 * @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.
52 * @throws DatabaseException
54 public void setInput(Resource oldModel, Resource newModel, Resource originalModel, boolean newDistinct) throws DatabaseException{
55 this.oldModel = oldModel;
56 this.newModel = newModel;
57 this.originalModel = originalModel;
59 if (originalModel != null) {
60 // tree way comparison
61 // compare the original and the old model
62 Pair<GraphComparator,String> result2 = getChanges(originalModel, oldModel);
63 GraphComparator comparator2 = result2.first;
64 if (result2.second != null)
65 showWarning(result2.second);
66 comparator2.test(getSession());
67 changes2 = comparator2.getChanges();
68 changes2 = getSession().syncRequest(new FilterChangesRead(changes2));
69 updateTree2 = getUpdateTree(changes2);
70 updateList2 = getUpdateList(changes2);
72 // compare the original and the new model
73 Pair<GraphComparator,String> result3 = getChanges(originalModel,newModel);
74 GraphComparator comparator3 = result3.first;
75 if (result3.second != null)
76 showWarning(result3.second);
77 comparator3.test(getSession());
78 changes3 = comparator3.getChanges();
79 changes3 = getSession().syncRequest(new FilterChangesRead(changes3));
82 Pair<GraphComparator,String> result = getChanges(oldModel,newModel);
83 GraphComparator comparator = result.first;
84 if (result.second != null)
85 showWarning(result.second);
86 if (originalModel != null) {
87 // three-way comparison: use change information to configure
88 // the comparison between the old and the new model.
90 // 1. map comparable resources
91 for (Entry<Resource, Resource> origToOld : changes2.getComparable().getEntries()) {
92 Resource oldR = origToOld.getValue();
93 Resource newR = changes3.getComparable().getRight(origToOld.getKey());
95 comparator.addComparableResources(oldR, newR);
98 // 2. mark removed resources as distinct, so that comparison does not pair them
99 for (Statement s : changes2.getDeletions()) {
100 if (changes3.getComparable().containsLeft(s.getObject()))
101 comparator.addNonMatchedRight(changes3.getComparable().getRight(s.getObject()));
104 for (Statement s : changes3.getDeletions()) {
105 if (changes2.getComparable().containsLeft(s.getObject()))
106 comparator.addNonMatchedLeft(changes2.getComparable().getRight(s.getObject()));
109 // 3. mark added resources as distinct, so that comparison does not pair them
110 for (Statement s : changes2.getAdditions()) {
111 comparator.addNonMatchedLeft(s.getObject());
114 for (Statement s : changes3.getAdditions()) {
115 comparator.addNonMatchedRight(s.getObject());
119 comparator.test(getSession());
120 changes = comparator.getChanges();
121 changes = getSession().syncRequest(new FilterChangesRead(changes));
122 updateTree = getUpdateTree(changes);
123 updateList = getUpdateList(changes);
126 if (originalModel != null) {
135 protected abstract Pair<GraphComparator,String> getChanges(Resource r1, Resource r2) throws DatabaseException;
136 protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
137 protected UpdateList getUpdateList(GraphChanges changes) throws DatabaseException {
138 return new UpdateList(changes, changes.getModifications());
141 protected void addFilters(List<ChangeFilter> filters) {
145 public Resource getOldModel() {
149 public Resource getNewModel() {
153 public Resource getOriginalModel() {
154 return originalModel;
157 public boolean isInit() {
161 public GraphChanges getChanges() {
164 public UpdateTree getUpdateTree() {
167 public UpdateList getUpdateList() {
170 public GraphChanges getChanges2() {
174 public UpdateTree getUpdateTree2() {
177 public UpdateList getUpdateList2() {
181 public GraphChanges getChanges3() {
185 public UpdateTree getUpdateTree3() throws DatabaseException{
186 if (updateTree3 == null && changes3 != null)
187 updateTree3 = getUpdateTree(changes3);
190 public UpdateList getUpdateList3() throws DatabaseException {
191 if (updateList3 == null && changes3 != null)
192 updateList3 = getUpdateList(changes3);
197 public void applyAll(WriteGraph graph) throws DatabaseException {
198 Layer0Utils.addCommentMetadata(graph, "Apply all model updates");
199 graph.markUndoPoint();
200 for (PropertyChange mod : updateList.getChanges()) {
204 updateTree.getUpdateOps().applyAll(graph);
207 public void applySelected(WriteGraph graph) throws DatabaseException {
208 Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
209 graph.markUndoPoint();
210 for (PropertyChange mod : updateList.getChanges()) {
215 updateTree.getUpdateOps().applySelected(graph);
221 protected Session getSession() {
222 return Simantics.getSession();
227 private class FilterChangesRead implements Read<GraphChanges> {
228 private GraphChanges changes;
229 public FilterChangesRead(GraphChanges changes) {
230 this.changes = changes;
234 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
235 return filterChanges(graph, changes);
241 * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
242 * 2. Runs custom filters for value changes.
247 * @throws DatabaseException
249 protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException
252 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
254 for (Pair<Statement, Statement> mod : changes.getModifications()) {
256 boolean accept = true;
257 for (ChangeFilter filter : filters) {
258 if (!filter.accept(g, mod)) {
264 modifications.add(mod);
266 GraphChanges newChanges = new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
270 public interface ChangeFilter {
271 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
277 * Filters floating point value changes (default filter is set filter when the change is less than 1%)
280 protected class FPValueFilter implements ChangeFilter {
282 private double percentage = 0.01;
284 public FPValueFilter() {
288 public FPValueFilter(double percentage) {
289 if (percentage < 0.0 || percentage > 1.0)
290 throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
291 this.percentage = percentage;
295 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
296 //filter floating point values that have less than 1% difference.
297 if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
299 Object v1 = g.getValue(change.first.getObject());
300 Object v2 = g.getValue(change.second.getObject());
302 if (v1 instanceof Double && v2 instanceof Double) {
303 double d1 = (Double)v1;
304 double d2 = (Double)v2;
305 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
307 } else if (v1 instanceof Float && v2 instanceof Float) {
308 float d1 = (Float)v1;
309 float d2 = (Float)v2;
310 if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
318 public void defaultSelections() {
319 if (changes3 == null) {
322 // select all changes
323 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
324 op.getValue().select(true);
328 for (PropertyChange pair : updateList.getChanges()) {
332 // preserve user-made changes (by removing selections)
333 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
334 UpdateOp op2 = updateTree2.getUpdateOps().getUpdateOp(op.getKey());
336 if (changes3.getComparable().containsRight(op.getKey())){
337 op2 = updateTree2.getUpdateOps().getUpdateOp(changes3.getComparable().getLeft(op.getKey()));
340 if (op2 != null && op.getValue().getClass() == op2.getClass()) {
341 op.getValue().select(false);
345 for (PropertyChange pair : updateList.getChanges()) {
346 if (pair.getFirst() != null) {
347 boolean found = false;
348 for (PropertyChange pair2 : updateList2.getChanges()) {
349 if (pair.getFirst().equals(pair2.getSecond())) {
362 private void showWarning(String string) {
363 for (WarningListener l : warningListeners)
364 l.showWarning(this, string);
367 private List<WarningListener> warningListeners = new ArrayList<>();
369 public static interface WarningListener {
370 void showWarning(ModelUpdate update, String warning);
373 public void addListener(WarningListener listener) {
374 warningListeners.add(listener);
377 public void removeListener(WarningListener listener) {
378 warningListeners.remove(listener);