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