]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/utils/MappingBase.java
Added editable unit for derived properties
[simantics/platform.git] / bundles / org.simantics.structural.synchronization / src / org / simantics / structural / synchronization / utils / MappingBase.java
1 package org.simantics.structural.synchronization.utils;
2
3 import java.io.PrintWriter;
4
5 import gnu.trove.map.hash.THashMap;
6 import gnu.trove.procedure.TObjectObjectProcedure;
7 import gnu.trove.procedure.TObjectProcedure;
8 import gnu.trove.set.hash.THashSet;
9
10 /**
11  * The entry point to the mapping structure between Simantics database and a
12  * designated solver. It is used to synchronize changes from Simantics to the
13  * solver.
14  * 
15  * @author Hannu Niemistö
16  */
17 abstract public class MappingBase<T extends ComponentBase<T>> {
18
19         abstract public T getConfiguration();
20     
21     /**
22      * Set of all components indexed by their UID.
23      */
24     transient protected THashMap<String, T> configurationByUid;
25     
26     /** 
27      * Set of components whose removal is delayed because they might
28      * have been moved somewhere else.
29      */
30     transient THashSet<T> pendingRemoval = new THashSet<T>();
31     
32     /**
33      * This is a structure that is used to return the state of the removed modules
34      * when the removal is undone.
35      */
36     public transient StateUndoContextBase undoContext;
37     
38     public transient ComponentFactory<T> componentFactory;
39     
40     /**
41      * The synchronization has processed all change sets before this change set 
42      * (excluding this change set).
43      * It means that next synchronization operation should start examining changes
44      * made in this revision and revisions after this.
45      */
46     public long currentRevision;
47     
48     /**
49      * Tells whether the uids in the components can be used
50      * in the synchronization. This is normally true, but
51      * may be set temporarily false when export/import:in 
52      * the model.
53      */
54     private boolean trustUids;
55
56     public MappingBase() {
57         this(null, -1L, false);
58     }
59
60     public MappingBase(T configuration, boolean trustUids) {
61         this(configuration, -1L, trustUids);
62     }
63
64     public MappingBase(T configuration, long currentRevision, boolean trustUids) {
65         undoContext = createUndoContext();
66         componentFactory = createComponentFactory();
67         if(trustUids)
68             createConfigurationById(configuration);
69         this.currentRevision = currentRevision;
70         this.trustUids = trustUids;
71     }
72     
73     abstract public StateUndoContextBase createUndoContext();
74     abstract public ComponentFactory<T> createComponentFactory();
75
76     protected void createConfigurationById(T configuration) {
77         THashMap<String, T> configurationByUid = new THashMap<String, T>();
78         browseConfiguration(configurationByUid, configuration);
79         this.configurationByUid = configurationByUid;
80     }
81
82     private void browseConfiguration(
83             THashMap<String, T> configurationByUid,
84             T configuration) {
85         configurationByUid.put(configuration.uid, configuration);
86         for(T child : configuration.getChildren()) {
87             browseConfiguration(configurationByUid, child);
88             child.parent = configuration;
89         }
90     }
91
92     public T detachOrCreateComponent(String uid) {
93         T result = configurationByUid.get(uid);
94         if(result == null) {
95             result = componentFactory.create(uid);
96             configurationByUid.put(uid, result);
97         }
98         else {
99             if(result.getParent() == null)
100                 pendingRemoval.remove(result);
101             else
102                 result.getParent().detachByUid(uid);
103         }
104         return result;
105     }
106
107     /**
108      * Marks that the component should be removed. The actual removal
109      * is delayed until the call of {@link #removePending} so that if the
110      * component is not actually removed but moved or renamed, we don't lose
111      * its state.
112      */
113     public void addPendingRemoval(T component) {
114         pendingRemoval.add(component);
115     }
116
117     public void printUidMap() {
118         printUidMap(new PrintWriter(System.out));
119     }
120
121     public void printUidMap(final PrintWriter out) {
122         out.println("Component tree");
123         out.print("    ");
124         getConfiguration().printConfiguration(out, 1);
125         if(configurationByUid != null) {
126             out.println("UIDs");
127             configurationByUid.forEachEntry(new TObjectObjectProcedure<String, T>() {
128                 @Override
129                 public boolean execute(String a, T b) {
130                     out.println("    " + a + " (" + b.solverComponentName + ", " + b.componentId + ", " + b.uid + ")");
131                     return true;
132                 }
133             });
134         }
135     }
136
137     /**
138      * Removes the component recursively.
139      */
140     public void remove(final Solver solver, T component) {
141         if(configurationByUid != null)
142             configurationByUid.remove(component.uid);
143         if(component.getChildMap() != null)
144             component.getChildMap().forEachValue(new TObjectProcedure<T>() {
145                 @Override
146                 public boolean execute(T child) {
147                     remove(solver, child);
148                     return true;
149                 }
150             });
151         if(component.componentId > 0 && !component.attached)
152             solver.remove(component.componentId);
153     }
154     
155     /**
156      * Saves undo state recursively
157      */
158     public void saveUndoState(final Solver solver, T component) {
159         if(component.getChildMap() != null)
160             component.getChildMap().forEachValue(new TObjectProcedure<T>() {
161                 @Override
162                 public boolean execute(T child) {
163                     saveUndoState(solver, child);
164                     return true;
165                 }
166             });
167         else if(component.componentId > 0 && !component.attached)
168             undoContext.saveState(solver, component.componentId, component.uid);
169     }
170
171     /**
172      * Removes components that are marked pending with {@link #addPendingRemoval} method.
173      */
174     public void removePending(final Solver solver) {
175         pendingRemoval.forEach(new TObjectProcedure<T>() {
176             @Override
177             public boolean execute(T component) {
178                 saveUndoState(solver, component);
179                 return true;
180             }
181         });
182         pendingRemoval.forEach(new TObjectProcedure<T>() {
183             @Override
184             public boolean execute(T component) {
185                 remove(solver, component);
186                 return true;
187             }
188         });
189         pendingRemoval.clear();
190     }
191     
192     /**
193      * Changes the {@link #trustUids} flag.
194      */
195     public void setTrustUids(boolean trustUids) {
196         if(trustUids != this.trustUids) {
197             this.trustUids = trustUids;
198             if(trustUids) {
199                 T configuration = getConfiguration();
200                 if(configuration != null)
201                     createConfigurationById(configuration);
202             }
203         }
204         if(!trustUids)
205             configurationByUid = null;
206     }
207     
208     public boolean getTrustUids() {
209         return trustUids;
210     }
211
212     public void dispose() {
213         if (configurationByUid != null)
214             configurationByUid.clear();
215         pendingRemoval.clear();
216     }
217     
218     public boolean hasPendingRemovals() {
219         return !pendingRemoval.isEmpty();
220     }
221
222 }