Small changes to synchronization base impl to allow extending it
[simantics/platform.git] / bundles / org.simantics.structural.synchronization / src / org / simantics / structural / synchronization / utils / MappingBase.java
1 package org.simantics.structural.synchronization.utils;
2
3 import gnu.trove.map.hash.THashMap;
4 import gnu.trove.procedure.TObjectObjectProcedure;
5 import gnu.trove.procedure.TObjectProcedure;
6 import gnu.trove.set.hash.THashSet;
7
8 import java.io.PrintWriter;
9 import java.util.function.Consumer;
10
11 /**
12  * The entry point to the mapping structure between Simantics database and a
13  * designated solver. It is used to synchronize changes from Simantics to the
14  * solver.
15  * 
16  * @author Hannu Niemistö
17  */
18 abstract public class MappingBase<T extends ComponentBase<T>> {
19
20         abstract public T getConfiguration();
21     
22     /**
23      * Set of all components indexed by their UID.
24      */
25     transient protected THashMap<String, T> configurationByUid;
26     
27     /** 
28      * Set of components whose removal is delayed because they might
29      * have been moved somewhere else.
30      */
31     transient THashSet<T> pendingRemoval = new THashSet<T>();
32     
33     /**
34      * This is a structure that is used to return the state of the removed modules
35      * when the removal is undone.
36      */
37     public transient StateUndoContextBase undoContext;
38     
39     public transient ComponentFactory<T> componentFactory;
40     
41     /**
42      * The synchronization has processed all change sets before this change set 
43      * (excluding this change set).
44      * It means that next synchronization operation should start examining changes
45      * made in this revision and revisions after this.
46      */
47     public long currentRevision;
48     
49     /**
50      * Tells whether the uids in the components can be used
51      * in the synchronization. This is normally true, but
52      * may be set temporarily false when export/import:in 
53      * the model.
54      */
55     private boolean trustUids;
56
57     public MappingBase() {
58         this(null, -1L, false);
59     }
60
61     public MappingBase(T configuration, boolean trustUids) {
62         this(configuration, -1L, trustUids);
63     }
64
65     public MappingBase(T configuration, long currentRevision, boolean trustUids) {
66         undoContext = createUndoContext();
67         componentFactory = createComponentFactory();
68         if(trustUids)
69             createConfigurationById(configuration);
70         this.currentRevision = currentRevision;
71         this.trustUids = trustUids;
72     }
73     
74     abstract public StateUndoContextBase createUndoContext();
75     abstract public ComponentFactory<T> createComponentFactory();
76
77     protected void createConfigurationById(T configuration) {
78         THashMap<String, T> configurationByUid = new THashMap<String, T>();
79         browseConfiguration(configurationByUid, configuration);
80         this.configurationByUid = configurationByUid;
81     }
82
83     private void browseConfiguration(
84             THashMap<String, T> configurationByUid,
85             T configuration) {
86         configurationByUid.put(configuration.uid, configuration);
87         for(T child : configuration.getChildren()) {
88             browseConfiguration(configurationByUid, child);
89             child.parent = configuration;
90         }
91     }
92
93     public T detachOrCreateComponent(String uid) {
94         T result = configurationByUid.get(uid);
95         if(result == null) {
96             result = componentFactory.create(uid);
97             configurationByUid.put(uid, result);
98         }
99         else {
100             if(result.getParent() == null)
101                 pendingRemoval.remove(result);
102             else
103                 result.getParent().detachByUid(uid);
104         }
105         return result;
106     }
107
108     /**
109      * Marks that the component should be removed. The actual removal
110      * is delayed until the call of {@link #removePending} so that if the
111      * component is not actually removed but moved or renamed, we don't lose
112      * its state.
113      */
114     public void addPendingRemoval(T component) {
115         pendingRemoval.add(component);
116     }
117
118     public void printUidMap() {
119         printUidMap(new PrintWriter(System.out));
120     }
121
122     public void printUidMap(final PrintWriter out) {
123         out.println("Component tree");
124         out.print("    ");
125         getConfiguration().printConfiguration(out, 1);
126         if(configurationByUid != null) {
127             out.println("UIDs");
128             configurationByUid.forEachEntry(new TObjectObjectProcedure<String, T>() {
129                 @Override
130                 public boolean execute(String a, T b) {
131                     out.println("    " + a + " (" + b.solverComponentName + ", " + b.componentId + ", " + b.uid + ")");
132                     return true;
133                 }
134             });
135         }
136     }
137
138     /**
139      * Removes the component recursively.
140      */
141     public void remove(final Solver solver, T component) {
142         if(configurationByUid != null)
143             configurationByUid.remove(component.uid);
144         if(component.getChildMap() != null)
145             component.getChildMap().forEachValue(new TObjectProcedure<T>() {
146                 @Override
147                 public boolean execute(T child) {
148                     remove(solver, child);
149                     return true;
150                 }
151             });
152         if(component.componentId > 0 && !component.attached)
153             solver.remove(component.componentId);
154     }
155     
156     /**
157      * Saves undo state recursively
158      */
159     public void saveUndoState(final Solver solver, T component) {
160         if(component.getChildMap() != null)
161             component.getChildMap().forEachValue(new TObjectProcedure<T>() {
162                 @Override
163                 public boolean execute(T child) {
164                     saveUndoState(solver, child);
165                     return true;
166                 }
167             });
168         else if(component.componentId > 0 && !component.attached)
169             undoContext.saveState(solver, component.componentId, component.uid);
170     }
171
172     /**
173      * Removes components that are marked pending with {@link #addPendingRemoval} method.
174      */
175     public void removePending(final Solver solver) {
176         pendingRemoval.forEach(new TObjectProcedure<T>() {
177             @Override
178             public boolean execute(T component) {
179                 saveUndoState(solver, component);
180                 return true;
181             }
182         });
183         pendingRemoval.forEach(new TObjectProcedure<T>() {
184             @Override
185             public boolean execute(T component) {
186                 remove(solver, component);
187                 return true;
188             }
189         });
190         pendingRemoval.clear();
191     }
192     
193     /**
194      * Changes the {@link #trustUids} flag.
195      */
196     public void setTrustUids(boolean trustUids) {
197         if(trustUids != this.trustUids) {
198             this.trustUids = trustUids;
199             if(trustUids) {
200                 T configuration = getConfiguration();
201                 if(configuration != null)
202                     createConfigurationById(configuration);
203             }
204         }
205         if(!trustUids)
206             configurationByUid = null;
207     }
208     
209     public boolean getTrustUids() {
210         return trustUids;
211     }
212
213     public void dispose() {
214         if (configurationByUid != null)
215             configurationByUid.clear();
216         pendingRemoval.clear();
217     }
218     
219     public boolean hasPendingRemovals() {
220         return !pendingRemoval.isEmpty();
221     }
222
223     public void forEachPendingRemoval(Consumer<T> consumer) {
224         pendingRemoval.forEach(c -> {
225             consumer.accept(c);
226             return true;
227         });
228     }
229
230 }