1 package org.simantics.structural.synchronization.utils;
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;
8 import java.io.PrintWriter;
10 import java.util.function.Consumer;
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
17 * @author Hannu Niemistö
19 abstract public class MappingBase<T extends ComponentBase<T>> {
21 abstract public T getConfiguration();
24 * Set of all components indexed by their UID.
26 transient protected THashMap<String, T> configurationByUid;
29 * Set of all components indexed by their solver name.
31 transient protected Map<String, T> configurationBySolverName;
34 * Set of components whose removal is delayed because they might
35 * have been moved somewhere else.
37 transient THashSet<T> pendingRemoval = new THashSet<T>();
40 * This is a structure that is used to return the state of the removed modules
41 * when the removal is undone.
43 public transient StateUndoContextBase undoContext;
45 public transient ComponentFactory<T> componentFactory;
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.
53 public long currentRevision;
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
61 private boolean trustUids;
63 public MappingBase() {
64 this(null, -1L, false);
67 public MappingBase(T configuration, boolean trustUids) {
68 this(configuration, -1L, trustUids);
71 public MappingBase(T configuration, long currentRevision, boolean trustUids) {
72 undoContext = createUndoContext();
73 componentFactory = createComponentFactory();
75 createConfigurationById(configuration);
76 this.currentRevision = currentRevision;
77 this.trustUids = trustUids;
80 abstract public StateUndoContextBase createUndoContext();
81 abstract public ComponentFactory<T> createComponentFactory();
83 protected void createConfigurationById(T configuration) {
84 THashMap<String, T> configurationByUid = new THashMap<String, T>();
85 browseConfiguration(configurationByUid, configuration);
86 this.configurationByUid = configurationByUid;
89 private void browseConfiguration(
90 THashMap<String, T> configurationByUid,
92 configurationByUid.put(configuration.uid, configuration);
93 for(T child : configuration.getChildren()) {
94 browseConfiguration(configurationByUid, child);
95 child.parent = configuration;
99 public Map<String, T> getConfigurationBySolverName() {
100 Map<String, T> result = configurationBySolverName;
102 result = configurationBySolverName = createConfigurationBySolverName(getConfiguration());
106 protected Map<String, T> createConfigurationBySolverName(T configuration) {
107 THashMap<String, T> configurationBySolverName = new THashMap<>();
108 browseConfigurationBySolverName(configurationBySolverName, configuration);
109 return configurationBySolverName;
112 private void browseConfigurationBySolverName(
113 THashMap<String, T> configurationBySolverName,
115 configurationBySolverName.put(configuration.solverComponentName, configuration);
116 for(T child : configuration.getChildren()) {
117 browseConfigurationBySolverName(configurationBySolverName, child);
118 child.parent = configuration;
122 public T detachOrCreateComponent(String uid) {
123 T result = configurationByUid.get(uid);
125 result = componentFactory.create(uid);
126 configurationByUid.put(uid, result);
129 if(result.getParent() == null)
130 pendingRemoval.remove(result);
132 result.getParent().detachByUid(uid);
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
143 public void addPendingRemoval(T component) {
144 pendingRemoval.add(component);
147 public void printUidMap() {
148 printUidMap(new PrintWriter(System.out));
151 public void printUidMap(final PrintWriter out) {
152 out.println("Component tree");
154 getConfiguration().printConfiguration(out, 1);
155 if(configurationByUid != null) {
157 configurationByUid.forEachEntry(new TObjectObjectProcedure<String, T>() {
159 public boolean execute(String a, T b) {
160 out.println(" " + a + " (" + b.solverComponentName + ", " + b.componentId + ", " + b.uid + ")");
168 * Removes the component recursively.
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>() {
178 public boolean execute(T child) {
179 remove(solver, child);
183 if(component.componentId > 0 && !component.attached)
184 solver.remove(component.componentId);
188 * Saves undo state recursively
190 public void saveUndoState(final Solver solver, T component) {
191 if(component.getChildMap() != null)
192 component.getChildMap().forEachValue(new TObjectProcedure<T>() {
194 public boolean execute(T child) {
195 saveUndoState(solver, child);
199 else if(component.componentId > 0 && !component.attached)
200 undoContext.saveState(solver, component.componentId, component.uid);
204 * Removes components that are marked pending with {@link #addPendingRemoval} method.
206 public void removePending(final Solver solver) {
207 pendingRemoval.forEach(new TObjectProcedure<T>() {
209 public boolean execute(T component) {
210 saveUndoState(solver, component);
214 pendingRemoval.forEach(new TObjectProcedure<T>() {
216 public boolean execute(T component) {
217 remove(solver, component);
221 pendingRemoval.clear();
225 * Changes the {@link #trustUids} flag.
227 public void setTrustUids(boolean trustUids) {
228 if(trustUids != this.trustUids) {
229 this.trustUids = trustUids;
231 T configuration = getConfiguration();
232 if(configuration != null)
233 createConfigurationById(configuration);
237 configurationByUid = null;
240 public boolean getTrustUids() {
244 public void dispose() {
245 if (configurationByUid != null)
246 configurationByUid.clear();
247 if (configurationBySolverName != null) {
248 configurationBySolverName.clear();
249 configurationBySolverName = null;
251 pendingRemoval.clear();
254 public boolean hasPendingRemovals() {
255 return !pendingRemoval.isEmpty();
258 public void forEachPendingRemoval(Consumer<T> consumer) {
259 pendingRemoval.forEach(c -> {