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 String parentName = solver.getName(component.getParent().getModuleId());
205 solver.includeSubprocess(parentName, subprocessName);
206 component.solverComponentName = subprocessName;
207 solverComponentNameToComponent.put(subprocessName, component);
210 if(updater.isComposite) {
211 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
212 updater.create(context, properties, connections);
219 component.solverComponentName = nameUtil.ensureNameIsVariationOf(
220 parentSolverComponentName, moduleId,
221 getSubprocessName(name, properties));
223 if(updater.isComposite) {
224 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
225 updater.update(context, properties, connections);
229 if(mapping.getTrustUids()) {
230 // Create a new child map
231 THashMap<String, T> newChildMap =
232 new THashMap<String, T>();
233 for(ChildInfo info : children) {
234 // Detach from the existing configuration the children with
235 // the right uids or create new components if uid is unknown.
236 T conf = mapping.detachOrCreateComponent(info.uid);
237 newChildMap.put(info.name, conf);
238 resolver.markPending(conf);
239 potentiallyUpdatedComponents.add(conf);
242 // Put old children not detached in the previous phase
243 // to the pending removal set. They might have been
244 // moved somewhere else.
245 THashMap<String, T> oldChildMap =
246 component.setChildMapAndReturnOld(newChildMap);
247 resolver.unmarkPending(component);
248 if(oldChildMap != null)
249 for(T component : oldChildMap.values()) {
250 component.clearParent();
251 addPendingRemoval(component);
254 // Alternative implementation when uids are not available.
256 // Create a new child map
257 THashMap<String, T> newChildMap =
258 new THashMap<String, T>();
259 Map<String, T> oldChildMap =
260 component.getChildMap();
261 if(oldChildMap == null)
262 oldChildMap = Collections.<String,T>emptyMap();
263 for(ChildInfo info : children) {
264 T conf = oldChildMap.remove(info.name);
266 conf = componentFactory.create(info.uid);
269 newChildMap.put(info.name, conf);
270 resolver.markPending(conf);
272 component.setChildMap(newChildMap);
274 resolver.unmarkPending(component);
275 if(oldChildMap != null)
276 for(T component : oldChildMap.values()) {
277 component.clearParent();
278 addPendingRemoval(component);
282 postCompositeAction(justCreated, properties, connections, updater);
287 if(!children.isEmpty())
288 throw new SynchronizationException("Component with type " + typeId + " cannot have children.");
290 boolean attached = isAttached(properties);
291 component.attached = attached;
293 // Create or update the component
294 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
295 int moduleId = component.getModuleId();
298 component.attached = true;
299 context.setModuleName(name);
300 context.setModuleId(solver.getId(name));
301 if(context.getModuleId() <= 0)
302 reportProblem("Didn't find attached module " + name + ".");
303 else if(!isDesynchronized(properties))
304 updater.update(context, properties, connections);
308 component.attached = false;
309 context.setModuleName(nameUtil.getFreshName(parentSolverComponentName, name));
310 context.addPostUpdateAction(new Runnable() {
313 context.stateLoadedFromUndo = mapping.undoContext.loadState(solver,
314 context.component.componentId,
315 context.component.uid);
318 updater.create(context, properties, connections);
319 solverComponentNameToComponent.put(context.getModuleName(), component);
322 else if(!isDesynchronized(properties)) {
323 context.setModuleName(nameUtil.ensureNameIsVariationOf(parentSolverComponentName,
325 updater.update(context, properties, connections);
328 resolver.unmarkPending(component);
331 } catch(Throwable e) {
333 throw new SynchronizationException(e);
337 protected void addPendingRemoval(T component) {
339 System.out.println("addPendingRemoval(" + component.componentId + " : " + component.solverComponentName + ")");
340 mapping.addPendingRemoval(component);
343 private String getSubprocessName(String name,
344 Collection<SerializedVariable> properties) {
345 for(SerializedVariable property : properties)
346 if(property.name.equals("HasSubprocessName"))
348 String value = (String)property.value.getValue(Bindings.STRING);
349 if (!value.isEmpty())
351 } catch (AdaptException e) {
352 // This is very improbable exception.
353 // Just ignore it and return the name.
361 public void endComponent() {
364 System.out.println("endComponent(" + (component != null ? component.solverComponentName : "null") + ")");
365 if(component == null) return;
366 for(T child : component.getChildren())
367 if(potentiallyUpdatedComponents.remove(child))
368 resolver.unmarkPending(child);
369 T parent = component.getParent();
370 if (parent == null && mapping.getConfiguration() != component)
371 throw new SynchronizationException("BUG: beginComponent/endComponent calls do not match.");
373 } catch(Throwable e) {
375 throw new SynchronizationException(e);
380 public void beginType(String id, Collection<SerializedVariable> properties) {
383 System.out.println("beginType("+id+")");*/
384 ModuleUpdaterBase<T> updater;
386 updater = moduleUpdaterFactory.createUpdater(id);
387 } catch (Exception e) {
388 throw new RuntimeException(e);
391 throw new SynchronizationException("Failed to create module updater for id " + id + ".");
392 moduleUpdaters.put(id, updater);
393 } catch(Throwable e) {
395 throw new SynchronizationException(e);
400 public void endType() {
402 System.out.println("endType()");*/
405 public boolean getDidChanges() {
409 public void setDidChanges() {
413 public void reportProblem(String description) {
414 getLogger().error(description);
417 public void reportProblem(String description, Exception e) {
418 getLogger().error(description, e);
421 public void addPostSynchronizationAction(Runnable action) {
422 postSynchronizationActions.add(action);
425 protected void postCompositeAction(boolean justCreated, Collection<SerializedVariable> properties,
426 Collection<Connection> connections, ModuleUpdaterBase<T> updater) throws Exception {
430 public long getFromRevision() {
431 return mapping.currentRevision;
434 public abstract Logger getLogger();