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