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.simantics.structural.synchronization.utils.ComponentBase;
19 import org.simantics.structural.synchronization.utils.ComponentFactory;
20 import org.simantics.structural.synchronization.utils.MappingBase;
21 import org.simantics.structural.synchronization.utils.Solver;
22 import org.slf4j.Logger;
24 import gnu.trove.map.hash.THashMap;
25 import gnu.trove.set.hash.THashSet;
28 * Handles synchronization events by updating the simulator designated by the
29 * provided {@link Solver} instance.
31 * @author Hannu Niemistö
33 public abstract class SynchronizationEventHandlerBase<T extends ComponentBase<T>> implements SynchronizationEventHandler {
35 public static final boolean TRACE_EVENTS = false;
37 public final Solver solver;
38 protected final SolverNameUtil nameUtil;
39 protected final MappingBase<T> mapping;
40 final ModuleUpdaterFactoryBase<T> moduleUpdaterFactory;
41 final ComponentFactory<T> componentFactory;
42 public final ReferenceResolverBase<T> resolver;
43 private boolean didChanges = false;
45 protected T component; // Current active component
46 THashMap<String, ModuleUpdaterBase<T>> moduleUpdaters = new THashMap<>();
47 Queue<Runnable> postSynchronizationActions = new ArrayDeque<>();
48 protected THashMap<String, ComponentBase<T>> solverComponentNameToComponent = new THashMap<>();
51 * This is a set of components satisfying the following conditions
53 * <li>beginComponent is called for their parents
54 * <li>endComponent is not yet called for their parents
55 * <li>beginComponent is not yet called for them
58 THashSet<T> potentiallyUpdatedComponents = new THashSet<>();
60 public SynchronizationEventHandlerBase(Solver solver, ReferenceResolverBase<T> resolver, SolverNameUtil nameUtil,
61 ComponentFactory<T> componentFactory, ModuleUpdaterFactoryBase<T> moduleUpdaterFactory, MappingBase<T> mapping) {
63 this.nameUtil = nameUtil;
64 this.mapping = mapping;
65 this.componentFactory = componentFactory;
66 this.moduleUpdaterFactory = moduleUpdaterFactory;
67 this.resolver = resolver;
71 public void beginSynchronization() {
73 System.out.println("beginSynchronization()");
74 //mapping.printUidMap();
80 public void endSynchronization() {
83 System.out.println("endSynchronization()");
85 throw new SynchronizationException("beginComponent/endComponent calls do not match.");
87 resolver.resolvePendingSelfReferences();
88 resolver.printPending();
91 mapping.removePending(solver);
93 // Post synchronization actions
95 while((action = postSynchronizationActions.poll()) != null)
98 // Rename modules to suggested names where possible.
99 nameUtil.applySuggestedNames((creationName, newName) -> {
100 ComponentBase<T> component = solverComponentNameToComponent.get(creationName);
101 if (component != null) {
102 component.solverComponentName = newName;
105 solverComponentNameToComponent.clear();
106 } catch(Throwable e) {
108 throw new SynchronizationException(e);
112 private boolean isAttached(Collection<SerializedVariable> properties) {
113 for(SerializedVariable property : properties)
114 if(property.name.equals("IsAttached"))
116 return (Boolean)property.value.getValue(Bindings.BOOLEAN);
117 } catch (AdaptException e) {
118 throw new SynchronizationException(e);
123 private boolean isDesynchronized(Collection<SerializedVariable> properties) {
124 for(SerializedVariable property : properties)
125 if(property.name.equals("IsDesynchronized"))
127 return (Boolean)property.value.getValue(Bindings.BOOLEAN);
128 } catch (AdaptException e) {
129 throw new SynchronizationException(e);
135 public void beginComponent(String name, String typeId,
136 Collection<SerializedVariable> properties,
137 Collection<Connection> connections,
138 Collection<ChildInfo> children)
139 throws SynchronizationException {
142 System.out.println("beginComponent("+name+", " + (component != null ? component.uid : "null") + "," + typeId + ")");
143 if(!children.isEmpty()) {
144 System.out.println(" Children:");
145 for(ChildInfo child : children)
146 System.out.println(" " + child.name + " " + child.uid);
148 if(!connections.isEmpty()) {
149 System.out.println(" Connections:");
150 for(Connection connection : connections)
151 System.out.println(" " + connection.relation + " " + connection.connectionPoints);
155 String parentSolverComponentName;
157 // Finds the composite
158 if(component == null) {
160 parentSolverComponentName = "";
161 component = mapping.getConfiguration();
162 component.setModuleId(solver.getId(name));
163 component.solverComponentName = name;
166 parentSolverComponentName = component.solverComponentName;
167 component = component.getChild(name);
168 if(component == null)
169 throw new SynchronizationException("Didn't find '"+name+"'. "
170 + "It should have been mentioned as a child in the parent beginComponent method.");
173 potentiallyUpdatedComponents.remove(component);
175 ModuleUpdaterBase<T> updater = null;
177 updater = moduleUpdaters.get(typeId);
179 throw new SynchronizationException("Undefined typeId " + typeId + ".");
183 if(typeId == null || updater.isUserComponent || updater.isComposite) {
184 // Create or update a subprocess
185 int moduleId = component.getModuleId();
186 boolean justCreated = false;
187 if(isAttached(properties))
188 ; // Subprocesses are not created for attached composites
189 else if(moduleId <= 0) {
190 String subprocessName = nameUtil.getFreshName(
191 parentSolverComponentName,
192 getSubprocessName(name, properties));
194 solver.addSubprocess(subprocessName, updater.subprocessType);
195 } catch(Exception e) {
196 reportProblem("Exception while adding subprocess.", e);
198 moduleId = solver.getId(subprocessName);
200 throw new SynchronizationException("Failed to create a subprocess " + subprocessName);
201 component.setModuleId(moduleId);
203 // TODO these two lines can be removed when IncludedInSimulation -property is given to all composites
204 if(component.getParent() != null) {
205 String parentName = solver.getName(component.getParent().getModuleId());
206 solver.includeSubprocess(parentName, subprocessName);
207 component.solverComponentName = subprocessName;
208 solverComponentNameToComponent.put(subprocessName, component);
211 if(updater.isComposite) {
212 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
213 updater.create(context, properties, connections);
220 component.solverComponentName = nameUtil.ensureNameIsVariationOf(
221 parentSolverComponentName, moduleId,
222 getSubprocessName(name, properties));
224 if(updater.isComposite) {
225 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
226 updater.update(context, properties, connections);
230 if(mapping.getTrustUids()) {
231 // Create a new child map
232 THashMap<String, T> newChildMap =
233 new THashMap<String, T>();
234 for(ChildInfo info : children) {
235 // Detach from the existing configuration the children with
236 // the right uids or create new components if uid is unknown.
237 T conf = mapping.detachOrCreateComponent(info.uid);
238 newChildMap.put(info.name, conf);
239 resolver.markPending(conf);
240 potentiallyUpdatedComponents.add(conf);
243 // Put old children not detached in the previous phase
244 // to the pending removal set. They might have been
245 // moved somewhere else.
246 THashMap<String, T> oldChildMap =
247 component.setChildMapAndReturnOld(newChildMap);
248 resolver.unmarkPending(component);
249 if(oldChildMap != null)
250 for(T component : oldChildMap.values()) {
251 component.clearParent();
252 mapping.addPendingRemoval(component);
255 // Alternative implementation when uids are not available.
257 // Create a new child map
258 THashMap<String, T> newChildMap =
259 new THashMap<String, T>();
260 Map<String, T> oldChildMap =
261 component.getChildMap();
262 if(oldChildMap == null)
263 oldChildMap = Collections.<String,T>emptyMap();
264 for(ChildInfo info : children) {
265 T conf = oldChildMap.remove(info.name);
267 conf = componentFactory.create(info.uid);
270 newChildMap.put(info.name, conf);
271 resolver.markPending(conf);
273 component.setChildMap(newChildMap);
275 resolver.unmarkPending(component);
276 if(oldChildMap != null)
277 for(T component : oldChildMap.values()) {
278 component.clearParent();
279 mapping.addPendingRemoval(component);
283 postCompositeAction(justCreated, properties, connections, updater);
288 if(!children.isEmpty())
289 throw new SynchronizationException("Component with type " + typeId + " cannot have children.");
291 boolean attached = isAttached(properties);
292 component.attached = attached;
294 // Create or update the component
295 final ModuleUpdateContext<T> context = new ModuleUpdateContext<T>(this, updater, component);
296 int moduleId = component.getModuleId();
299 component.attached = true;
300 context.setModuleName(name);
301 context.setModuleId(solver.getId(name));
302 if(context.getModuleId() <= 0)
303 reportProblem("Didn't find attached module " + name + ".");
304 else if(!isDesynchronized(properties))
305 updater.update(context, properties, connections);
309 component.attached = false;
310 context.setModuleName(nameUtil.getFreshName(parentSolverComponentName, name));
311 context.addPostUpdateAction(new Runnable() {
314 context.stateLoadedFromUndo = mapping.undoContext.loadState(solver,
315 context.component.componentId,
316 context.component.uid);
319 updater.create(context, properties, connections);
320 solverComponentNameToComponent.put(context.getModuleName(), component);
323 else if(!isDesynchronized(properties)) {
324 context.setModuleName(nameUtil.ensureNameIsVariationOf(parentSolverComponentName,
326 updater.update(context, properties, connections);
329 resolver.unmarkPending(component);
332 } catch(Throwable e) {
334 throw new SynchronizationException(e);
338 private String getSubprocessName(String name,
339 Collection<SerializedVariable> properties) {
340 for(SerializedVariable property : properties)
341 if(property.name.equals("HasSubprocessName"))
343 String value = (String)property.value.getValue(Bindings.STRING);
344 if (!value.isEmpty())
346 } catch (AdaptException e) {
347 // This is very improbable exception.
348 // Just ignore it and return the name.
356 public void endComponent() {
359 System.out.println("endComponent(" + (component != null ? component.solverComponentName : "null") + ")");
360 if(component == null) return;
361 for(T child : component.getChildren())
362 if(potentiallyUpdatedComponents.remove(child))
363 resolver.unmarkPending(child);
364 T parent = component.getParent();
365 if (parent == null && mapping.getConfiguration() != component)
366 throw new SynchronizationException("BUG: beginComponent/endComponent calls do not match.");
368 } catch(Throwable e) {
370 throw new SynchronizationException(e);
375 public void beginType(String id, Collection<SerializedVariable> properties) {
378 System.out.println("beginType("+id+")");*/
379 ModuleUpdaterBase<T> updater;
381 updater = moduleUpdaterFactory.createUpdater(id);
382 } catch (DatabaseException e) {
383 throw new RuntimeException(e);
386 throw new SynchronizationException("Failed to create module updater for id " + id + ".");
387 moduleUpdaters.put(id, updater);
388 } catch(Throwable e) {
390 throw new SynchronizationException(e);
395 public void endType() {
397 System.out.println("endType()");*/
400 public boolean getDidChanges() {
404 public void setDidChanges() {
408 public void reportProblem(String description) {
409 getLogger().error(description);
412 public void reportProblem(String description, Exception e) {
413 getLogger().error(description, e);
416 public void addPostSynchronizationAction(Runnable action) {
417 postSynchronizationActions.add(action);
420 protected void postCompositeAction(boolean justCreated, Collection<SerializedVariable> properties,
421 Collection<Connection> connections, ModuleUpdaterBase<T> updater) throws Exception {
425 public long getFromRevision() {
426 return mapping.currentRevision;
429 public abstract Logger getLogger();