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.db.exception.DatabaseException;
12 import org.simantics.structural.synchronization.internal.Policy;
13 import org.simantics.structural.synchronization.protocol.ChildInfo;
14 import org.simantics.structural.synchronization.protocol.Connection;
15 import org.simantics.structural.synchronization.protocol.SerializedVariable;
16 import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler;
17 import org.simantics.structural.synchronization.protocol.SynchronizationException;
18 import org.slf4j.Logger;
20 import gnu.trove.map.hash.THashMap;
21 import gnu.trove.set.hash.THashSet;
24 * Handles synchronization events by updating the simulator designated by the
25 * provided {@link Solver} instance.
27 * @author Hannu Niemistö
29 public abstract class SynchronizationEventHandlerBase<T extends ComponentBase<T>> implements SynchronizationEventHandler {
31 public static final boolean TRACE_EVENTS = false;
33 public final Solver solver;
34 protected final SolverNameUtil nameUtil;
35 protected final MappingBase<T> mapping;
36 final ModuleUpdaterFactoryBase<T> moduleUpdaterFactory;
37 final ComponentFactory<T> componentFactory;
38 public final ReferenceResolverBase<T> resolver;
39 private boolean didChanges = false;
41 protected T component; // Current active component
42 THashMap<String, ModuleUpdaterBase<T>> moduleUpdaters = new THashMap<>();
43 Queue<Runnable> postSynchronizationActions = new ArrayDeque<>();
44 protected THashMap<String, ComponentBase<T>> solverComponentNameToComponent = new THashMap<>();
47 * This is a set of components satisfying the following conditions
49 * <li>beginComponent is called for their parents
50 * <li>endComponent is not yet called for their parents
51 * <li>beginComponent is not yet called for them
54 THashSet<T> potentiallyUpdatedComponents = new THashSet<>();
56 public SynchronizationEventHandlerBase(Solver solver, ReferenceResolverBase<T> resolver, SolverNameUtil nameUtil,
57 ComponentFactory<T> componentFactory, ModuleUpdaterFactoryBase<T> moduleUpdaterFactory, MappingBase<T> mapping) {
59 this.nameUtil = nameUtil;
60 this.mapping = mapping;
61 this.componentFactory = componentFactory;
62 this.moduleUpdaterFactory = moduleUpdaterFactory;
63 this.resolver = resolver;
67 public void beginSynchronization() {
69 System.out.println("beginSynchronization()");
70 //mapping.printUidMap();
76 public void endSynchronization() {
79 System.out.println("endSynchronization()");
81 throw new SynchronizationException("beginComponent/endComponent calls do not match.");
83 resolver.resolvePendingSelfReferences();
84 resolver.printPending();
87 mapping.removePending(solver);
89 // Post synchronization actions
91 while((action = postSynchronizationActions.poll()) != null)
94 // Rename modules to suggested names where possible.
95 nameUtil.applySuggestedNames((creationName, newName) -> {
96 ComponentBase<T> component = solverComponentNameToComponent.get(creationName);
97 if (component != null) {
98 component.solverComponentName = newName;
101 solverComponentNameToComponent.clear();
102 } catch(Throwable e) {
104 throw new SynchronizationException(e);
108 private boolean isAttached(Collection<SerializedVariable> properties) {
109 for(SerializedVariable property : properties)
110 if(property.name.equals("IsAttached"))
112 return (Boolean)property.value.getValue(Bindings.BOOLEAN);
113 } catch (AdaptException e) {
114 throw new SynchronizationException(e);
119 private boolean isDesynchronized(Collection<SerializedVariable> properties) {
120 for(SerializedVariable property : properties)
121 if(property.name.equals("IsDesynchronized"))
123 return (Boolean)property.value.getValue(Bindings.BOOLEAN);
124 } catch (AdaptException e) {
125 throw new SynchronizationException(e);
131 public void beginComponent(String name, String typeId,
132 Collection<SerializedVariable> properties,
133 Collection<Connection> connections,
134 Collection<ChildInfo> children)
135 throws SynchronizationException {
138 System.out.println("beginComponent("+name+", " + (component != null ? component.uid : "null") + "," + typeId + ")");
139 if(!children.isEmpty()) {
140 System.out.println(" Children:");
141 for(ChildInfo child : children)
142 System.out.println(" " + child.name + " " + child.uid);
144 if(!connections.isEmpty()) {
145 System.out.println(" Connections:");
146 for(Connection connection : connections)
147 System.out.println(" " + connection.relation + " " + connection.connectionPoints);
151 String parentSolverComponentName;
153 // Finds the composite
154 if(component == null) {
156 parentSolverComponentName = "";
157 component = mapping.getConfiguration();
158 component.setModuleId(solver.getId(name));
159 component.solverComponentName = name;
162 parentSolverComponentName = component.solverComponentName;
163 component = component.getChild(name);
164 if(component == null)
165 throw new SynchronizationException("Didn't find '"+name+"'. "
166 + "It should have been mentioned as a child in the parent beginComponent method.");
169 potentiallyUpdatedComponents.remove(component);
171 ModuleUpdaterBase<T> updater = null;
173 updater = moduleUpdaters.get(typeId);
175 throw new SynchronizationException("Undefined typeId " + typeId + ".");
179 if(typeId == null || updater.isUserComponent || updater.isComposite) {
180 // Create or update a subprocess
181 int moduleId = component.getModuleId();
182 boolean justCreated = false;
183 if(isAttached(properties))
184 ; // Subprocesses are not created for attached composites
185 else if(moduleId <= 0) {
186 String subprocessName = nameUtil.getFreshName(
187 parentSolverComponentName,
188 getSubprocessName(name, properties));
190 solver.addSubprocess(subprocessName, updater.subprocessType);
191 } catch(Exception e) {
192 reportProblem("Exception while adding subprocess.", e);
194 moduleId = solver.getId(subprocessName);
196 throw new SynchronizationException("Failed to create a subprocess " + subprocessName);
197 component.setModuleId(moduleId);
199 // TODO these two lines can be removed when IncludedInSimulation -property is given to all composites
200 if(component.getParent() != null) {
201 String parentName = solver.getName(component.getParent().getModuleId());
202 solver.includeSubprocess(parentName, subprocessName);
203 component.solverComponentName = subprocessName;
204 solverComponentNameToComponent.put(subprocessName, component);
207 if(updater.isComposite) {
208 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
209 updater.create(context, properties, connections);
216 component.solverComponentName = nameUtil.ensureNameIsVariationOf(
217 parentSolverComponentName, moduleId,
218 getSubprocessName(name, properties));
220 if(updater.isComposite) {
221 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
222 updater.update(context, properties, connections);
226 if(mapping.getTrustUids()) {
227 // Create a new child map
228 THashMap<String, T> newChildMap =
229 new THashMap<String, T>();
230 for(ChildInfo info : children) {
231 // Detach from the existing configuration the children with
232 // the right uids or create new components if uid is unknown.
233 T conf = mapping.detachOrCreateComponent(info.uid);
234 newChildMap.put(info.name, conf);
235 resolver.markPending(conf);
236 potentiallyUpdatedComponents.add(conf);
239 // Put old children not detached in the previous phase
240 // to the pending removal set. They might have been
241 // moved somewhere else.
242 THashMap<String, T> oldChildMap =
243 component.setChildMapAndReturnOld(newChildMap);
244 resolver.unmarkPending(component);
245 if(oldChildMap != null)
246 for(T component : oldChildMap.values()) {
247 component.clearParent();
248 mapping.addPendingRemoval(component);
251 // Alternative implementation when uids are not available.
253 // Create a new child map
254 THashMap<String, T> newChildMap =
255 new THashMap<String, T>();
256 Map<String, T> oldChildMap =
257 component.getChildMap();
258 if(oldChildMap == null)
259 oldChildMap = Collections.<String,T>emptyMap();
260 for(ChildInfo info : children) {
261 T conf = oldChildMap.remove(info.name);
263 conf = componentFactory.create(info.uid);
266 newChildMap.put(info.name, conf);
267 resolver.markPending(conf);
269 component.setChildMap(newChildMap);
271 resolver.unmarkPending(component);
272 if(oldChildMap != null)
273 for(T component : oldChildMap.values()) {
274 component.clearParent();
275 mapping.addPendingRemoval(component);
279 postCompositeAction(justCreated, properties, connections, updater);
284 if(!children.isEmpty())
285 throw new SynchronizationException("Component with type " + typeId + " cannot have children.");
287 boolean attached = isAttached(properties);
288 component.attached = attached;
290 // Create or update the component
291 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
292 int moduleId = component.getModuleId();
295 component.attached = true;
296 context.setModuleName(name);
297 context.setModuleId(solver.getId(name));
298 if(context.getModuleId() <= 0)
299 reportProblem("Didn't find attached module " + name + ".");
300 else if(!isDesynchronized(properties))
301 updater.update(context, properties, connections);
305 component.attached = false;
306 context.setModuleName(nameUtil.getFreshName(parentSolverComponentName, name));
307 context.addPostUpdateAction(new Runnable() {
310 context.stateLoadedFromUndo = mapping.undoContext.loadState(solver,
311 context.component.componentId,
312 context.component.uid);
315 updater.create(context, properties, connections);
316 solverComponentNameToComponent.put(context.getModuleName(), component);
319 else if(!isDesynchronized(properties)) {
320 context.setModuleName(nameUtil.ensureNameIsVariationOf(parentSolverComponentName,
322 updater.update(context, properties, connections);
325 resolver.unmarkPending(component);
328 } catch(Throwable e) {
330 throw new SynchronizationException(e);
334 private String getSubprocessName(String name,
335 Collection<SerializedVariable> properties) {
336 for(SerializedVariable property : properties)
337 if(property.name.equals("HasSubprocessName"))
339 String value = (String)property.value.getValue(Bindings.STRING);
340 if (!value.isEmpty())
342 } catch (AdaptException e) {
343 // This is very improbable exception.
344 // Just ignore it and return the name.
352 public void endComponent() {
355 System.out.println("endComponent(" + (component != null ? component.solverComponentName : "null") + ")");
356 if(component == null) return;
357 for(T child : component.getChildren())
358 if(potentiallyUpdatedComponents.remove(child))
359 resolver.unmarkPending(child);
360 T parent = component.getParent();
361 if (parent == null && mapping.getConfiguration() != component)
362 throw new SynchronizationException("BUG: beginComponent/endComponent calls do not match.");
364 } catch(Throwable e) {
366 throw new SynchronizationException(e);
371 public void beginType(String id, Collection<SerializedVariable> properties) {
374 System.out.println("beginType("+id+")");*/
375 ModuleUpdaterBase<T> updater;
377 updater = moduleUpdaterFactory.createUpdater(id);
378 } catch (DatabaseException e) {
379 throw new RuntimeException(e);
382 throw new SynchronizationException("Failed to create module updater for id " + id + ".");
383 moduleUpdaters.put(id, updater);
384 } catch(Throwable e) {
386 throw new SynchronizationException(e);
391 public void endType() {
393 System.out.println("endType()");*/
396 public boolean getDidChanges() {
400 public void setDidChanges() {
404 public void reportProblem(String description) {
405 getLogger().error(description);
408 public void reportProblem(String description, Exception e) {
409 getLogger().error(description, e);
412 public void addPostSynchronizationAction(Runnable action) {
413 postSynchronizationActions.add(action);
416 protected void postCompositeAction(boolean justCreated, Collection<SerializedVariable> properties,
417 Collection<Connection> connections, ModuleUpdaterBase<T> updater) throws Exception {
421 public long getFromRevision() {
422 return mapping.currentRevision;
425 public abstract Logger getLogger();