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;
9 import java.util.function.Consumer;
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
16 * @author Hannu Niemistö
18 abstract public class MappingBase<T extends ComponentBase<T>> {
20 abstract public T getConfiguration();
23 * Set of all components indexed by their UID.
25 transient protected THashMap<String, T> configurationByUid;
28 * Set of components whose removal is delayed because they might
29 * have been moved somewhere else.
31 transient THashSet<T> pendingRemoval = new THashSet<T>();
34 * This is a structure that is used to return the state of the removed modules
35 * when the removal is undone.
37 public transient StateUndoContextBase undoContext;
39 public transient ComponentFactory<T> componentFactory;
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.
47 public long currentRevision;
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
55 private boolean trustUids;
57 public MappingBase() {
58 this(null, -1L, false);
61 public MappingBase(T configuration, boolean trustUids) {
62 this(configuration, -1L, trustUids);
65 public MappingBase(T configuration, long currentRevision, boolean trustUids) {
66 undoContext = createUndoContext();
67 componentFactory = createComponentFactory();
69 createConfigurationById(configuration);
70 this.currentRevision = currentRevision;
71 this.trustUids = trustUids;
74 abstract public StateUndoContextBase createUndoContext();
75 abstract public ComponentFactory<T> createComponentFactory();
77 protected void createConfigurationById(T configuration) {
78 THashMap<String, T> configurationByUid = new THashMap<String, T>();
79 browseConfiguration(configurationByUid, configuration);
80 this.configurationByUid = configurationByUid;
83 private void browseConfiguration(
84 THashMap<String, T> configurationByUid,
86 configurationByUid.put(configuration.uid, configuration);
87 for(T child : configuration.getChildren()) {
88 browseConfiguration(configurationByUid, child);
89 child.parent = configuration;
93 public T detachOrCreateComponent(String uid) {
94 T result = configurationByUid.get(uid);
96 result = componentFactory.create(uid);
97 configurationByUid.put(uid, result);
100 if(result.getParent() == null)
101 pendingRemoval.remove(result);
103 result.getParent().detachByUid(uid);
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
114 public void addPendingRemoval(T component) {
115 pendingRemoval.add(component);
118 public void printUidMap() {
119 printUidMap(new PrintWriter(System.out));
122 public void printUidMap(final PrintWriter out) {
123 out.println("Component tree");
125 getConfiguration().printConfiguration(out, 1);
126 if(configurationByUid != null) {
128 configurationByUid.forEachEntry(new TObjectObjectProcedure<String, T>() {
130 public boolean execute(String a, T b) {
131 out.println(" " + a + " (" + b.solverComponentName + ", " + b.componentId + ", " + b.uid + ")");
139 * Removes the component recursively.
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>() {
147 public boolean execute(T child) {
148 remove(solver, child);
152 if(component.componentId > 0 && !component.attached)
153 solver.remove(component.componentId);
157 * Saves undo state recursively
159 public void saveUndoState(final Solver solver, T component) {
160 if(component.getChildMap() != null)
161 component.getChildMap().forEachValue(new TObjectProcedure<T>() {
163 public boolean execute(T child) {
164 saveUndoState(solver, child);
168 else if(component.componentId > 0 && !component.attached)
169 undoContext.saveState(solver, component.componentId, component.uid);
173 * Removes components that are marked pending with {@link #addPendingRemoval} method.
175 public void removePending(final Solver solver) {
176 pendingRemoval.forEach(new TObjectProcedure<T>() {
178 public boolean execute(T component) {
179 saveUndoState(solver, component);
183 pendingRemoval.forEach(new TObjectProcedure<T>() {
185 public boolean execute(T component) {
186 remove(solver, component);
190 pendingRemoval.clear();
194 * Changes the {@link #trustUids} flag.
196 public void setTrustUids(boolean trustUids) {
197 if(trustUids != this.trustUids) {
198 this.trustUids = trustUids;
200 T configuration = getConfiguration();
201 if(configuration != null)
202 createConfigurationById(configuration);
206 configurationByUid = null;
209 public boolean getTrustUids() {
213 public void dispose() {
214 if (configurationByUid != null)
215 configurationByUid.clear();
216 pendingRemoval.clear();
219 public boolean hasPendingRemovals() {
220 return !pendingRemoval.isEmpty();
223 public void forEachPendingRemoval(Consumer<T> consumer) {
224 pendingRemoval.forEach(c -> {