--- /dev/null
+package org.simantics.structural.synchronization.base;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.procedure.TObjectObjectProcedure;\r
+import gnu.trove.procedure.TObjectProcedure;\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+import java.io.PrintWriter;\r
+\r
+/**\r
+ * The entry point to the mapping structure between Simantics database and a\r
+ * designated solver. It is used to synchronize changes from Simantics to the\r
+ * solver.\r
+ * \r
+ * @author Hannu Niemistö\r
+ */\r
+abstract public class MappingBase<T extends ComponentBase<T>> {\r
+\r
+ abstract public T getConfiguration();\r
+ \r
+ /**\r
+ * Set of all components indexed by their UID.\r
+ */\r
+ transient protected THashMap<String, T> configurationByUid;\r
+ \r
+ /** \r
+ * Set of components whose removal is delayed because they might\r
+ * have been moved somewhere else.\r
+ */\r
+ transient THashSet<T> pendingRemoval = new THashSet<T>();\r
+ \r
+ /**\r
+ * This is a structure that is used to return the state of the removed modules\r
+ * when the removal is undone.\r
+ */\r
+ public transient StateUndoContextBase undoContext;\r
+ \r
+ public transient ComponentFactory<T> componentFactory;\r
+ \r
+ /**\r
+ * The synchronization has processed all change sets before this change set \r
+ * (excluding this change set).\r
+ * It means that next synchronization operation should start examining changes\r
+ * made in this revision and revisions after this.\r
+ */\r
+ public long currentRevision;\r
+ \r
+ /**\r
+ * Tells whether the uids in the components can be used\r
+ * in the synchronization. This is normally true, but\r
+ * may be set temporarily false when export/import:in \r
+ * the model.\r
+ */\r
+ private boolean trustUids;\r
+\r
+ public MappingBase() {\r
+ this(null, -1L, false);\r
+ }\r
+\r
+ public MappingBase(T configuration, boolean trustUids) {\r
+ this(configuration, -1L, trustUids);\r
+ }\r
+\r
+ public MappingBase(T configuration, long currentRevision, boolean trustUids) {\r
+ undoContext = createUndoContext();\r
+ componentFactory = createComponentFactory();\r
+ if(trustUids)\r
+ createConfigurationById(configuration);\r
+ this.currentRevision = currentRevision;\r
+ this.trustUids = trustUids;\r
+ }\r
+ \r
+ abstract public StateUndoContextBase createUndoContext();\r
+ abstract public ComponentFactory<T> createComponentFactory();\r
+\r
+ protected void createConfigurationById(T configuration) {\r
+ THashMap<String, T> configurationByUid = new THashMap<String, T>();\r
+ browseConfiguration(configurationByUid, configuration);\r
+ this.configurationByUid = configurationByUid;\r
+ }\r
+\r
+ private void browseConfiguration(\r
+ THashMap<String, T> configurationByUid,\r
+ T configuration) {\r
+ configurationByUid.put(configuration.uid, configuration);\r
+ for(T child : configuration.getChildren()) {\r
+ browseConfiguration(configurationByUid, child);\r
+ child.parent = configuration;\r
+ }\r
+ }\r
+\r
+ public T detachOrCreateComponent(String uid) {\r
+ T result = configurationByUid.get(uid);\r
+ if(result == null) {\r
+ result = componentFactory.create(uid);\r
+ configurationByUid.put(uid, result);\r
+ }\r
+ else {\r
+ if(result.getParent() == null)\r
+ pendingRemoval.remove(result);\r
+ else\r
+ result.getParent().detachByUid(uid);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Marks that the component should be removed. The actual removal\r
+ * is delayed until the call of {@link #removePending} so that if the\r
+ * component is not actually removed but moved or renamed, we don't lose\r
+ * its state.\r
+ */\r
+ public void addPendingRemoval(T component) {\r
+ pendingRemoval.add(component);\r
+ }\r
+\r
+ public void printUidMap() {\r
+ printUidMap(new PrintWriter(System.out));\r
+ }\r
+\r
+ public void printUidMap(final PrintWriter out) {\r
+ out.println("Component tree");\r
+ out.print(" ");\r
+ getConfiguration().printConfiguration(out, 1);\r
+ if(configurationByUid != null) {\r
+ out.println("UIDs");\r
+ configurationByUid.forEachEntry(new TObjectObjectProcedure<String, T>() {\r
+ @Override\r
+ public boolean execute(String a, T b) {\r
+ out.println(" " + a + " (" + b.solverComponentName + ", " + b.componentId + ", " + b.uid + ")");\r
+ return true;\r
+ }\r
+ });\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Removes the component recursively.\r
+ */\r
+ public void remove(final Solver solver, T component) {\r
+ if(configurationByUid != null)\r
+ configurationByUid.remove(component.uid);\r
+ if(component.getChildMap() != null)\r
+ component.getChildMap().forEachValue(new TObjectProcedure<T>() {\r
+ @Override\r
+ public boolean execute(T child) {\r
+ remove(solver, child);\r
+ return true;\r
+ }\r
+ });\r
+ if(component.componentId > 0 && !component.attached)\r
+ solver.remove(component.componentId);\r
+ }\r
+ \r
+ /**\r
+ * Saves undo state recursively\r
+ */\r
+ public void saveUndoState(final Solver solver, T component) {\r
+ if(component.getChildMap() != null)\r
+ component.getChildMap().forEachValue(new TObjectProcedure<T>() {\r
+ @Override\r
+ public boolean execute(T child) {\r
+ saveUndoState(solver, child);\r
+ return true;\r
+ }\r
+ });\r
+ else if(component.componentId > 0 && !component.attached)\r
+ undoContext.saveState(solver, component.componentId, component.uid);\r
+ }\r
+\r
+ /**\r
+ * Removes components that are marked pending with {@link #addPendingRemoval} method.\r
+ */\r
+ public void removePending(final Solver solver) {\r
+ pendingRemoval.forEach(new TObjectProcedure<T>() {\r
+ @Override\r
+ public boolean execute(T component) {\r
+ saveUndoState(solver, component);\r
+ return true;\r
+ }\r
+ });\r
+ pendingRemoval.forEach(new TObjectProcedure<T>() {\r
+ @Override\r
+ public boolean execute(T component) {\r
+ remove(solver, component);\r
+ return true;\r
+ }\r
+ });\r
+ pendingRemoval.clear();\r
+ }\r
+ \r
+ /**\r
+ * Changes the {@link #trustUids} flag.\r
+ */\r
+ public void setTrustUids(boolean trustUids) {\r
+ if(trustUids != this.trustUids) {\r
+ this.trustUids = trustUids;\r
+ if(trustUids) {\r
+ T configuration = getConfiguration();\r
+ if(configuration != null)\r
+ createConfigurationById(configuration);\r
+ }\r
+ }\r
+ if(!trustUids)\r
+ configurationByUid = null;\r
+ }\r
+ \r
+ public boolean getTrustUids() {\r
+ return trustUids;\r
+ }\r
+\r
+ public void dispose() {\r
+ if (configurationByUid != null)\r
+ configurationByUid.clear();\r
+ pendingRemoval.clear();\r
+ }\r
+\r
+}\r