]> gerrit.simantics Code Review - simantics/interop.git/blob - org.simantics.interop.update/src/org/simantics/interop/update/model/ModelUpdate.java
Separating model update logic from UI
[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 GraphChanges changes3;
29         private UpdateTree updateTree2;
30         private UpdateList updateList2;
31         
32         private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
33         
34         boolean init = false;
35         
36         public void setInput(Resource oldModel, Resource newModel) throws DatabaseException {
37                 setInput(oldModel, newModel, null, false);
38         }
39         
40         public void setInput(Resource oldModel, Resource newModel, Resource originalModel, boolean newDistinct) throws DatabaseException{
41                 addFilters(filters);
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);
54                         
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));
63                 }
64                 
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.
72                         
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());
77                                 if (newR != null) {
78                                         comparator.addComparableResources(oldR, newR);
79                                 }
80                         }
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()));
85                         }
86                         
87                         for (Statement s : changes3.getDeletions()) {
88                                 if (changes2.getComparable().containsLeft(s.getObject()))
89                                         comparator.addNonMatchedLeft(changes2.getComparable().getRight(s.getObject()));
90                         }
91                         if (newDistinct) {
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());
95                                 }
96                                 
97                                 for (Statement s : changes3.getAdditions()) {
98                                         comparator.addNonMatchedRight(s.getObject());
99                                 }
100                         }
101                 }
102                 comparator.test(getSession());
103                 changes = comparator.getChanges();
104                 changes = getSession().syncRequest(new FilterChangesRead(changes));
105                 updateTree = getUpdateTree(changes);
106                 updateList = getUpdateList(changes);
107                 
108                 
109                 if (originalModel != null) {
110                         createDefaultSelections();
111                 }
112                 
113                 init = true;
114         }
115         
116         
117         
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());
122         }
123         
124         protected void addFilters(List<ChangeFilter> filters) {
125                 
126         }
127         
128         public boolean isInit() {
129                 return init;
130         }
131         
132         public GraphChanges getChanges() {
133                 return changes;
134         }
135         public UpdateTree getUpdateTree() {
136                 return updateTree;
137         }
138         public UpdateList getUpdateList() {
139                 return updateList;
140         }
141         public GraphChanges getChanges2() {
142                 return changes2;
143         }
144         public GraphChanges getChanges3() {
145                 return changes3;
146         }
147         public UpdateTree getUpdateTree2() {
148                 return updateTree2;
149         }
150         public UpdateList getUpdateList2() {
151                 return updateList2;
152         }
153
154         
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);
160                 }
161                 updateList.clear();
162                 
163                 updateTree.getUpdateOps().applyAll(graph);
164         }
165         
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);
173                 }
174                 
175                 updateTree.getUpdateOps().applySelected(graph);
176         }
177         
178         protected void applyLiteralChange(WriteGraph graph, Pair<Statement, Statement> mod) throws DatabaseException {
179                 if (mod.second == null) {
180                         graph.deny(mod.first);
181                         return;
182                 } 
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);
188                 } else {
189                         graph.deny(s,pred);
190                 }
191         }
192         
193         
194         protected Session getSession() {
195                 return Simantics.getSession();
196         }
197         
198         
199         
200         private class FilterChangesRead implements Read<GraphChanges> {
201                 private GraphChanges changes;
202                 public FilterChangesRead(GraphChanges changes) {
203                         this.changes = changes;
204                 }
205                 
206                 @Override
207                 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
208                         return filterChanges(graph, changes);
209                 }
210         }
211         
212         /**
213          * Filters 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. 
216          * 
217          * @param g
218          * @param changes
219          * @return
220          * @throws DatabaseException
221          */
222         protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException 
223     {
224                 
225                 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
226                 
227         for (Pair<Statement, Statement> mod : changes.getModifications()) {
228                 
229                 boolean accept = true;
230                 for (ChangeFilter filter : filters) {
231                         if (!filter.accept(g, mod)) {
232                                 accept = false;
233                                 break;
234                         }       
235                 }
236                 if (accept)
237                         modifications.add(mod);
238         }
239         GraphChanges newChanges =  new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
240         return newChanges;
241     }
242         
243         public interface ChangeFilter {
244                 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
245         }
246
247         
248         /**
249          * 
250          * Filters floating point value changes (default filter is set filter when the change is less than 1%)  
251          *
252          */
253         protected class FPValueFilter implements ChangeFilter  {
254                 
255                 private double percentage = 0.01;
256                 
257                 public FPValueFilter() {
258                         
259                 }
260                 
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;
265                 }
266                 
267                 @Override
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()))
271                                 return true;
272                 Object v1 = g.getValue(change.first.getObject());
273                 Object v2 = g.getValue(change.second.getObject());
274                 
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)
279                                 return false;
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)
284                                 return false;
285                 }
286
287                 return true;
288                 }
289         }
290         
291         protected void createDefaultSelections() {
292                 // select all changes
293                 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
294                         op.getValue().select(true);
295                 }
296                 
297                 
298                 for (Pair<Statement, Statement> pair : updateList.getChanges()) {
299                         updateList.addSelected(pair);
300                 }
301                 
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());
305                         if (op2 == null) {
306                                 if (changes3.getComparable().containsRight(op.getKey())){
307                                         op2 = updateTree2.getUpdateOps().getUpdateOp(changes3.getComparable().getLeft(op.getKey()));
308                                 }
309                         }
310                         if (op2 != null && op.getValue().getClass() == op2.getClass()) {
311                                 op.getValue().select(false);
312                         }
313                 }
314                 
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)) {
320                                                 found = true;
321                                                 break;
322                                         }
323                                 }
324                                 if (found) {
325                                         updateList.removeSelected(pair);
326                                 }
327                         }
328                 }
329                 
330         }
331         
332         private void showWarning(String string) {
333                 for (WarningListener l : warningListeners)
334                         l.showWarning(this, string);
335         }
336         
337         private List<WarningListener> warningListeners = new ArrayList<>();
338         
339         public static interface WarningListener {
340                 void showWarning(ModelUpdate update, String warning);
341         }
342         
343         public void addListener(WarningListener listener) {
344                 warningListeners.add(listener);
345         }
346         
347         public void removeListener(WarningListener listener) {
348                 warningListeners.remove(listener);
349         }
350 }