1 package org.simantics.structural.synchronization.base;
3 import java.util.ArrayDeque;
4 import java.util.Collection;
5 import java.util.Collections;
7 import java.util.Queue;
9 import org.simantics.databoard.Bindings;
10 import org.simantics.databoard.adapter.AdaptException;
11 import org.simantics.structural.synchronization.internal.Policy;
12 import org.simantics.structural.synchronization.protocol.ChildInfo;
13 import org.simantics.structural.synchronization.protocol.Connection;
14 import org.simantics.structural.synchronization.protocol.SerializedVariable;
15 import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler;
16 import org.simantics.structural.synchronization.protocol.SynchronizationException;
17 import org.simantics.structural.synchronization.utils.ComponentBase;
18 import org.simantics.structural.synchronization.utils.ComponentFactory;
19 import org.simantics.structural.synchronization.utils.MappingBase;
20 import org.simantics.structural.synchronization.utils.Solver;
21 import org.slf4j.Logger;
23 import gnu.trove.map.hash.THashMap;
24 import gnu.trove.set.hash.THashSet;
27 * Handles synchronization events by updating the simulator designated by the
28 * provided {@link Solver} instance.
30 * @author Hannu Niemistö
32 public abstract class SynchronizationEventHandlerBase<T extends ComponentBase<T>> implements SynchronizationEventHandler {
34 public static final boolean TRACE_EVENTS = false;
36 public final Solver solver;
37 protected final SolverNameUtil nameUtil;
38 protected final MappingBase<T> mapping;
39 final ModuleUpdaterFactoryBase<T> moduleUpdaterFactory;
40 final ComponentFactory<T> componentFactory;
41 public final ReferenceResolverBase<T> resolver;
42 private boolean didChanges = false;
44 protected T component; // Current active component
45 THashMap<String, ModuleUpdaterBase<T>> moduleUpdaters = new THashMap<>();
46 Queue<Runnable> postSynchronizationActions = new ArrayDeque<>();
47 protected THashMap<String, ComponentBase<T>> solverComponentNameToComponent = new THashMap<>();
50 * This is a set of components satisfying the following conditions
52 * <li>beginComponent is called for their parents
53 * <li>endComponent is not yet called for their parents
54 * <li>beginComponent is not yet called for them
57 THashSet<T> potentiallyUpdatedComponents = new THashSet<>();
59 public SynchronizationEventHandlerBase(Solver solver, ReferenceResolverBase<T> resolver, SolverNameUtil nameUtil,
60 ComponentFactory<T> componentFactory, ModuleUpdaterFactoryBase<T> moduleUpdaterFactory, MappingBase<T> mapping) {
62 this.nameUtil = nameUtil;
63 this.mapping = mapping;
64 this.componentFactory = componentFactory;
65 this.moduleUpdaterFactory = moduleUpdaterFactory;
66 this.resolver = resolver;
70 public void beginSynchronization() {
72 System.out.println("beginSynchronization()");
73 //mapping.printUidMap();
79 public void endSynchronization() {
82 System.out.println("endSynchronization()");
84 throw new SynchronizationException("beginComponent/endComponent calls do not match.");
86 resolver.resolvePendingSelfReferences();
87 resolver.printPending();
90 mapping.removePending(solver);
92 // Post synchronization actions
94 while((action = postSynchronizationActions.poll()) != null)
97 // Rename modules to suggested names where possible.
98 nameUtil.applySuggestedNames((creationName, newName) -> {
99 ComponentBase<T> component = solverComponentNameToComponent.get(creationName);
100 if (component != null) {
101 component.solverComponentName = newName;
104 solverComponentNameToComponent.clear();
105 } catch(Throwable e) {
107 throw new SynchronizationException(e);
111 private boolean isAttached(Collection<SerializedVariable> properties) {
112 for(SerializedVariable property : properties)
113 if(property.name.equals("IsAttached"))
115 return (Boolean)property.value.getValue(Bindings.BOOLEAN);
116 } catch (AdaptException e) {
117 throw new SynchronizationException(e);
122 private boolean isDesynchronized(Collection<SerializedVariable> properties) {
123 for(SerializedVariable property : properties)
124 if(property.name.equals("IsDesynchronized"))
126 return (Boolean)property.value.getValue(Bindings.BOOLEAN);
127 } catch (AdaptException e) {
128 throw new SynchronizationException(e);
134 public void beginComponent(String name, String typeId,
135 Collection<SerializedVariable> properties,
136 Collection<Connection> connections,
137 Collection<ChildInfo> children)
138 throws SynchronizationException {
141 System.out.println("beginComponent("+name+", " + (component != null ? component.uid : "null") + "," + typeId + ")");
142 if(!children.isEmpty()) {
143 System.out.println(" Children:");
144 for(ChildInfo child : children)
145 System.out.println(" " + child.name + " " + child.uid);
147 if(!connections.isEmpty()) {
148 System.out.println(" Connections:");
149 for(Connection connection : connections)
150 System.out.println(" " + connection.relation + " " + connection.connectionPoints);
154 String parentSolverComponentName;
156 // Finds the composite
157 if(component == null) {
159 parentSolverComponentName = "";
160 component = mapping.getConfiguration();
161 component.setModuleId(solver.getId(name));
162 component.solverComponentName = name;
165 parentSolverComponentName = component.solverComponentName;
166 component = component.getChild(name);
167 if(component == null)
168 throw new SynchronizationException("Didn't find '"+name+"'. "
169 + "It should have been mentioned as a child in the parent beginComponent method.");
172 potentiallyUpdatedComponents.remove(component);
174 ModuleUpdaterBase<T> updater = null;
176 updater = moduleUpdaters.get(typeId);
178 throw new SynchronizationException("Undefined typeId " + typeId + ".");
182 if(typeId == null || updater.isUserComponent || updater.isComposite) {
183 // Create or update a subprocess
184 int moduleId = component.getModuleId();
185 boolean justCreated = false;
186 if(isAttached(properties))
187 ; // Subprocesses are not created for attached composites
188 else if(moduleId <= 0) {
189 String subprocessName = nameUtil.getFreshName(
190 parentSolverComponentName,
191 getSubprocessName(name, properties));
193 solver.addSubprocess(subprocessName, updater.subprocessType);
194 } catch(Exception e) {
195 reportProblem("Exception while adding subprocess.", e);
197 moduleId = solver.getId(subprocessName);
199 throw new SynchronizationException("Failed to create a subprocess " + subprocessName);
200 component.setModuleId(moduleId);
202 // TODO these two lines can be removed when IncludedInSimulation -property is given to all composites
203 if(component.getParent() != null) {
204 int nearestParentId = getNearestParentComponentId(component);
205 if (nearestParentId <= 0) {
206 throw new SynchronizationException("Could not find parent with non-zero component id from Component("+name+", " + (component != null ? component.uid : "null") + "," + typeId + ")");
208 String parentName = solver.getName(nearestParentId);
209 solver.includeSubprocess(parentName, subprocessName);
210 component.solverComponentName = subprocessName;
211 solverComponentNameToComponent.put(subprocessName, component);
214 if(updater.isComposite) {
215 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
216 updater.create(context, properties, connections);
223 component.solverComponentName = nameUtil.ensureNameIsVariationOf(
224 parentSolverComponentName, moduleId,
225 getSubprocessName(name, properties));
227 if(updater.isComposite) {
228 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
229 updater.update(context, properties, connections);
233 if(mapping.getTrustUids()) {
234 // Create a new child map
235 THashMap<String, T> newChildMap =
236 new THashMap<String, T>();
237 for(ChildInfo info : children) {
238 // Detach from the existing configuration the children with
239 // the right uids or create new components if uid is unknown.
240 T conf = mapping.detachOrCreateComponent(info.uid);
241 newChildMap.put(info.name, conf);
242 resolver.markPending(conf);
243 potentiallyUpdatedComponents.add(conf);
246 // Put old children not detached in the previous phase
247 // to the pending removal set. They might have been
248 // moved somewhere else.
249 THashMap<String, T> oldChildMap =
250 component.setChildMapAndReturnOld(newChildMap);
251 resolver.unmarkPending(component);
252 if(oldChildMap != null)
253 for(T component : oldChildMap.values()) {
254 component.clearParent();
255 addPendingRemoval(component);
258 // Alternative implementation when uids are not available.
260 // Create a new child map
261 THashMap<String, T> newChildMap =
262 new THashMap<String, T>();
263 Map<String, T> oldChildMap =
264 component.getChildMap();
265 if(oldChildMap == null)
266 oldChildMap = Collections.<String,T>emptyMap();
267 for(ChildInfo info : children) {
268 T conf = oldChildMap.remove(info.name);
270 conf = componentFactory.create(info.uid);
273 newChildMap.put(info.name, conf);
274 resolver.markPending(conf);
276 component.setChildMap(newChildMap);
278 resolver.unmarkPending(component);
279 if(oldChildMap != null)
280 for(T component : oldChildMap.values()) {
281 component.clearParent();
282 addPendingRemoval(component);
286 postCompositeAction(justCreated, properties, connections, updater);
291 if(!children.isEmpty())
292 throw new SynchronizationException("Component with type " + typeId + " cannot have children.");
294 boolean attached = isAttached(properties);
295 component.attached = attached;
297 // Create or update the component
298 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
299 int moduleId = component.getModuleId();
302 component.attached = true;
303 context.setModuleName(name);
304 context.setModuleId(solver.getId(name));
305 if(context.getModuleId() <= 0)
306 reportProblem("Didn't find attached module " + name + ".");
307 else if(!isDesynchronized(properties))
308 updater.update(context, properties, connections);
312 component.attached = false;
313 context.setModuleName(nameUtil.getFreshName(parentSolverComponentName, name));
314 context.addPostUpdateAction(new Runnable() {
317 context.stateLoadedFromUndo = mapping.undoContext.loadState(solver,
318 context.component.componentId,
319 context.component.uid);
322 updater.create(context, properties, connections);
323 solverComponentNameToComponent.put(context.getModuleName(), component);
326 else if(!isDesynchronized(properties)) {
327 context.setModuleName(nameUtil.ensureNameIsVariationOf(parentSolverComponentName,
329 updater.update(context, properties, connections);
332 resolver.unmarkPending(component);
335 } catch(Throwable e) {
337 throw new SynchronizationException(e);
341 private static int getNearestParentComponentId(ComponentBase<?> component) {
342 ComponentBase<?> parent = component.getParent();
343 while (parent != null) {
344 int pid = parent.getModuleId();
347 parent = parent.getParent();
352 protected void addPendingRemoval(T component) {
354 System.out.println("addPendingRemoval(" + component.componentId + " : " + component.solverComponentName + ")");
355 mapping.addPendingRemoval(component);
358 private String getSubprocessName(String name,
359 Collection<SerializedVariable> properties) {
360 for(SerializedVariable property : properties)
361 if(property.name.equals("HasSubprocessName"))
363 String value = (String)property.value.getValue(Bindings.STRING);
364 if (!value.isEmpty())
366 } catch (AdaptException e) {
367 // This is very improbable exception.
368 // Just ignore it and return the name.
376 public void endComponent() {
379 System.out.println("endComponent(" + (component != null ? component.solverComponentName : "null") + ")");
380 if(component == null) return;
381 for(T child : component.getChildren())
382 if(potentiallyUpdatedComponents.remove(child))
383 resolver.unmarkPending(child);
384 T parent = component.getParent();
385 if (parent == null && mapping.getConfiguration() != component)
386 throw new SynchronizationException("BUG: beginComponent/endComponent calls do not match.");
388 } catch(Throwable e) {
390 throw new SynchronizationException(e);
395 public void beginType(String id, Collection<SerializedVariable> properties) {
398 System.out.println("beginType("+id+")");*/
399 ModuleUpdaterBase<T> updater;
401 updater = moduleUpdaterFactory.createUpdater(id);
402 } catch (Exception e) {
403 throw new RuntimeException(e);
406 throw new SynchronizationException("Failed to create module updater for id " + id + ".");
407 moduleUpdaters.put(id, updater);
408 } catch(Throwable e) {
410 throw new SynchronizationException(e);
415 public void endType() {
417 System.out.println("endType()");*/
420 public boolean getDidChanges() {
424 public void setDidChanges() {
428 public void reportProblem(String description) {
429 getLogger().error(description);
432 public void reportProblem(String description, Exception e) {
433 getLogger().error(description, e);
436 public void addPostSynchronizationAction(Runnable action) {
437 postSynchronizationActions.add(action);
440 protected void postCompositeAction(boolean justCreated, Collection<SerializedVariable> properties,
441 Collection<Connection> connections, ModuleUpdaterBase<T> updater) throws Exception {
445 public long getFromRevision() {
446 return mapping.currentRevision;
449 public abstract Logger getLogger();