]> gerrit.simantics Code Review - simantics/interop.git/blob - org.simantics.interop.update/src/org/simantics/interop/update/model/ModelUpdate.java
Default property selection was using a wrong resource for comparison
[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 GraphChanges changes;
24         private UpdateTree updateTree;
25         private UpdateList updateList;
26         
27         private GraphChanges changes2;
28         private UpdateTree updateTree2;
29         private UpdateList updateList2;
30         
31         private GraphChanges changes3;
32         private UpdateTree updateTree3;
33         private UpdateList updateList3;
34         
35         private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
36         
37         boolean init = false;
38         
39         public void setInput(Resource oldModel, Resource newModel) throws DatabaseException {
40                 setInput(oldModel, newModel, null, false);
41         }
42         
43         public void setInput(Resource oldModel, Resource newModel, Resource originalModel, boolean newDistinct) throws DatabaseException{
44                 addFilters(filters);
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);
57                         
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));
66                 }
67                 
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.
75                         
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());
80                                 if (newR != null) {
81                                         comparator.addComparableResources(oldR, newR);
82                                 }
83                         }
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()));
88                         }
89                         
90                         for (Statement s : changes3.getDeletions()) {
91                                 if (changes2.getComparable().containsLeft(s.getObject()))
92                                         comparator.addNonMatchedLeft(changes2.getComparable().getRight(s.getObject()));
93                         }
94                         if (newDistinct) {
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());
98                                 }
99                                 
100                                 for (Statement s : changes3.getAdditions()) {
101                                         comparator.addNonMatchedRight(s.getObject());
102                                 }
103                         }
104                 }
105                 comparator.test(getSession());
106                 changes = comparator.getChanges();
107                 changes = getSession().syncRequest(new FilterChangesRead(changes));
108                 updateTree = getUpdateTree(changes);
109                 updateList = getUpdateList(changes);
110                 
111                 
112                 if (originalModel != null) {
113                         createDefaultSelections();
114                 }
115                 
116                 init = true;
117         }
118         
119         
120         
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());
125         }
126         
127         protected void addFilters(List<ChangeFilter> filters) {
128                 
129         }
130         
131         public boolean isInit() {
132                 return init;
133         }
134         
135         public GraphChanges getChanges() {
136                 return changes;
137         }
138         public UpdateTree getUpdateTree() {
139                 return updateTree;
140         }
141         public UpdateList getUpdateList() {
142                 return updateList;
143         }
144         public GraphChanges getChanges2() {
145                 return changes2;
146         }
147         
148         public UpdateTree getUpdateTree2() {
149                 return updateTree2;
150         }
151         public UpdateList getUpdateList2() {
152                 return updateList2;
153         }
154         
155         public GraphChanges getChanges3() {
156                 return changes3;
157         }
158         
159         public UpdateTree getUpdateTree3() throws DatabaseException{
160                 if (updateTree3 == null && changes3 != null)
161                         updateTree3 = getUpdateTree(changes3);
162                 return updateTree3;
163         }
164         public UpdateList getUpdateList3() throws DatabaseException {
165                 if (updateList3 == null && changes3 != null)
166                         updateList3 = getUpdateList(changes3);
167                 return updateList3;
168         }
169
170         
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);
176                 }
177                 updateList.clear();
178                 
179                 updateTree.getUpdateOps().applyAll(graph);
180         }
181         
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);
189                 }
190                 
191                 updateTree.getUpdateOps().applySelected(graph);
192         }
193         
194         protected void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DatabaseException {
195                 if (mod.second == null) {
196                         graph.deny(mod.first);
197                         return;
198                 } 
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);
204                 } else {
205                         graph.deny(s,pred);
206                 }
207         }
208         
209         
210         protected Session getSession() {
211                 return Simantics.getSession();
212         }
213         
214         
215         
216         private class FilterChangesRead implements Read<GraphChanges> {
217                 private GraphChanges changes;
218                 public FilterChangesRead(GraphChanges changes) {
219                         this.changes = changes;
220                 }
221                 
222                 @Override
223                 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
224                         return filterChanges(graph, changes);
225                 }
226         }
227         
228         /**
229          * Filters 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. 
232          * 
233          * @param g
234          * @param changes
235          * @return
236          * @throws DatabaseException
237          */
238         protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException 
239     {
240                 
241                 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
242                 
243         for (Pair<Statement, Statement> mod : changes.getModifications()) {
244                 
245                 boolean accept = true;
246                 for (ChangeFilter filter : filters) {
247                         if (!filter.accept(g, mod)) {
248                                 accept = false;
249                                 break;
250                         }       
251                 }
252                 if (accept)
253                         modifications.add(mod);
254         }
255         GraphChanges newChanges =  new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
256         return newChanges;
257     }
258         
259         public interface ChangeFilter {
260                 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
261         }
262
263         
264         /**
265          * 
266          * Filters floating point value changes (default filter is set filter when the change is less than 1%)  
267          *
268          */
269         protected class FPValueFilter implements ChangeFilter  {
270                 
271                 private double percentage = 0.01;
272                 
273                 public FPValueFilter() {
274                         
275                 }
276                 
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;
281                 }
282                 
283                 @Override
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()))
287                                 return true;
288                 Object v1 = g.getValue(change.first.getObject());
289                 Object v2 = g.getValue(change.second.getObject());
290                 
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)
295                                 return false;
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)
300                                 return false;
301                 }
302
303                 return true;
304                 }
305         }
306         
307         protected void createDefaultSelections() {
308                 // select all changes
309                 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
310                         op.getValue().select(true);
311                 }
312                 
313                 
314                 for (Pair<Statement, Statement> pair : updateList.getChanges()) {
315                         updateList.addSelected(pair);
316                 }
317                 
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());
321                         if (op2 == null) {
322                                 if (changes3.getComparable().containsRight(op.getKey())){
323                                         op2 = updateTree2.getUpdateOps().getUpdateOp(changes3.getComparable().getLeft(op.getKey()));
324                                 }
325                         }
326                         if (op2 != null && op.getValue().getClass() == op2.getClass()) {
327                                 op.getValue().select(false);
328                         }
329                 }
330                 
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)) {
336                                                 found = true;
337                                                 break;
338                                         }
339                                 }
340                                 if (found) {
341                                         updateList.removeSelected(pair);
342                                 }
343                         }
344                 }
345                 
346         }
347         
348         private void showWarning(String string) {
349                 for (WarningListener l : warningListeners)
350                         l.showWarning(this, string);
351         }
352         
353         private List<WarningListener> warningListeners = new ArrayList<>();
354         
355         public static interface WarningListener {
356                 void showWarning(ModelUpdate update, String warning);
357         }
358         
359         public void addListener(WarningListener listener) {
360                 warningListeners.add(listener);
361         }
362         
363         public void removeListener(WarningListener listener) {
364                 warningListeners.remove(listener);
365         }
366 }