]> gerrit.simantics Code Review - simantics/interop.git/blob - org.simantics.interop.update/src/org/simantics/interop/update/model/ModelUpdate.java
46bf79ecbcdeccd3679b684e924ca13e0214a5b1
[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.List;
5 import java.util.Map.Entry;
6
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;
19
20 public abstract class ModelUpdate {
21         
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)
25         
26         private GraphChanges changes;   // changes between old /new
27         private UpdateTree updateTree;
28         private UpdateList updateList;
29         
30         private GraphChanges changes2;  // changes between original / old
31         private UpdateTree updateTree2;
32         private UpdateList updateList2;
33         
34         private GraphChanges changes3;  // changes between original / new
35         private UpdateTree updateTree3;
36         private UpdateList updateList3;
37         
38         private List<ChangeFilter> filters = new ArrayList<ChangeFilter>();
39         
40         boolean init = false;
41         
42         public void setInput(Resource oldModel, Resource newModel) throws DatabaseException {
43                 setInput(oldModel, newModel, null, false);
44         }
45         
46         /**
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
53          */
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;
58                 addFilters(filters);
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);
71                         
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));
80                 }
81                 
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.
89                         
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());
94                                 if (newR != null) {
95                                         comparator.addComparableResources(oldR, newR);
96                                 }
97                         }
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()));
102                         }
103                         
104                         for (Statement s : changes3.getDeletions()) {
105                                 if (changes2.getComparable().containsLeft(s.getObject()))
106                                         comparator.addNonMatchedLeft(changes2.getComparable().getRight(s.getObject()));
107                         }
108                         if (newDistinct) {
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());
112                                 }
113                                 
114                                 for (Statement s : changes3.getAdditions()) {
115                                         comparator.addNonMatchedRight(s.getObject());
116                                 }
117                         }
118                 }
119                 comparator.test(getSession());
120                 changes = comparator.getChanges();
121                 changes = getSession().syncRequest(new FilterChangesRead(changes));
122                 updateTree = getUpdateTree(changes);
123                 updateList = getUpdateList(changes);
124                 
125                 
126                 if (originalModel != null) {
127                         defaultSelections();
128                 }
129                 
130                 init = true;
131         }
132         
133         
134         
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());
139         }
140         
141         protected void addFilters(List<ChangeFilter> filters) {
142                 
143         }
144         
145         public Resource getOldModel() {
146                 return oldModel;
147         }
148         
149         public Resource getNewModel() {
150                 return newModel;
151         }
152         
153         public Resource getOriginalModel() {
154                 return originalModel;
155         }
156         
157         public boolean isInit() {
158                 return init;
159         }
160         
161         public GraphChanges getChanges() {
162                 return changes;
163         }
164         public UpdateTree getUpdateTree() {
165                 return updateTree;
166         }
167         public UpdateList getUpdateList() {
168                 return updateList;
169         }
170         public GraphChanges getChanges2() {
171                 return changes2;
172         }
173         
174         public UpdateTree getUpdateTree2() {
175                 return updateTree2;
176         }
177         public UpdateList getUpdateList2() {
178                 return updateList2;
179         }
180         
181         public GraphChanges getChanges3() {
182                 return changes3;
183         }
184         
185         public UpdateTree getUpdateTree3() throws DatabaseException{
186                 if (updateTree3 == null && changes3 != null)
187                         updateTree3 = getUpdateTree(changes3);
188                 return updateTree3;
189         }
190         public UpdateList getUpdateList3() throws DatabaseException {
191                 if (updateList3 == null && changes3 != null)
192                         updateList3 = getUpdateList(changes3);
193                 return updateList3;
194         }
195
196         
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()) {
201                         mod.apply(graph);
202                 }
203                 
204                 updateTree.getUpdateOps().applyAll(graph);
205         }
206         
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()) {
211                         if (mod.selected())
212                                 mod.apply(graph);
213                 }
214                 
215                 updateTree.getUpdateOps().applySelected(graph);
216         }
217         
218         
219         
220         
221         protected Session getSession() {
222                 return Simantics.getSession();
223         }
224         
225         
226         
227         private class FilterChangesRead implements Read<GraphChanges> {
228                 private GraphChanges changes;
229                 public FilterChangesRead(GraphChanges changes) {
230                         this.changes = changes;
231                 }
232                 
233                 @Override
234                 public GraphChanges perform(ReadGraph graph) throws DatabaseException {
235                         return filterChanges(graph, changes);
236                 }
237         }
238         
239         /**
240          * Filters 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. 
243          * 
244          * @param g
245          * @param changes
246          * @return
247          * @throws DatabaseException
248          */
249         protected GraphChanges filterChanges(ReadGraph g, GraphChanges changes) throws DatabaseException 
250     {
251                 
252                 List<Pair<Statement,Statement>> modifications = new ArrayList<Pair<Statement,Statement>>();
253                 
254         for (Pair<Statement, Statement> mod : changes.getModifications()) {
255                 
256                 boolean accept = true;
257                 for (ChangeFilter filter : filters) {
258                         if (!filter.accept(g, mod)) {
259                                 accept = false;
260                                 break;
261                         }       
262                 }
263                 if (accept)
264                         modifications.add(mod);
265         }
266         GraphChanges newChanges =  new GraphChanges(changes.getResource1(),changes.getResource2(),changes.getDeletions(), changes.getAdditions(), modifications, changes.getComparable());
267         return newChanges;
268     }
269         
270         public interface ChangeFilter {
271                 public boolean accept(ReadGraph g, Pair<Statement, Statement> change) throws DatabaseException;
272         }
273
274         
275         /**
276          * 
277          * Filters floating point value changes (default filter is set filter when the change is less than 1%)  
278          *
279          */
280         protected class FPValueFilter implements ChangeFilter  {
281                 
282                 private double percentage = 0.01;
283                 
284                 public FPValueFilter() {
285                         
286                 }
287                 
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;
292                 }
293                 
294                 @Override
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()))
298                                 return true;
299                 Object v1 = g.getValue(change.first.getObject());
300                 Object v2 = g.getValue(change.second.getObject());
301                 
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)
306                                 return false;
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)
311                                 return false;
312                 }
313
314                 return true;
315                 }
316         }
317         
318         public void defaultSelections() {
319                 if (changes3 == null) {
320                         return;
321                 }
322                 // select all changes
323                 for (Entry<Resource, UpdateOp> op : updateTree.getUpdateOps().getResourceMap().entrySet()) {
324                         op.getValue().select(true);
325                 }
326                 
327                 
328                 for (PropertyChange pair : updateList.getChanges()) {
329                         pair.select(true);
330                 }
331                 
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());
335                         if (op2 == null) {
336                                 if (changes3.getComparable().containsRight(op.getKey())){
337                                         op2 = updateTree2.getUpdateOps().getUpdateOp(changes3.getComparable().getLeft(op.getKey()));
338                                 }
339                         }
340                         if (op2 != null && op.getValue().getClass() == op2.getClass()) {
341                                 op.getValue().select(false);
342                         }
343                 }
344                 
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())) {
350                                                 found = true;
351                                                 break;
352                                         }
353                                 }
354                                 if (found) {
355                                         pair.select(false);
356                                 }
357                         }
358                 }
359                 
360         }
361         
362         private void showWarning(String string) {
363                 for (WarningListener l : warningListeners)
364                         l.showWarning(this, string);
365         }
366         
367         private List<WarningListener> warningListeners = new ArrayList<>();
368         
369         public static interface WarningListener {
370                 void showWarning(ModelUpdate update, String warning);
371         }
372         
373         public void addListener(WarningListener listener) {
374                 warningListeners.add(listener);
375         }
376         
377         public void removeListener(WarningListener listener) {
378                 warningListeners.remove(listener);
379         }
380 }