1 package org.simantics.structural.synchronization.base;
3 import java.io.PrintWriter;
4 import java.util.Collections;
6 import java.util.function.Consumer;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
11 import gnu.trove.map.hash.THashMap;
12 import gnu.trove.procedure.TObjectObjectProcedure;
13 import gnu.trove.procedure.TObjectProcedure;
14 import gnu.trove.set.hash.THashSet;
17 * The entry point to the mapping structure between Simantics database and a
18 * designated solver. It is used to synchronize changes from Simantics to the
21 * @author Hannu Niemistö
23 abstract public class MappingBase<T extends ComponentBase<T>> {
25 private final transient Logger LOGGER = LoggerFactory.getLogger(getClass());
27 abstract public T getConfiguration();
30 * Set of all components indexed by their UID.
32 transient protected THashMap<String, T> configurationByUid;
35 * Set of all components indexed by their solver name.
37 transient protected Map<String, T> configurationBySolverName;
40 * Set of components whose removal is delayed because they might
41 * have been moved somewhere else.
43 transient THashSet<T> pendingRemoval = new THashSet<T>();
46 * This is a structure that is used to return the state of the removed modules
47 * when the removal is undone.
49 public transient StateUndoContextBase undoContext;
51 public transient ComponentFactory<T> componentFactory;
54 * The synchronization has processed all change sets before this change set
55 * (excluding this change set).
56 * It means that next synchronization operation should start examining changes
57 * made in this revision and revisions after this.
59 public long currentRevision;
62 * Tells whether the uids in the components can be used
63 * in the synchronization. This is normally true, but
64 * may be set temporarily false when export/import:in
67 private boolean trustUids;
69 public MappingBase() {
70 this(null, -1L, false);
73 public MappingBase(T configuration, boolean trustUids) {
74 this(configuration, -1L, trustUids);
77 public MappingBase(T configuration, long currentRevision, boolean trustUids) {
78 undoContext = createUndoContext();
79 componentFactory = createComponentFactory();
81 createConfigurationById(configuration);
82 this.currentRevision = currentRevision;
83 this.trustUids = trustUids;
86 abstract public StateUndoContextBase createUndoContext();
87 abstract public ComponentFactory<T> createComponentFactory();
89 protected void createConfigurationById(T configuration) {
90 THashMap<String, T> configurationByUid = new THashMap<String, T>();
91 browseConfiguration(configurationByUid, configuration);
92 this.configurationByUid = configurationByUid;
95 private void browseConfiguration(
96 THashMap<String, T> configurationByUid,
98 configurationByUid.put(configuration.uid, configuration);
99 for(T child : configuration.getChildren()) {
100 browseConfiguration(configurationByUid, child);
101 child.parent = configuration;
105 public Map<String, T> getConfigurationBySolverName() {
106 Map<String, T> result = configurationBySolverName;
107 if (result == null) {
108 T configuration = getConfiguration();
109 if (configuration != null)
110 result = configurationBySolverName = createConfigurationBySolverName(configuration);
112 result = Collections.emptyMap();
117 protected Map<String, T> createConfigurationBySolverName(T configuration) {
118 THashMap<String, T> configurationBySolverName = new THashMap<>();
119 browseConfigurationBySolverName(configurationBySolverName, configuration);
120 return configurationBySolverName;
123 private void browseConfigurationBySolverName(
124 THashMap<String, T> configurationBySolverName,
126 if (configuration.solverComponentName != null) {
127 configurationBySolverName.put(configuration.solverComponentName, configuration);
128 } else if (configuration.componentId != 0) {
129 LOGGER.warn("configuration.solverComponentName is null! configuration uid is {} and component id {}", configuration.getUid(), configuration.componentId);
131 for(T child : configuration.getChildren()) {
132 browseConfigurationBySolverName(configurationBySolverName, child);
133 child.parent = configuration;
137 public T detachOrCreateComponent(String uid) {
138 T result = configurationByUid.get(uid);
140 result = componentFactory.create(uid);
141 configurationByUid.put(uid, result);
142 configurationBySolverName = null; // forces recalculation
145 if(result.getParent() == null)
146 pendingRemoval.remove(result);
148 result.getParent().detachByUid(uid);
154 * Marks that the component should be removed. The actual removal
155 * is delayed until the call of {@link #removePending} so that if the
156 * component is not actually removed but moved or renamed, we don't lose
159 public void addPendingRemoval(T component) {
160 pendingRemoval.add(component);
163 public void printUidMap() {
164 printUidMap(new PrintWriter(System.out));
167 public void printUidMap(final PrintWriter out) {
168 out.println("Component tree");
170 getConfiguration().printConfiguration(out, 1);
171 if(configurationByUid != null) {
173 configurationByUid.forEachEntry(new TObjectObjectProcedure<String, T>() {
175 public boolean execute(String a, T b) {
176 out.println(" " + a + " (" + b.solverComponentName + ", " + b.componentId + ", " + b.uid + ")");
184 * Removes the component recursively.
186 public void remove(final Solver solver, T component) {
187 if(configurationByUid != null)
188 configurationByUid.remove(component.uid);
189 if (configurationBySolverName != null && component.solverComponentName != null)
190 configurationBySolverName.remove(component.solverComponentName);
191 if(component.getChildMap() != null)
192 component.getChildMap().forEachValue(new TObjectProcedure<T>() {
194 public boolean execute(T child) {
195 remove(solver, child);
199 if(component.componentId > 0 && !component.attached)
200 solver.remove(component.componentId);
204 * Saves undo state recursively
206 public void saveUndoState(final Solver solver, T component) {
207 if(component.getChildMap() != null)
208 component.getChildMap().forEachValue(new TObjectProcedure<T>() {
210 public boolean execute(T child) {
211 saveUndoState(solver, child);
215 else if(component.componentId > 0 && !component.attached)
216 undoContext.saveState(solver, component.componentId, component.uid);
220 * Removes components that are marked pending with {@link #addPendingRemoval} method.
222 public void removePending(final Solver solver) {
223 pendingRemoval.forEach(new TObjectProcedure<T>() {
225 public boolean execute(T component) {
226 saveUndoState(solver, component);
230 pendingRemoval.forEach(new TObjectProcedure<T>() {
232 public boolean execute(T component) {
233 remove(solver, component);
237 pendingRemoval.clear();
241 * Changes the {@link #trustUids} flag.
243 public void setTrustUids(boolean trustUids) {
244 if(trustUids != this.trustUids) {
245 this.trustUids = trustUids;
247 T configuration = getConfiguration();
248 if(configuration != null)
249 createConfigurationById(configuration);
253 configurationByUid = null;
256 public boolean getTrustUids() {
260 public void dispose() {
261 if (configurationByUid != null)
262 configurationByUid.clear();
263 if (configurationBySolverName != null) {
264 configurationBySolverName.clear();
265 configurationBySolverName = null;
267 pendingRemoval.clear();
270 public boolean hasPendingRemovals() {
271 return !pendingRemoval.isEmpty();
274 public void forEachPendingRemoval(Consumer<T> consumer) {
275 pendingRemoval.forEach(c -> {