1 package org.simantics.structural.synchronization.base;
\r
3 import gnu.trove.map.hash.THashMap;
\r
4 import gnu.trove.set.hash.THashSet;
\r
6 import java.util.ArrayDeque;
\r
7 import java.util.Collection;
\r
8 import java.util.Collections;
\r
9 import java.util.Map;
\r
10 import java.util.Queue;
\r
12 import org.simantics.databoard.Bindings;
\r
13 import org.simantics.databoard.adapter.AdaptException;
\r
14 import org.simantics.db.exception.DatabaseException;
\r
15 import org.simantics.structural.synchronization.internal.Policy;
\r
16 import org.simantics.structural.synchronization.protocol.ChildInfo;
\r
17 import org.simantics.structural.synchronization.protocol.Connection;
\r
18 import org.simantics.structural.synchronization.protocol.SerializedVariable;
\r
19 import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler;
\r
20 import org.simantics.structural.synchronization.protocol.SynchronizationException;
\r
23 * Handles synchronization events by updating the simulator designated by the
\r
24 * provided {@link Solver} instance.
\r
26 * @author Hannu Niemistö
\r
28 public abstract class SynchronizationEventHandlerBase<T extends ComponentBase<T>> implements SynchronizationEventHandler {
\r
30 public static final boolean TRACE_EVENTS = false;
\r
32 public final Solver solver;
\r
33 protected final SolverNameUtil nameUtil;
\r
34 protected final MappingBase<T> mapping;
\r
35 final ModuleUpdaterFactoryBase<T> moduleUpdaterFactory;
\r
36 final ComponentFactory<T> componentFactory;
\r
37 public final ReferenceResolverBase<T> resolver;
\r
38 private boolean didChanges = false;
\r
40 protected T component; // Current active component
\r
41 THashMap<String, ModuleUpdaterBase<T>> moduleUpdaters = new THashMap<>();
\r
42 Queue<Runnable> postSynchronizationActions = new ArrayDeque<>();
\r
43 protected THashMap<String, ComponentBase<T>> solverComponentNameToComponent = new THashMap<>();
\r
46 * This is a set of components satisfying the following conditions
\r
48 * <li>beginComponent is called for their parents
\r
49 * <li>endComponent is not yet called for their parents
\r
50 * <li>beginComponent is not yet called for them
\r
53 THashSet<T> potentiallyUpdatedComponents = new THashSet<>();
\r
55 public SynchronizationEventHandlerBase(Solver solver, ReferenceResolverBase<T> resolver, SolverNameUtil nameUtil,
\r
56 ComponentFactory<T> componentFactory, ModuleUpdaterFactoryBase<T> moduleUpdaterFactory, MappingBase<T> mapping) {
\r
57 this.solver = solver;
\r
58 this.nameUtil = nameUtil;
\r
59 this.mapping = mapping;
\r
60 this.componentFactory = componentFactory;
\r
61 this.moduleUpdaterFactory = moduleUpdaterFactory;
\r
62 this.resolver = resolver;
\r
66 public void beginSynchronization() {
\r
68 System.out.println("beginSynchronization()");
\r
69 //mapping.printUidMap();
\r
75 public void endSynchronization() {
\r
78 System.out.println("endSynchronization()");
\r
79 if(component != null)
\r
80 throw new SynchronizationException("beginComponent/endComponent calls do not match.");
\r
82 resolver.resolvePendingSelfReferences();
\r
83 resolver.printPending();
\r
86 mapping.removePending(solver);
\r
88 // Post synchronization actions
\r
90 while((action = postSynchronizationActions.poll()) != null)
\r
93 // Rename modules to suggested names where possible.
\r
94 nameUtil.applySuggestedNames((creationName, newName) -> {
\r
95 ComponentBase<T> component = solverComponentNameToComponent.get(creationName);
\r
96 if (component != null) {
\r
97 component.solverComponentName = newName;
\r
100 solverComponentNameToComponent.clear();
\r
101 } catch(Throwable e) {
\r
102 Policy.logError(e);
\r
103 throw new SynchronizationException(e);
\r
107 private boolean isAttached(Collection<SerializedVariable> properties) {
\r
108 for(SerializedVariable property : properties)
\r
109 if(property.name.equals("IsAttached"))
\r
111 return (Boolean)property.value.getValue(Bindings.BOOLEAN);
\r
112 } catch (AdaptException e) {
\r
113 throw new SynchronizationException(e);
\r
118 private boolean isDesynchronized(Collection<SerializedVariable> properties) {
\r
119 for(SerializedVariable property : properties)
\r
120 if(property.name.equals("IsDesynchronized"))
\r
122 return (Boolean)property.value.getValue(Bindings.BOOLEAN);
\r
123 } catch (AdaptException e) {
\r
124 throw new SynchronizationException(e);
\r
130 public void beginComponent(String name, String typeId,
\r
131 Collection<SerializedVariable> properties,
\r
132 Collection<Connection> connections,
\r
133 Collection<ChildInfo> children)
\r
134 throws SynchronizationException {
\r
137 System.out.println("beginComponent("+name+", " + (component != null ? component.uid : "null") + "," + typeId + ")");
\r
138 if(!children.isEmpty()) {
\r
139 System.out.println(" Children:");
\r
140 for(ChildInfo child : children)
\r
141 System.out.println(" " + child.name + " " + child.uid);
\r
143 if(!connections.isEmpty()) {
\r
144 System.out.println(" Connections:");
\r
145 for(Connection connection : connections)
\r
146 System.out.println(" " + connection.relation + " " + connection.connectionPoints);
\r
150 String parentSolverComponentName;
\r
152 // Finds the composite
\r
153 if(component == null) {
\r
154 name = "COMP_ROOT";
\r
155 parentSolverComponentName = "";
\r
156 component = mapping.getConfiguration();
\r
157 component.setModuleId(solver.getId(name));
\r
158 component.solverComponentName = name;
\r
161 parentSolverComponentName = component.solverComponentName;
\r
162 component = component.getChild(name);
\r
163 if(component == null)
\r
164 throw new SynchronizationException("Didn't find '"+name+"'. "
\r
165 + "It should have been mentioned as a child in the parent beginComponent method.");
\r
168 potentiallyUpdatedComponents.remove(component);
\r
170 ModuleUpdaterBase<T> updater = null;
\r
171 if(typeId != null) {
\r
172 updater = moduleUpdaters.get(typeId);
\r
173 if(updater == null)
\r
174 throw new SynchronizationException("Undefined typeId " + typeId + ".");
\r
177 // Handle composite
\r
178 if(typeId == null || updater.isUserComponent || updater.isComposite) {
\r
179 // Create or update a subprocess
\r
180 int moduleId = component.getModuleId();
\r
181 boolean justCreated = false;
\r
182 if(isAttached(properties))
\r
183 ; // Subprocesses are not created for attached composites
\r
184 else if(moduleId <= 0) {
\r
185 String subprocessName = nameUtil.getFreshName(
\r
186 parentSolverComponentName,
\r
187 getSubprocessName(name, properties));
\r
189 solver.addSubprocess(subprocessName);
\r
190 } catch(Exception e) {
\r
191 reportProblem("Exception while adding subprocess.", e);
\r
193 moduleId = solver.getId(subprocessName);
\r
195 throw new SynchronizationException("Failed to create a subprocess " + subprocessName);
\r
196 component.setModuleId(moduleId);
\r
198 // TODO these two lines can be removed when IncludedInSimulation -property is given to all composites
\r
199 if(component.getParent() != null) {
\r
200 String parentName = solver.getName(component.getParent().getModuleId());
\r
201 solver.includeSubprocess(parentName, subprocessName);
\r
202 component.solverComponentName = subprocessName;
\r
203 solverComponentNameToComponent.put(subprocessName, component);
\r
206 if(updater.isComposite) {
\r
207 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
\r
208 updater.create(context, properties, connections);
\r
211 justCreated = true;
\r
215 component.solverComponentName = nameUtil.ensureNameIsVariationOf(
\r
216 parentSolverComponentName, moduleId,
\r
217 getSubprocessName(name, properties));
\r
219 if(updater.isComposite) {
\r
220 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
\r
221 updater.update(context, properties, connections);
\r
225 if(mapping.getTrustUids()) {
\r
226 // Create a new child map
\r
227 THashMap<String, T> newChildMap =
\r
228 new THashMap<String, T>();
\r
229 for(ChildInfo info : children) {
\r
230 // Detach from the existing configuration the children with
\r
231 // the right uids or create new components if uid is unknown.
\r
232 T conf = mapping.detachOrCreateComponent(info.uid);
\r
233 newChildMap.put(info.name, conf);
\r
234 resolver.markPending(conf);
\r
235 potentiallyUpdatedComponents.add(conf);
\r
238 // Put old children not detached in the previous phase
\r
239 // to the pending removal set. They might have been
\r
240 // moved somewhere else.
\r
241 THashMap<String, T> oldChildMap =
\r
242 component.setChildMapAndReturnOld(newChildMap);
\r
243 resolver.unmarkPending(component);
\r
244 if(oldChildMap != null)
\r
245 for(T component : oldChildMap.values()) {
\r
246 component.clearParent();
\r
247 mapping.addPendingRemoval(component);
\r
250 // Alternative implementation when uids are not available.
\r
252 // Create a new child map
\r
253 THashMap<String, T> newChildMap =
\r
254 new THashMap<String, T>();
\r
255 Map<String, T> oldChildMap =
\r
256 component.getChildMap();
\r
257 if(oldChildMap == null)
\r
258 oldChildMap = Collections.<String,T>emptyMap();
\r
259 for(ChildInfo info : children) {
\r
260 T conf = oldChildMap.remove(info.name);
\r
262 conf = componentFactory.create(info.uid);
\r
264 conf.uid = info.uid;
\r
265 newChildMap.put(info.name, conf);
\r
266 resolver.markPending(conf);
\r
268 component.setChildMap(newChildMap);
\r
270 resolver.unmarkPending(component);
\r
271 if(oldChildMap != null)
\r
272 for(T component : oldChildMap.values()) {
\r
273 component.clearParent();
\r
274 mapping.addPendingRemoval(component);
\r
278 postCompositeAction(justCreated, properties, connections, updater);
\r
281 // Handle component
\r
283 if(!children.isEmpty())
\r
284 throw new SynchronizationException("Component with type " + typeId + " cannot have children.");
\r
286 boolean attached = isAttached(properties);
\r
287 component.attached = attached;
\r
289 // Create or update the component
\r
290 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
\r
291 int moduleId = component.getModuleId();
\r
292 if(moduleId <= 0) {
\r
294 component.attached = true;
\r
295 context.setModuleName(name);
\r
296 context.setModuleId(solver.getId(name));
\r
297 if(context.getModuleId() <= 0)
\r
298 reportProblem("Didn't find attached module " + name + ".");
\r
299 else if(!isDesynchronized(properties))
\r
300 updater.update(context, properties, connections);
\r
304 component.attached = false;
\r
305 context.setModuleName(nameUtil.getFreshName(parentSolverComponentName, name));
\r
306 context.addPostUpdateAction(new Runnable() {
\r
308 public void run() {
\r
309 context.stateLoadedFromUndo = mapping.undoContext.loadState(solver,
\r
310 context.component.componentId,
\r
311 context.component.uid);
\r
314 updater.create(context, properties, connections);
\r
315 solverComponentNameToComponent.put(context.getModuleName(), component);
\r
318 else if(!isDesynchronized(properties)) {
\r
319 context.setModuleName(nameUtil.ensureNameIsVariationOf(parentSolverComponentName,
\r
321 updater.update(context, properties, connections);
\r
324 resolver.unmarkPending(component);
\r
327 } catch(Throwable e) {
\r
328 Policy.logError(e);
\r
329 throw new SynchronizationException(e);
\r
333 private String getSubprocessName(String name,
\r
334 Collection<SerializedVariable> properties) {
\r
335 for(SerializedVariable property : properties)
\r
336 if(property.name.equals("HasSubprocessName"))
\r
338 String value = (String)property.value.getValue(Bindings.STRING);
\r
339 if (!value.isEmpty())
\r
341 } catch (AdaptException e) {
\r
342 // This is very improbable exception.
\r
343 // Just ignore it and return the name.
\r
344 e.printStackTrace();
\r
351 public void endComponent() {
\r
354 System.out.println("endComponent(" + (component != null ? component.solverComponentName : "null") + ")");
\r
355 if(component == null) return;
\r
356 for(T child : component.getChildren())
\r
357 if(potentiallyUpdatedComponents.remove(child))
\r
358 resolver.unmarkPending(child);
\r
359 T parent = component.getParent();
\r
360 if (parent == null && mapping.getConfiguration() != component)
\r
361 throw new SynchronizationException("BUG: beginComponent/endComponent calls do not match.");
\r
362 component = parent;
\r
363 } catch(Throwable e) {
\r
364 Policy.logError(e);
\r
365 throw new SynchronizationException(e);
\r
370 public void beginType(String id, Collection<SerializedVariable> properties) {
\r
373 System.out.println("beginType("+id+")");*/
\r
374 ModuleUpdaterBase<T> updater;
\r
376 updater = moduleUpdaterFactory.createUpdater(id);
\r
377 } catch (DatabaseException e) {
\r
378 throw new RuntimeException(e);
\r
380 if(updater == null)
\r
381 throw new SynchronizationException("Failed to create module updater for id " + id + ".");
\r
382 moduleUpdaters.put(id, updater);
\r
383 } catch(Throwable e) {
\r
384 Policy.logError(e);
\r
385 throw new SynchronizationException(e);
\r
390 public void endType() {
\r
392 System.out.println("endType()");*/
\r
395 public boolean getDidChanges() {
\r
399 public void setDidChanges() {
\r
403 public void reportProblem(String description) {
\r
404 System.err.println(description);
\r
407 public void reportProblem(String description, Exception e) {
\r
408 System.err.println(description);
\r
409 e.printStackTrace();
\r
412 public void addPostSynchronizationAction(Runnable action) {
\r
413 postSynchronizationActions.add(action);
\r
416 protected void postCompositeAction(boolean justCreated, Collection<SerializedVariable> properties,
\r
417 Collection<Connection> connections, ModuleUpdaterBase<T> updater) throws Exception {
\r
421 public long getFromRevision() {
\r
422 return mapping.currentRevision;
\r