]> gerrit.simantics Code Review - simantics/interop.git/blob - org.simantics.interop.update/src/org/simantics/interop/update/model/ModelUpdate.java
bf391d49e52e8a1fd367f1fbbfac29758b696314
[simantics/interop.git] / org.simantics.interop.update / src / org / simantics / interop / update / model / ModelUpdate.java
1 package org.simantics.interop.update.model;
2
3 import java.util.ArrayList;
4 import java.util.HashSet;
5 import java.util.List;
6 import java.util.Map.Entry;
7
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;
20
21 public abstract class ModelUpdate {
22         
23         private Resource oldModel;      // old model that is going to be updated (User modified model)
24         private Resource newModel;      // new model that contains the updates   (New design model)
25         private Resource originalModel; // original model (optional) that is used for detecting and retaining user made changes (Old design model)
26         
27         private GraphChanges changes;   // changes between old /new
28         private UpdateTree updateTree;
29         private UpdateList updateList;
30         
31         private GraphChanges changes2;  // changes between original / old
32         private UpdateTree updateTree2;
33         private UpdateList updateList2;
34         
35         private GraphChanges changes3;  // changes between original / new
36         private UpdateTree updateTree3;
37         private UpdateList updateList3;
38         
39         private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
40         
41         boolean init = false;
42         
43         public void setInput(Resource oldModel, Resource newModel) throws DatabaseException {
44                 setInput(oldModel, newModel, null, false);
45         }
46         
47         /**
48          * Initialises the ModelUpdate with given input
49          * @param oldModel the model that is going to be updated (User modified model)
50          * @param newModel the model containing updates (New design model)
51          * @param originalModel the model that is used for detecting and retaining user made changes (Old design model). Parameter can be null.
52          * @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. 
53          * @throws DatabaseException
54          */
55         public void setInput(Resource oldModel, Resource newModel, Resource originalModel, boolean newDistinct) throws DatabaseException{
56                 this.oldModel = oldModel;
57                 this.newModel = newModel;
58                 this.originalModel = originalModel;
59                 addFilters(filters);
60                 if (originalModel != null) {
61                         // tree way comparison
62                         // compare the original and the old model
63                         Pair<GraphComparator,String> result2 = getChanges(originalModel, oldModel);
64                         GraphComparator comparator2  = result2.first;
65                         if (result2.second != null)
66                                 showWarning(result2.second);
67                         comparator2.test(getSession());
68                         changes2 = comparator2.getChanges();
69                         changes2 = getSession().syncRequest(new FilterChangesRead(changes2));
70                         updateTree2 = getUpdateTree(changes2);
71                         updateList2 = getUpdateList(changes2);
72                         
73                         // compare the original and the new model
74                         Pair<GraphComparator,String> result3 = getChanges(originalModel,newModel);
75                         GraphComparator comparator3  = result3.first;
76                         if (result3.second != null)
77                                 showWarning(result3.second);
78                         comparator3.test(getSession());
79                         changes3 = comparator3.getChanges();
80                         changes3 = getSession().syncRequest(new FilterChangesRead(changes3));
81                 }
82                 
83                 Pair<GraphComparator,String> result = getChanges(oldModel,newModel);
84                 GraphComparator comparator  = result.first;
85                 if (result.second != null)
86                         showWarning(result.second);
87                 if (originalModel != null) {
88                         // three-way comparison: use change information to configure
89                         // the comparison between the old and the new model.
90                         
91                         // 1. map comparable resources
92                         for (Entry<Resource, Resource> origToOld : changes2.getComparable().getEntries()) {
93                                 Resource oldR = origToOld.getValue();
94                                 Resource newR = changes3.getComparable().getRight(origToOld.getKey());
95                                 if (newR != null) {
96                                         comparator.addComparableResources(oldR, newR);
97                                 }
98                         }
99                         // 2. mark removed resources as distinct, so that comparison does not pair them
100                         for (Statement s : changes2.getDeletions()) {
101                                 if (changes3.getComparable().containsLeft(s.getObject()))
102                                         comparator.addNonMatchedRight(changes3.getComparable().getRight(s.getObject()));
103                         }
104                         
105                         for (Statement s : changes3.getDeletions()) {
106                                 if (changes2.getComparable().containsLeft(s.getObject()))
107                                         comparator.addNonMatchedLeft(changes2.getComparable().getRight(s.getObject()));
108                         }
109                         if (newDistinct) {
110                                 // 3. mark added resources as distinct, so that comparison does not pair them
111                                 for (Statement s : changes2.getAdditions()) {
112                                         comparator.addNonMatchedLeft(s.getObject());
113                                 }
114                                 
115                                 for (Statement s : changes3.getAdditions()) {
116                                         comparator.addNonMatchedRight(s.getObject());
117                                 }
118                         }
119                 }
120                 comparator.test(getSession());
121                 changes = comparator.getChanges();
122                 changes = getSession().syncRequest(new FilterChangesRead(changes));
123                 updateTree = getUpdateTree(changes);
124                 updateList = getUpdateList(changes);
125                 
126                 
127                 if (originalModel != null) {
128                         defaultSelections();
129                 }
130                 
131                 init = true;
132         }
133         
134         
135         
136         protected abstract Pair<GraphComparator,String> getChanges(Resource r1, Resource r2)  throws DatabaseException;
137         protected abstract UpdateTree getUpdateTree(GraphChanges changes) throws DatabaseException;
138         protected UpdateList getUpdateList(GraphChanges changes) throws DatabaseException {
139                 return new UpdateList(changes.getModifications());
140         }
141         
142         protected void addFilters(List<ChangeFilter> filters) {
143                 
144         }
145         
146         public Resource getOldModel() {
147                 return oldModel;
148         }
149         
150         public Resource getNewModel() {
151                 return newModel;
152         }
153         
154         public Resource getOriginalModel() {
155                 return originalModel;
156         }
157         
158         public boolean isInit() {
159                 return init;
160         }
161         
162         public GraphChanges getChanges() {
163                 return changes;
164         }
165         public UpdateTree getUpdateTree() {
166                 return updateTree;
167         }
168         public UpdateList getUpdateList() {
169                 return updateList;
170         }
171         public GraphChanges getChanges2() {
172                 return changes2;
173         }
174         
175         public UpdateTree getUpdateTree2() {
176                 return updateTree2;
177         }
178         public UpdateList getUpdateList2() {
179                 return updateList2;
180         }
181         
182         public GraphChanges getChanges3() {
183                 return changes3;
184         }
185         
186         public UpdateTree getUpdateTree3() throws DatabaseException{
187                 if (updateTree3 == null && changes3 != null)
188                         updateTree3 = getUpdateTree(changes3);
189                 return updateTree3;
190         }
191         public UpdateList getUpdateList3() throws DatabaseException {
192                 if (updateList3 == null && changes3 != null)
193                         updateList3 = getUpdateList(changes3);
194                 return updateList3;
195         }
196
197         
198         public void applyAll(WriteGraph graph) throws DatabaseException {
199                 Layer0Utils.addCommentMetadata(graph, "Apply all model updates");
200                 graph.markUndoPoint();
201                 for (Pair<Statement, Statement> mod : updateList.getChanges()) {
202                         applyLiteralChange(graph, mod);
203                 }
204                 updateList.clear();
205                 
206                 updateTree.getUpdateOps().applyAll(graph);
207         }
208         
209         public void applySelected(WriteGraph graph) throws DatabaseException {
210                 Layer0Utils.addCommentMetadata(graph, "Apply selected model updates");
211                 graph.markUndoPoint();
212                 HashSet<Pair<Statement, Statement>> changes = new HashSet<>(updateList.getSelected());
213                 for (Pair<Statement, Statement> mod : changes) {
214                         updateList.removeChange(mod);
215                         applyLiteralChange(graph, mod);
216                 }
217                 
218                 updateTree.getUpdateOps().applySelected(graph);
219         }
220         
221         protected void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DatabaseException {
222                 if (mod.second == null) {
223                         graph.deny(mod.first);
224                         return;
225                 } 
226                 Resource s = changes.getComparable().getLeft(mod.second.getSubject());
227                 Resource pred = mod.second.getPredicate();
228                 if (graph.hasValue(mod.second.getObject())) {
229                         Object value = graph.getValue(mod.second.getObject());
230                         graph.claimLiteral(s, pred, value);
231                 } else {
232                         graph.deny(s,pred);
233                 }
234         }
235         
236         
237         protected Session getSession() {
238                 return Simantics.getSession();
239         }
240         
241         
242         
243         private class FilterChangesRead implements Read<GraphChanges> {
244                 private GraphChanges changes;
245                 public FilterChangesRead(GraphChanges changes) {
246                         this.changes = changes;
247                 }
248                 
249                 @Override
250                 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
251                         return filterChanges(graph, changes);
252                 }
253         }
254         
255         /**
256          * Filters changes:
257          * 1. Changes that are not essential for model update (changes that can be found when the models are axcatly the same)
258          * 2. Runs custom filters for value changes. 
259          * 
260          * @param g
261          * @param changes
262          * @return
263          * @throws DatabaseException
264          */
265         protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException 
266     {
267                 
268                 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
269                 
270         for (Pair<Statement, Statement> mod : changes.getModifications()) {
271                 
272                 boolean accept = true;
273                 for (ChangeFilter filter : filters) {
274                         if (!filter.accept(g, mod)) {
275                                 accept = false;
276                                 break;
277                         }       
278                 }
279                 if (accept)
280                         modifications.add(mod);
281         }
282         GraphChanges newChanges =  new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
283         return newChanges;
284     }
285         
286         public interface ChangeFilter {
287                 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
288         }
289
290         
291         /**
292          * 
293          * Filters floating point value changes (default filter is set filter when the change is less than 1%)  
294          *
295          */
296         protected class FPValueFilter implements ChangeFilter  {
297                 
298                 private double percentage = 0.01;
299                 
300                 public FPValueFilter() {
301                         
302                 }
303                 
304                 public FPValueFilter(double percentage) {
305                         if (percentage < 0.0 || percentage > 1.0)
306                                 throw new IllegalArgumentException("Percentage must be between 0.0 and 1.0.");
307                         this.percentage = percentage;
308                 }
309                 
310                 @Override
311                 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException {
312                         //filter floating point values that have less than 1% difference.
313                         if (!g.hasValue(change.first.getObject()) || !g.hasValue(change.second.getObject()))
314                                 return true;
315                 Object v1 = g.getValue(change.first.getObject());
316                 Object v2 = g.getValue(change.second.getObject());
317                 
318                 if (v1 instanceof Double && v2 instanceof Double) {
319                         double d1 = (Double)v1;
320                         double d2 = (Double)v2;
321                         if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
322                                 return false;
323                 } else if (v1 instanceof Float && v2 instanceof Float) {
324                         float d1 = (Float)v1;
325                         float d2 = (Float)v2;
326                         if (Math.abs(d1-d2) / Math.max(Math.abs(d1), Math.abs(d2)) < percentage)
327                                 return false;
328                 }
329
330                 return true;
331                 }
332         }
333         
334         public void defaultSelections() {
335                 if (changes3 == null) {
336                         return;
337                 }
338                 // select all changes
339                 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
340                         op.getValue().select(true);
341                 }
342                 
343                 
344                 for (Pair<Statement, Statement> pair : updateList.getChanges()) {
345                         updateList.addSelected(pair);
346                 }
347                 
348                 // preserve user-made changes (by removing selections)
349                 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
350                         UpdateOp op2 = updateTree2.getUpdateOps().getUpdateOp(op.getKey());
351                         if (op2 == null) {
352                                 if (changes3.getComparable().containsRight(op.getKey())){
353                                         op2 = updateTree2.getUpdateOps().getUpdateOp(changes3.getComparable().getLeft(op.getKey()));
354                                 }
355                         }
356                         if (op2 != null && op.getValue().getClass() == op2.getClass()) {
357                                 op.getValue().select(false);
358                         }
359                 }
360                 
361                 for (Pair<Statement, Statement> pair : updateList.getChanges()) {
362                         if (pair.first != null) {
363                                 boolean found = false;
364                                 for (Pair<Statement, Statement> pair2 : updateList2.getChanges()) {
365                                         if (pair.first.equals(pair2.second)) {
366                                                 found = true;
367                                                 break;
368                                         }
369                                 }
370                                 if (found) {
371                                         updateList.removeSelected(pair);
372                                 }
373                         }
374                 }
375                 
376         }
377         
378         private void showWarning(String string) {
379                 for (WarningListener l : warningListeners)
380                         l.showWarning(this, string);
381         }
382         
383         private List<WarningListener> warningListeners = new ArrayList<>();
384         
385         public static interface WarningListener {
386                 void showWarning(ModelUpdate update, String warning);
387         }
388         
389         public void addListener(WarningListener listener) {
390                 warningListeners.add(listener);
391         }
392         
393         public void removeListener(WarningListener listener) {
394                 warningListeners.remove(listener);
395         }
396 }