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