1 /*******************************************************************************
2 * Copyright (c) 2007, 2012 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.modeling.typicals;
14 import java.awt.geom.Point2D;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Date;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
26 import org.eclipse.core.runtime.IProgressMonitor;
27 import org.simantics.Simantics;
28 import org.simantics.databoard.Bindings;
29 import org.simantics.db.ReadGraph;
30 import org.simantics.db.Resource;
31 import org.simantics.db.Statement;
32 import org.simantics.db.WriteGraph;
33 import org.simantics.db.common.CommentMetadata;
34 import org.simantics.db.common.NamedResource;
35 import org.simantics.db.common.primitiverequest.Adapter;
36 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
37 import org.simantics.db.common.request.ObjectsWithType;
38 import org.simantics.db.common.request.PossibleIndexRoot;
39 import org.simantics.db.common.request.WriteRequest;
40 import org.simantics.db.common.uri.UnescapedChildMapOfResource;
41 import org.simantics.db.common.utils.CommonDBUtils;
42 import org.simantics.db.common.utils.NameUtils;
43 import org.simantics.db.exception.DatabaseException;
44 import org.simantics.db.layer0.adapter.Instances;
45 import org.simantics.db.layer0.request.ActiveModels;
46 import org.simantics.db.layer0.util.RemoverUtil;
47 import org.simantics.diagram.content.ConnectionUtil;
48 import org.simantics.diagram.handler.CopyPasteStrategy;
49 import org.simantics.diagram.handler.ElementObjectAssortment;
50 import org.simantics.diagram.handler.PasteOperation;
51 import org.simantics.diagram.handler.Paster;
52 import org.simantics.diagram.handler.Paster.RouteLine;
53 import org.simantics.diagram.stubs.DiagramResource;
54 import org.simantics.diagram.synchronization.CollectingModificationQueue;
55 import org.simantics.diagram.synchronization.CopyAdvisor;
56 import org.simantics.diagram.synchronization.SynchronizationHints;
57 import org.simantics.diagram.synchronization.graph.GraphSynchronizationContext;
58 import org.simantics.diagram.ui.DiagramModelHints;
59 import org.simantics.document.DocumentResource;
60 import org.simantics.g2d.canvas.ICanvasContext;
61 import org.simantics.g2d.diagram.DiagramClass;
62 import org.simantics.g2d.diagram.IDiagram;
63 import org.simantics.g2d.diagram.impl.Diagram;
64 import org.simantics.layer0.Layer0;
65 import org.simantics.modeling.ModelingResources;
66 import org.simantics.modeling.ModelingUtils;
67 import org.simantics.modeling.mapping.ModelingSynchronizationHints;
68 import org.simantics.modeling.typicals.rules.AuxKeys;
69 import org.simantics.modeling.typicals.rules.FlagRule;
70 import org.simantics.modeling.typicals.rules.InstanceOfRule;
71 import org.simantics.modeling.typicals.rules.LabelRule;
72 import org.simantics.modeling.typicals.rules.MonitorRule;
73 import org.simantics.modeling.typicals.rules.NameRule;
74 import org.simantics.modeling.typicals.rules.ProfileMonitorRule;
75 import org.simantics.modeling.typicals.rules.SVGElementRule;
76 import org.simantics.modeling.typicals.rules.TransformRule;
77 import org.simantics.scenegraph.g2d.events.command.Commands;
78 import org.simantics.scl.runtime.function.Function4;
79 import org.simantics.structural.stubs.StructuralResource2;
80 import org.simantics.utils.datastructures.MapSet;
81 import org.simantics.utils.strings.AlphanumComparator;
82 import org.simantics.utils.strings.EString;
83 import org.simantics.utils.ui.ErrorLogger;
84 import org.slf4j.Logger;
85 import org.slf4j.LoggerFactory;
87 import gnu.trove.map.hash.THashMap;
88 import gnu.trove.set.hash.THashSet;
91 * A write request that synchronizes typical master templates and their
92 * instances as specified.
95 * Use {@link #SyncTypicalTemplatesToInstances(Resource[], MapSet)} to
96 * synchronize all instances of specified templates. Use
97 * {@link #syncSingleInstance(Resource)} to synchronize a single typical
98 * instance with its master template.
100 * @author Tuukka Lehtonen
102 * @see ReadTypicalInfo
104 * @see TypicalSynchronizationMetadata
106 public class SyncTypicalTemplatesToInstances extends WriteRequest {
107 private static final Logger LOGGER = LoggerFactory.getLogger(SyncTypicalTemplatesToInstances.class);
110 * A constant used as the second argument to
111 * {@link #SyncTemplates(Resource[], MapSet)} for stating that all specified
112 * templates should be fully synchronized to their instances.
114 * This is useful for forcing complete synchronization and unit testing.
116 public static final EmptyMapSet ALL = EmptyMapSet.INSTANCE;
118 public static class EmptyMapSet extends MapSet<Resource, Resource> {
120 public static final EmptyMapSet INSTANCE = new EmptyMapSet();
122 public EmptyMapSet() {
123 this.sets = Collections.emptyMap();
127 protected Set<Resource> getOrCreateSet(Resource key) {
128 throw new UnsupportedOperationException("immutable constant instance");
133 protected static final boolean DEBUG = false;
137 final private IProgressMonitor monitor;
139 * Typical diagram rules to apply
141 protected Set<Resource> selectedRules;
144 * Typical diagram templates to synchronize with their instances.
146 protected Resource[] templates;
149 * Typical diagram instances to synchronize with their templates.
151 protected Resource[] instances;
154 * For each template diagram in {@link #templates}, shall contain a set of
155 * elements that have changed and should be synchronized into the instance
156 * diagrams. Provided as an argument by the client. Allows optimizing
157 * real-time synchronization by not processing everything all the time.
159 * If the value is {@link #ALL}, all elements of the template shall be fully
162 protected MapSet<Resource, Resource> changedElementsByDiagram;
167 protected StructuralResource2 STR;
168 protected DiagramResource DIA;
169 protected ModelingResources MOD;
172 * Needed for using {@link Paster} in
173 * {@link #addMissingElements(WriteGraph, TypicalInfo, Resource, Resource, Set)}
175 protected GraphSynchronizationContext syncCtx;
178 * For collecting commit metadata during the processing of this request.
180 protected TypicalSynchronizationMetadata metadata;
183 * Necessary for using {@link CopyPasteStrategy} and {@link PasteOperation}
184 * for now. Will be removed in the future once IDiagram is removed from
187 protected IDiagram temporaryDiagram;
189 protected ConnectionUtil cu;
192 * Maps source -> target connection route nodes, i.e. connectors and
193 * interior route nodes (route lines). Inverse mapping of {@link #t2s}.
195 protected Map<Resource, Resource> s2t;
198 * Maps target -> source connection route nodes, i.e. connectors and
199 * interior route nodes (route lines). Inverse mapping of {@link #s2t}.
201 protected Map<Resource, Resource> t2s;
204 * An auxiliary resource map for extracting the correspondences between
205 * originals and copied resource when diagram contents are copied from
206 * template to instance.
208 protected Map<Object, Object> copyMap;
210 final private Map<Resource, List<String>> messageLogs = new HashMap<>();
212 public List<Resource> logs = new ArrayList<>();
214 private boolean writeLog;
220 * @param selectedRules
223 * @throws DatabaseException
225 public static void syncTypicals(WriteGraph graph, boolean log, List<Resource> templates, List<Resource> instances) throws DatabaseException {
227 new SyncTypicalTemplatesToInstances(
229 templates.toArray(Resource.NONE),
230 instances.toArray(Resource.NONE),
237 * @param templates typical diagram templates to completely synchronize with
240 public SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource... templates) {
241 this(selectedRules, templates, null, ALL, null);
245 * @param templates typical diagram templates to partially synchronize with
247 * @param changedElementsByDiagram see {@link #changedElementsByDiagram}
249 public SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource[] templates, MapSet<Resource, Resource> changedElementsByDiagram) {
250 this(selectedRules, templates, null, changedElementsByDiagram, null);
254 * Return a write request that completely synchronizes the specified
255 * instance diagram with its template.
260 public static SyncTypicalTemplatesToInstances syncSingleInstance(Set<Resource> selectedRules, Resource instance) {
261 return new SyncTypicalTemplatesToInstances(selectedRules, null, new Resource[] { instance }, ALL, null);
265 * @param templates typical diagram templates to synchronize with their instances
266 * @param instances typical diagram instances to synchronize with their templates
267 * @param changedElementsByDiagram see {@link #changedElementsByDiagram}
269 private SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource[] templates, Resource[] instances, MapSet<Resource, Resource> changedElementsByDiagram, IProgressMonitor monitor) {
270 this.selectedRules = selectedRules;
271 this.templates = templates;
272 this.instances = instances;
273 this.changedElementsByDiagram = changedElementsByDiagram;
274 this.monitor = monitor;
277 public SyncTypicalTemplatesToInstances logging(boolean writeLog) {
278 this.writeLog = writeLog;
282 private Resource getDiagramNameResource(ReadGraph graph, Resource diagram) throws DatabaseException {
283 Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
284 if(composite != null) return composite;
288 private Resource getElementNameResource(ReadGraph graph, Resource element) throws DatabaseException {
289 Resource corr = ModelingUtils.getPossibleElementCorrespondendence(graph, element);
290 if(corr != null) return corr;
294 private List<String> getLog(ReadGraph graph, Resource diagram) throws DatabaseException {
295 Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(diagram));
296 if(indexRoot == null) throw new DatabaseException("FATAL: Diagram is not under any index root.");
297 List<String> log = messageLogs.get(indexRoot);
299 log = new ArrayList<>();
300 messageLogs.put(indexRoot, log);
305 private String elementName(ReadGraph graph, Resource element) throws DatabaseException {
307 StringBuilder b = new StringBuilder();
308 b.append(safeNameAndType(graph, element));
310 int spaces = 60-b.length();
311 for(int i=0;i<spaces;i++) b.append(" ");
313 Resource corr = ModelingUtils.getPossibleElementCorrespondendence(graph, element);
315 b.append(safeNameAndType(graph, corr));
325 public void perform(WriteGraph graph) throws DatabaseException {
326 this.L0 = Layer0.getInstance(graph);
327 this.STR = StructuralResource2.getInstance(graph);
328 this.DIA = DiagramResource.getInstance(graph);
329 this.MOD = ModelingResources.getInstance(graph);
331 this.syncCtx = GraphSynchronizationContext.getWriteInstance( graph, new CollectingModificationQueue() );
332 this.syncCtx.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph));
334 this.metadata = new TypicalSynchronizationMetadata();
335 this.metadata.synchronizedTypicals = new ArrayList<>();
337 this.temporaryDiagram = Diagram.spawnNew(DiagramClass.DEFAULT);
338 this.temporaryDiagram.setHint(SynchronizationHints.CONTEXT, syncCtx);
340 this.cu = new ConnectionUtil(graph);
342 if (templates != null) {
343 // Look for typical template instances from the currently active models only.
344 Collection<Resource> activeModels = graph.syncRequest(new ActiveModels(Simantics.getProjectResource()));
345 if (!activeModels.isEmpty()) {
346 for (Resource template : templates) {
347 syncTemplate(graph, template, activeModels);
351 if (instances != null) {
352 for (Resource instance : instances) {
353 syncInstance(graph, instance);
358 for(Map.Entry<Resource, List<String>> entry : messageLogs.entrySet()) {
360 Resource indexRoot = entry.getKey();
361 List<String> messageLog = entry.getValue();
363 Layer0 L0 = Layer0.getInstance(graph);
364 DocumentResource DOC = DocumentResource.getInstance(graph);
366 Collection<Resource> libs = graph.syncRequest(new ObjectsWithType(indexRoot, L0.ConsistsOf, DOC.DocumentLibrary));
367 if(libs.isEmpty()) continue;
369 List<NamedResource> nrs = new ArrayList<>();
370 for(Resource lib : libs) nrs.add(new NamedResource(NameUtils.getSafeName(graph, lib), lib));
371 Collections.sort(nrs, AlphanumComparator.CASE_INSENSITIVE_COMPARATOR);
372 Resource library = nrs.iterator().next().getResource();
374 CommonDBUtils.selectClusterSet(graph, library);
376 String text = "--- Created: " + new Date().toString() + " ---\n";
377 text += EString.implode(messageLog);
379 Resource log = graph.newResource();
380 graph.claim(log, L0.InstanceOf, null, DOC.PlainTextDocument);
381 graph.claimLiteral(log, L0.HasName, L0.String, "Typical Sync " + new Date().toString());
382 graph.claim(library, L0.ConsistsOf, L0.PartOf, log);
383 graph.claimLiteral(log, DOC.PlainTextDocument_text, L0.String, text);
389 if (!metadata.getTypicals().isEmpty()) {
390 graph.addMetadata(metadata);
392 // Add comment to change set.
393 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
394 graph.addMetadata( cm.add("Synchronized " + metadata.getTypicals().size() + " typical diagram instances (" + metadata.getTypicals() + ") with their templates.") );
397 temporaryDiagram = null;
401 private Collection<Resource> findInstances(ReadGraph graph, Resource ofType, Collection<Resource> indexRoots) throws DatabaseException {
402 Instances index = graph.adapt(ofType, Instances.class);
403 Set<Resource> instances = new HashSet<>();
404 for (Resource indexRoot : indexRoots)
405 instances.addAll( index.find(graph, indexRoot) );
409 private void syncTemplate(WriteGraph graph, Resource template, Collection<Resource> indexRoots) throws DatabaseException {
410 Resource templateType = graph.getPossibleType(template, DIA.Diagram);
411 if (templateType == null)
414 Collection<Resource> instances = findInstances(graph, templateType, indexRoots);
415 // Do not include the template itself as it is also an instance of templateType
416 instances.remove(template);
417 if (instances.isEmpty())
420 Set<Resource> templateElements = new THashSet<>( graph.syncRequest(
421 new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );
424 for (Resource instance : instances) {
425 this.temporaryDiagram.setHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE, instance);
426 syncInstance(graph, template, instance, templateElements);
428 } catch (Exception e) {
429 LOGGER.error("Template synchronization failed.", e);
431 this.temporaryDiagram.removeHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
435 private void syncInstance(WriteGraph graph, Resource instance) throws DatabaseException {
436 Resource template = graph.getPossibleObject(instance, MOD.HasDiagramSource);
437 if (template == null)
440 Set<Resource> templateElements = new THashSet<>( graph.syncRequest(
441 new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );
444 this.temporaryDiagram.setHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE, instance);
445 syncInstance(graph, template, instance, templateElements);
447 this.temporaryDiagram.removeHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
451 private Resource findInstanceCounterpart(ReadGraph graph, Resource instanceDiagram, Resource templateElement) throws DatabaseException {
452 Map<String,Resource> children = graph.syncRequest(new UnescapedChildMapOfResource(instanceDiagram));
453 for(Resource child : children.values()) {
454 if(graph.hasStatement(child, MOD.HasElementSource, templateElement)) return child;
459 private boolean isSynchronizedConnector(ReadGraph graph, Resource templateConnection, Resource instanceConnector) throws DatabaseException {
460 DiagramResource DIA = DiagramResource.getInstance(graph);
461 Resource instanceConnection = graph.getPossibleObject(instanceConnector, DIA.IsConnectorOf);
462 if (instanceConnection == null)
464 return graph.hasStatement(instanceConnection, MOD.HasElementSource, templateConnection)
465 // If the master connection has been removed, this is all that's left
466 // to identify a connection that at least was originally synchronized
467 // from the typical master to this instance.
468 || graph.hasStatement(instanceConnection, MOD.IsTemplatized);
472 * Perform the following synchronization steps for the instance diagram:
474 * <li>remove such templatized elements from the instance diagram whose
475 * template counterpart no longer exists</li>
476 * <li>add elements to the instance diagram that are only in the template</li>
477 * <li>synchronize elements of the instance diagram that have been deemed
481 * @param graph database write access
482 * @param template the synchronization source diagram
483 * @param instance the synchronization target diagram
484 * @param currentTemplateElements the set of all elements currently in the
486 * @throws DatabaseException if anything goes wrong
488 private void syncInstance(WriteGraph graph, Resource template, Resource instance, Set<Resource> currentTemplateElements) throws DatabaseException {
490 List<String> messageLog = getLog(graph, instance);
492 messageLog.add("Synchronization of changed typical template: " + SyncTypicalTemplatesToInstances.safeNameAndType(graph, getDiagramNameResource(graph, template)));
493 messageLog.add("----\n\ttypical instance: " + safeNameAndType(graph, getDiagramNameResource(graph, instance)));
495 CommonDBUtils.selectClusterSet(graph, instance);
497 // Form instance element <-> template element bijection
498 TypicalInfoBean typicalInfoBean = graph.syncRequest(
499 new ReadTypicalInfo(instance),
500 TransientCacheListener.<TypicalInfoBean> instance());
501 // Must be able to modify the typicalInfo structure,
502 // therefore clone the query result.
503 typicalInfoBean = (TypicalInfoBean) typicalInfoBean.clone();
504 typicalInfoBean.templateElements = currentTemplateElements;
505 typicalInfoBean.auxiliary = new HashMap<>(1);
507 TypicalInfo info = new TypicalInfo();
508 info.monitor = monitor;
509 info.messageLog = messageLog;
510 info.bean = typicalInfoBean;
512 // Resolve naming function for this typical instance.
513 Resource compositeInstance = graph.getPossibleObject(instance, MOD.DiagramToComposite);
514 if (compositeInstance != null) {
515 Function4<ReadGraph, Resource, Resource, String, String> namingFunction = TypicalUtil.getTypicalNamingFunction(graph, compositeInstance);
516 if (namingFunction != null)
517 typicalInfoBean.auxiliary.put(AuxKeys.KEY_TYPICAL_NAMING_FUNCTION, namingFunction);
520 int dSizeAbs = Math.abs(typicalInfoBean.instanceElements.size() - currentTemplateElements.size());
523 System.out.println("typical <-> template mapping: " + typicalInfoBean.instanceToTemplate);
525 // Find elements to be removed from instance by looking for all
526 // instance elements that do not have a MOD.HasElementSource
527 // relation but have a MOD.IsTemplatized tag.
528 Set<Resource> instanceElementsRemovedFromTemplate = findInstanceElementsRemovedFromTemplate(
529 graph, info, new THashSet<>(dSizeAbs));
531 // Find elements in template that do not yet exist in the instance
532 Set<Resource> templateElementsAddedToTemplate = findTemplateElementsMissingFromInstance(
533 graph, currentTemplateElements, info,
534 new THashSet<>(dSizeAbs));
536 Set<Resource> changedTemplateElements = changedElementsByDiagram.removeValues(template);
539 System.out.println("ADDED: " + templateElementsAddedToTemplate.size() + ", REMOVED: " + instanceElementsRemovedFromTemplate.size() + ", CHANGED: " + changedTemplateElements.size());
542 for(Resource templateElement : graph.getObjects(template, L0.ConsistsOf)) {
543 if(graph.isInstanceOf(templateElement, DIA.RouteGraphConnection)) {
544 for(Resource connector : graph.getObjects(templateElement, DIA.HasConnector)) {
545 for(Statement elementStm : graph.getStatements(connector, STR.Connects)) {
546 Resource otherElement = elementStm.getObject();
547 if(!otherElement.equals(templateElement)) {
548 Resource counterPartElement = findInstanceCounterpart(graph, instance, otherElement);
549 if(counterPartElement != null) {
550 Resource diagramConnectionPoint = graph.getInverse(elementStm.getPredicate());
551 Resource connectionPoint = graph.getPossibleObject(diagramConnectionPoint, MOD.DiagramConnectionRelationToConnectionRelation);
552 if(connectionPoint != null) {
553 Statement stm = graph.getPossibleStatement(counterPartElement, diagramConnectionPoint);
555 if(graph.isInstanceOf(connectionPoint, L0.FunctionalRelation)) {
556 if(!isSynchronizedConnector(graph, templateElement, stm.getObject())) {
557 messageLog.add("\t\tWARNING: skipping addition of template connection " + NameUtils.getSafeName(graph, templateElement, true) + " into instance.");
558 messageLog.add("\t\t\ttried to connect to an already connected terminal " + NameUtils.getSafeName(graph, counterPartElement, true) + " " + NameUtils.getSafeName(graph, connectionPoint));
559 templateElementsAddedToTemplate.remove(templateElement);
572 boolean changed = false;
573 changed |= synchronizeDiagramChanges(graph, info, template, instance);
574 changed |= removeElements(graph, info, instanceElementsRemovedFromTemplate);
575 changed |= addMissingElements(graph, info, template, instance, templateElementsAddedToTemplate);
576 changed |= synchronizeChangedElements(graph, info, template, instance, changedTemplateElements, templateElementsAddedToTemplate, changedElementsByDiagram == ALL);
579 metadata.addTypical(instance);
583 * Synchronize any configurable aspects of the typical diagram instance itself.
584 * Every rule executed here comes from the ontology, nothing is fixed.
590 * @return if any changes were made.
591 * @throws DatabaseException
593 private boolean synchronizeDiagramChanges(
595 TypicalInfo typicalInfo,
598 throws DatabaseException
600 boolean changed = false;
601 for (Resource rule : graph.getObjects(template, MOD.HasTypicalSynchronizationRule)) {
602 if (selectedRules != null && !selectedRules.contains(rule))
604 ITypicalSynchronizationRule r = graph.getPossibleAdapter(rule, ITypicalSynchronizationRule.class);
606 changed |= r.synchronize(graph, template, instance, typicalInfo);
612 * Add elements from template that do not yet exist in the instance.
617 * @param elementsAddedToTemplate
618 * @return <code>true</code> if changes were made to the instance
619 * @throws DatabaseException
621 private boolean addMissingElements(WriteGraph graph, TypicalInfo typicalInfo, Resource template,
622 Resource instance, Set<Resource> elementsAddedToTemplate)
623 throws DatabaseException {
624 if (elementsAddedToTemplate.isEmpty())
627 CopyAdvisor copyAdvisor = graph.syncRequest(new Adapter<CopyAdvisor>(instance, CopyAdvisor.class));
628 this.temporaryDiagram.setHint(SynchronizationHints.COPY_ADVISOR, copyAdvisor);
630 ElementObjectAssortment assortment = new ElementObjectAssortment(graph, elementsAddedToTemplate);
632 copyMap = new THashMap<>();
637 System.out.println("ADD MISSING ELEMENTS: " + assortment);
639 // initialCopyMap argument is needed for copying just connections
640 // when their end-points are not copied at the same time.
642 PasteOperation pasteOp = new PasteOperation(Commands.COPY,
643 (ICanvasContext) null, template, instance, temporaryDiagram,
644 assortment, false, new Point2D.Double(0, 0),
645 typicalInfo.bean.templateToInstance, copyMap)
646 .options(PasteOperation.ForceCopyReferences.INSTANCE);
648 new Paster(graph.getSession(), pasteOp).perform(graph);
650 boolean changed = false;
652 if(!elementsAddedToTemplate.isEmpty())
653 typicalInfo.messageLog.add("\tadded elements");
655 for (Resource addedElement : elementsAddedToTemplate) {
656 Resource copyElement = (Resource) copyMap.get(addedElement);
657 if (copyElement != null) {
658 graph.claim(copyElement, MOD.IsTemplatized, MOD.IsTemplatized, copyElement);
659 graph.claim(copyElement, MOD.HasElementSource, MOD.ElementHasInstance, addedElement);
661 typicalInfo.bean.instanceElements.add(copyElement);
662 typicalInfo.bean.instanceToTemplate.put(copyElement, addedElement);
663 typicalInfo.bean.templateToInstance.put(addedElement, copyElement);
665 typicalInfo.messageLog.add("\t\t" + safeNameAndType(graph, copyElement));
671 ModelingResources MOD = ModelingResources.getInstance(graph);
672 Resource instanceComposite = graph.getPossibleObject(instance, MOD.DiagramToComposite);
673 List<Resource> instanceComponents = new ArrayList<>(elementsAddedToTemplate.size());
675 // Post-process added elements after typicalInfo has been updated and
676 // template mapping statements are in place.
677 for (Resource addedElement : elementsAddedToTemplate) {
678 Resource copyElement = (Resource) copyMap.get(addedElement);
679 if (copyElement != null) {
680 postProcessAddedElement(graph, addedElement, copyElement, typicalInfo);
682 if (instanceComponents != null) {
683 // Gather all instance typical components for applying naming
685 Resource component = graph.getPossibleObject(copyElement, MOD.ElementToComponent);
686 if (component != null)
687 instanceComponents.add(component);
692 if (instanceComposite != null)
693 TypicalUtil.applySelectedModuleNames(graph, instanceComposite, instanceComponents);
698 private void postProcessAddedElement(WriteGraph graph,
699 Resource addedTemplateElement, Resource addedInstanceElement,
700 TypicalInfo typicalInfo) throws DatabaseException {
701 if (graph.isInstanceOf(addedInstanceElement, DIA.Monitor)) {
702 postProcessAddedMonitor(graph, addedTemplateElement, addedInstanceElement, typicalInfo);
706 private void postProcessAddedMonitor(WriteGraph graph,
707 Resource addedTemplateMonitor, Resource addedInstanceMonitor,
708 TypicalInfo typicalInfo) throws DatabaseException {
709 Resource monitor = addedInstanceMonitor;
710 Resource monitoredComponent = graph.getPossibleObject(monitor, DIA.HasMonitorComponent);
711 if (monitoredComponent != null) {
712 Resource monitoredTemplateElement = graph.getPossibleObject(monitoredComponent, MOD.ComponentToElement);
713 if (monitoredTemplateElement != null) {
714 Resource monitoredInstanceElement = typicalInfo.bean.templateToInstance.get(monitoredTemplateElement);
715 if (monitoredInstanceElement != null) {
716 Resource monitoredInstanceComponent = graph.getPossibleObject(monitoredInstanceElement, MOD.ElementToComponent);
717 if (monitoredInstanceComponent != null) {
718 // Ok, the monitor refers to a component within the
719 // template composite. Change it to refer to the
720 // instance composite.
721 graph.deny(monitor, DIA.HasMonitorComponent);
722 graph.claim(monitor, DIA.HasMonitorComponent, monitoredInstanceComponent);
729 private boolean removeElements(WriteGraph graph, TypicalInfo typicalInfo, Set<Resource> elementsRemovedFromTemplate) throws DatabaseException {
730 if (elementsRemovedFromTemplate.isEmpty())
733 // Remove mapped elements from instance that are removed from the template.
734 boolean changed = false;
736 if(!elementsRemovedFromTemplate.isEmpty())
737 typicalInfo.messageLog.add("\tremoved elements");
739 for (Resource removedElement : elementsRemovedFromTemplate) {
740 typicalInfo.messageLog.add("\t\t" + safeNameAndType(graph, removedElement));
742 RemoverUtil.remove(graph, removedElement);
744 typicalInfo.bean.instanceElements.remove(removedElement);
745 Resource template = typicalInfo.bean.instanceToTemplate.remove(removedElement);
746 if (template != null)
747 typicalInfo.bean.templateToInstance.remove(template);
754 private Set<Resource> findTemplateElementsMissingFromInstance(
755 WriteGraph graph, Collection<Resource> currentTemplateElements,
756 TypicalInfo typicalInfo, THashSet<Resource> result)
757 throws DatabaseException {
758 for (Resource templateElement : currentTemplateElements) {
759 Resource instanceElement = typicalInfo.bean.templateToInstance.get(templateElement);
760 if (instanceElement == null) {
762 System.out.println("No instance correspondence for template element " + NameUtils.getSafeName(graph, templateElement, true) + " => add");
763 result.add(templateElement);
769 public Set<Resource> findInstanceElementsRemovedFromTemplate(
770 ReadGraph graph, TypicalInfo typicalInfo,
771 THashSet<Resource> result) throws DatabaseException {
772 for (Resource instanceElement : typicalInfo.bean.instanceElements) {
773 if (!typicalInfo.bean.instanceToTemplate.containsKey(instanceElement)) {
774 if (typicalInfo.bean.isTemplatized.contains(instanceElement)) {
776 System.out.println("Templatized typical instance element " + NameUtils.getSafeName(graph, instanceElement, true) + " has no correspondence in template => remove");
777 result.add(instanceElement);
785 * Synchronize basic visual aspects of changed elements. For all elements,
786 * transform and label are synchronized. Otherwise synchronization is
787 * type-specific for connections, flags, monitors and svg elements.
793 * @param changedTemplateElements
794 * @param addedElements
795 * elements that have been added and thus need not be
797 * @param synchronizeAllElements
799 * @throws DatabaseException
801 private boolean synchronizeChangedElements(WriteGraph graph,
802 TypicalInfo typicalInfo, Resource template, Resource instance,
803 Collection<Resource> changedTemplateElements,
804 Set<Resource> addedElements,
805 boolean synchronizeAllElements) throws DatabaseException {
807 if (synchronizeAllElements) {
808 // For unit testing purposes.
809 changedTemplateElements = graph.syncRequest(new ObjectsWithType(template, L0.ConsistsOf, DIA.Element));
812 if (changedTemplateElements.isEmpty())
815 boolean changed = false;
817 typicalInfo.messageLog.add("\telement change analysis");
818 int analysisLogPosition = typicalInfo.messageLog.size();
820 for (Resource changedTemplateElement : changedTemplateElements) {
821 // Skip synchronization of elements that were just added and are
822 // thus already synchronized.
823 if (addedElements.contains(changedTemplateElement))
826 Resource instanceElement = typicalInfo.bean.templateToInstance.get(changedTemplateElement);
827 if (instanceElement == null) {
828 // There's an earlier problem in the sync process if this happens.
829 typicalInfo.messageLog.add("\t\tSKIPPING SYNC OF CHANGED TEMPLATE ELEMENT DUE TO MISSING INSTANCE: " + safeNameAndType(graph, getElementNameResource(graph, changedTemplateElement)));
833 typicalInfo.messageLog.add("\t\t" + elementName(graph, changedTemplateElement));
834 int currentLogSize = typicalInfo.messageLog.size();
836 changed |= InstanceOfRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
837 changed |= NameRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
838 changed |= TransformRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
839 changed |= LabelRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
841 Collection<Resource> types = graph.getTypes(changedTemplateElement);
842 if (types.contains(DIA.RouteGraphConnection)) {
843 changed |= synchronizeConnection(graph, changedTemplateElement, instanceElement, typicalInfo);
844 } else if (types.contains(DIA.Flag)) {
845 changed |= FlagRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
846 } else if (types.contains(DIA.Monitor)) {
847 changed |= MonitorRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
848 } else if (types.contains(DIA.SVGElement)) {
849 changed |= SVGElementRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
852 changed |= ProfileMonitorRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
854 for (Resource rule : graph.getObjects(changedTemplateElement, MOD.HasTypicalSynchronizationRule)) {
855 if(selectedRules != null && !selectedRules.contains(rule)) continue;
856 ITypicalSynchronizationRule r = graph.getPossibleAdapter(rule, ITypicalSynchronizationRule.class);
858 changed |= r.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
861 // Show element only if something has happened
862 if(currentLogSize == typicalInfo.messageLog.size())
863 typicalInfo.messageLog.remove(typicalInfo.messageLog.size()-1);
872 // Show analysis header only if something has happened
873 if(analysisLogPosition == typicalInfo.messageLog.size())
874 typicalInfo.messageLog.remove(typicalInfo.messageLog.size()-1);
879 private static class Connector {
880 public final Resource attachmentRelation;
881 public final Resource connector;
882 public RouteLine attachedTo;
884 public Connector(Resource attachmentRelation, Resource connector) {
885 this.attachmentRelation = attachmentRelation;
886 this.connector = connector;
891 * Synchronizes two route graph connection topologies if and only if the
892 * destination connection is not attached to any node elements besides
893 * the ones that exist in the source. This means that connections that
894 * have instance-specific connections to non-template nodes are ignored
898 * @param sourceConnection
899 * @param targetConnection
901 * @return <code>true</code> if changes were made
902 * @throws DatabaseException
904 private boolean synchronizeConnection(WriteGraph graph, Resource sourceConnection, Resource targetConnection, TypicalInfo typicalInfo)
905 throws DatabaseException {
908 System.out.println("connection " + NameUtils.getSafeName(graph, sourceConnection, true) + " to target connection " + NameUtils.getSafeName(graph, targetConnection, true));
910 boolean changed = false;
912 // Initialize utilities and data maps
913 s2t = newOrClear(s2t);
914 t2s = newOrClear(t2s);
917 cu = new ConnectionUtil(graph);
919 // 0.1. find mappings between source and target connection connectors
920 Collection<Statement> toTargetConnectors = graph.getStatements(targetConnection, DIA.HasConnector);
921 Map<Resource, Connector> targetConnectors = new THashMap<>(toTargetConnectors.size());
922 for (Statement toTargetConnector : toTargetConnectors) {
923 Resource targetConnector = toTargetConnector.getObject();
924 targetConnectors.put(targetConnector, new Connector(toTargetConnector.getPredicate(), targetConnector));
925 Statement toNode = cu.getConnectedComponentStatement(targetConnection, targetConnector);
926 if (toNode == null) {
927 // Corrupted target connection!
928 ErrorLogger.defaultLogError("Encountered corrupted typical template connection "
929 + NameUtils.getSafeName(graph, targetConnection, true) + " with a stray DIA.Connector instance "
930 + NameUtils.getSafeName(graph, targetConnector, true) + " that is not attached to any element.",
931 new Exception("trace"));
934 if (!graph.hasStatement(targetConnector, DIA.AreConnected)) {
935 // Corrupted target connection!
936 ErrorLogger.defaultLogError("Encountered corrupted typical template connection "
937 + NameUtils.getSafeName(graph, targetConnection, true) + " with a stray DIA.Connector instance "
938 + NameUtils.getSafeName(graph, targetConnector, true) + " that is not connected to any other route node.",
939 new Exception("trace"));
943 //Resource templateNode = typicalInfo.instanceToTemplate.get(toNode.getObject());
944 Resource templateNode = graph.getPossibleObject(toNode.getObject(), MOD.HasElementSource);
945 if (templateNode != null) {
946 Resource isConnectedTo = graph.getPossibleInverse(toNode.getPredicate());
947 if (isConnectedTo != null) {
948 Resource templateConnector = graph.getPossibleObject(templateNode, isConnectedTo);
949 if (templateConnector != null) {
950 Resource connectionOfTemplateConnector = ConnectionUtil.tryGetConnection(graph, templateConnector);
951 if (sourceConnection.equals(connectionOfTemplateConnector)) {
952 s2t.put(templateConnector, targetConnector);
953 t2s.put(targetConnector, templateConnector);
956 debug(typicalInfo, "Mapping connector "
957 + NameUtils.getSafeName(graph, templateConnector, true)
958 + " to " + NameUtils.getSafeName(graph, targetConnector, true));
965 // 0.2. find mapping between source and target route lines
966 Collection<Resource> sourceInteriorRouteNodes = graph.getObjects(sourceConnection, DIA.HasInteriorRouteNode);
967 Collection<Resource> targetInteriorRouteNodes = graph.getObjects(targetConnection, DIA.HasInteriorRouteNode);
968 Map<Resource, Paster.RouteLine> sourceToRouteLine = new THashMap<>();
969 Map<Resource, Paster.RouteLine> targetToRouteLine = new THashMap<>();
971 for (Resource source : sourceInteriorRouteNodes)
972 sourceToRouteLine.put(source, Paster.readRouteLine(graph, source));
973 for (Resource target : targetInteriorRouteNodes)
974 targetToRouteLine.put(target, Paster.readRouteLine(graph, target));
976 Map<Resource, Paster.RouteLine> originalSourceToRouteLine = new THashMap<>(sourceToRouteLine);
977 Map<Resource, Paster.RouteLine> originalTargetToRouteLine = new THashMap<>(targetToRouteLine);
980 for (Iterator<Map.Entry<Resource, Paster.RouteLine>> sourceIt = sourceToRouteLine.entrySet().iterator(); !targetToRouteLine.isEmpty() && sourceIt.hasNext();) {
981 Map.Entry<Resource, Paster.RouteLine> sourceEntry = sourceIt.next();
982 Paster.RouteLine sourceLine = sourceEntry.getValue();
983 for (Iterator<Map.Entry<Resource, Paster.RouteLine>> targetIt = targetToRouteLine.entrySet().iterator(); targetIt.hasNext();) {
984 Map.Entry<Resource, Paster.RouteLine> targetEntry = targetIt.next();
985 if (sourceLine.equals(targetEntry.getValue())) {
986 s2t.put(sourceEntry.getKey(), targetEntry.getKey());
987 t2s.put(targetEntry.getKey(), sourceEntry.getKey());
992 debug(typicalInfo, "Mapping routeline "
993 + NameUtils.getSafeName(graph, sourceEntry.getKey(), true)
994 + " - " + sourceEntry.getValue()
995 + " to " + NameUtils.getSafeName(graph, targetEntry.getKey(), true)
996 + " - " + targetEntry.getValue());
998 continue nextSourceLine;
1004 debug(typicalInfo, "Take 1: Source to target route nodes map : " + s2t);
1005 debug(typicalInfo, "Take 1: Target to source route nodes map : " + t2s);
1008 // 1.1. Temporarily disconnect instance-specific connectors from the the connection .
1009 // They will be added back to the connection after the templatized parts of the
1010 // connection have been synchronized.
1012 // Stores diagram connectors that are customizations in the synchronized instance.
1013 List<Connector> instanceOnlyConnectors = null;
1015 for (Connector connector : targetConnectors.values()) {
1016 if (!t2s.containsKey(connector.connector)) {
1017 typicalInfo.messageLog.add("\t\tencountered instance-specific diagram connector in target connection: " + NameUtils.getSafeName(graph, connector.connector));
1019 // Find the RouteLine this connectors is connected to.
1020 for (Resource rl : graph.getObjects(connector.connector, DIA.AreConnected)) {
1021 connector.attachedTo = originalTargetToRouteLine.get(rl);
1022 if (connector.attachedTo != null)
1026 // Disconnect connector from connection
1027 graph.deny(targetConnection, connector.attachmentRelation, connector.connector);
1028 graph.deny(connector.connector, DIA.AreConnected);
1030 // Keep track of the disconnected connector
1031 if (instanceOnlyConnectors == null)
1032 instanceOnlyConnectors = new ArrayList<>(targetConnectors.size());
1033 instanceOnlyConnectors.add(connector);
1037 // 1.2. add missing connectors to target
1038 Collection<Resource> sourceConnectors = graph.getObjects(sourceConnection, DIA.HasConnector);
1039 for (Resource sourceConnector : sourceConnectors) {
1040 if (!s2t.containsKey(sourceConnector)) {
1041 Statement sourceIsConnectorOf = graph.getSingleStatement(sourceConnector, DIA.IsConnectorOf);
1042 Statement connects = cu.getConnectedComponentStatement(sourceConnection, sourceConnector);
1043 if (connects == null) {
1044 // TODO: serious error!
1045 throw new DatabaseException("ERROR: connector is astray, i.e. not connected to a node element: " + safeNameAndType(graph, sourceConnector));
1047 Resource connectsInstanceElement = typicalInfo.bean.templateToInstance.get(connects.getObject());
1048 if (connectsInstanceElement == null) {
1049 // TODO: serious error!
1050 throw new DatabaseException("ERROR: could not find instance element to which template element " + safeNameAndType(graph, connects.getObject()) + " is connected to");
1052 Resource hasConnector = graph.getInverse(sourceIsConnectorOf.getPredicate());
1054 Resource newTargetConnector = cu.newConnector(targetConnection, hasConnector);
1055 graph.claim(newTargetConnector, connects.getPredicate(), connectsInstanceElement);
1058 s2t.put(sourceConnector, newTargetConnector);
1059 t2s.put(newTargetConnector, sourceConnector);
1061 typicalInfo.messageLog.add("\t\t\tadd new connector to target connection: " + NameUtils.getSafeName(graph, newTargetConnector) + " to map to source connector " + NameUtils.getSafeName(graph, sourceConnector));
1065 // 2. sync route lines and their connectivity:
1066 // 2.1. assign correspondences in target for each source route line
1067 // by reusing excess route lines in target and by creating new
1070 Resource[] targetRouteLines = targetToRouteLine.keySet().toArray(Resource.NONE);
1071 int targetRouteLine = targetRouteLines.length - 1;
1073 for (Iterator<Map.Entry<Resource, Paster.RouteLine>> sourceIt = sourceToRouteLine.entrySet().iterator(); sourceIt.hasNext();) {
1074 Map.Entry<Resource, Paster.RouteLine> sourceEntry = sourceIt.next();
1075 Resource source = sourceEntry.getKey();
1076 Paster.RouteLine sourceLine = sourceEntry.getValue();
1078 typicalInfo.messageLog.add("\t\t\tassign an instance-side routeline counterpart for " + NameUtils.getSafeName(graph, source, true) + " - " + sourceLine);
1080 // Assign target route line for source
1081 Resource target = null;
1082 if (targetRouteLine < 0) {
1083 // by creating new route lines
1084 target = cu.newRouteLine(targetConnection, sourceLine.getPosition(), sourceLine.isHorizontal());
1085 typicalInfo.messageLog.add("\t\t\tcreate new route line " + NameUtils.getSafeName(graph, target));
1088 // by reusing existing route line
1089 target = targetRouteLines[targetRouteLine--];
1090 copyRouteLine(graph, source, target);
1091 cu.disconnectFromAllRouteNodes(target);
1092 typicalInfo.messageLog.add("\t\t\treused existing route line " + NameUtils.getSafeName(graph, target));
1095 s2t.put(source, target);
1096 t2s.put(target, source);
1098 typicalInfo.messageLog.add("\t\t\tmapped source route line " + NameUtils.getSafeName(graph, source) + " to target route line " + NameUtils.getSafeName(graph, target));
1101 if (targetRouteLine >= 0) {
1102 typicalInfo.messageLog.add("\t\t\tremove excess route lines (" + (targetRouteLine + 1) + ") from target connection");
1103 for (; targetRouteLine >= 0; targetRouteLine--) {
1104 typicalInfo.messageLog.add("\t\t\t\tremove excess route line: " + NameUtils.getSafeName(graph, targetRouteLines[targetRouteLine], true));
1105 cu.removeConnectionPart(targetRouteLines[targetRouteLine]);
1110 debug(typicalInfo, "Take 2: Source to target route nodes map : " + s2t);
1111 debug(typicalInfo, "Take 2: Target to source route nodes map : " + t2s);
1114 // 2.2. Synchronize target connection topology (DIA.AreConnected)
1115 changed |= connectRouteNodes(graph, typicalInfo, sourceInteriorRouteNodes);
1116 changed |= connectRouteNodes(graph, typicalInfo, sourceConnectors);
1118 // 3. remove excess routelines & connectors from target connection
1119 changed |= cu.removeExtraInteriorRouteNodes(targetConnection) > 0;
1120 changed |= cu.removeUnusedConnectors(targetConnection) > 0;
1122 // 3.1. Ensure that all mapped route nodes in the target connection
1123 // are tagged with MOD.IsTemplatized. Future synchronization
1124 // can then take advantage of this information to more easily
1125 // decide which parts of the connection are originated from
1126 // the template and which are not.
1127 changed |= markMappedRouteNodesTemplatized(graph, s2t.values());
1129 // 4. Add temporarily disconnected instance-specific connectors
1130 // back to the synchronized connection. The route line to attach
1131 // to is based on a simple heuristic.
1132 if (instanceOnlyConnectors != null) {
1133 if (originalSourceToRouteLine.isEmpty()) {
1134 // If there are 0 route lines in the template connection,
1135 // then one must be added to the instance connection.
1136 // This can only happen if the template connection is
1137 // simple, i.e. just between two terminals without any
1140 // Attach all target connection connectors to the newly created route line
1141 Resource rl = cu.newRouteLine(targetConnection, null, null);
1142 for (Resource sourceConnector : sourceConnectors) {
1143 Resource targetConnector = s2t.get(sourceConnector);
1144 graph.deny(targetConnector, DIA.AreConnected);
1145 graph.claim(targetConnector, DIA.AreConnected, DIA.AreConnected, rl);
1148 // Copy orientation and position for new route line from original target route lines.
1149 // This is a simplification that will attach any amount of route lines in the original
1150 // target connection into just one route line. There is room for improvement here
1151 // but it will require a more elaborate algorithm to find and cut the non-templatized
1152 // route lines as well as connectors out of the connection before synchronizing it.
1154 // TODO: This implementation chooses the added route line position at random if
1155 // there are multiple route lines in the target connection.
1156 if (!originalTargetToRouteLine.isEmpty()) {
1157 RouteLine originalRl = originalTargetToRouteLine.values().iterator().next();
1158 setRouteLine(graph, rl, originalRl);
1161 // Attach the instance specific connectors also to the only route line
1162 for (Connector connector : instanceOnlyConnectors) {
1163 graph.claim(targetConnection, connector.attachmentRelation, connector.connector);
1164 graph.claim(connector.connector, DIA.AreConnected, DIA.AreConnected, rl);
1169 for (Connector connector : instanceOnlyConnectors) {
1170 // Find the route line that most closely matches the original
1171 // route line that the connector was connected to.
1172 Resource closestMatch = null;
1173 double closestDistance = Double.MAX_VALUE;
1174 if (connector.attachedTo != null) {
1175 for (Map.Entry<Resource, Paster.RouteLine> sourceLine : originalSourceToRouteLine.entrySet()) {
1176 double dist = distance(sourceLine.getValue(), connector.attachedTo);
1177 if (dist < closestDistance) {
1178 closestMatch = s2t.get(sourceLine.getKey());
1179 closestDistance = dist;
1183 closestMatch = originalSourceToRouteLine.keySet().iterator().next();
1185 graph.claim(targetConnection, connector.attachmentRelation, connector.connector);
1186 graph.claim(connector.connector, DIA.AreConnected, DIA.AreConnected, closestMatch);
1187 if (closestDistance > 0)
1189 typicalInfo.messageLog.add("\t\t\treattached instance-specific connector "
1190 + NameUtils.getSafeName(graph, connector.connector) + " to nearest existing route line "
1191 + NameUtils.getSafeName(graph, closestMatch) + " with distance " + closestDistance);
1199 private boolean markMappedRouteNodesTemplatized(WriteGraph graph, Iterable<Resource> routeNodes) throws DatabaseException {
1200 boolean changed = false;
1201 for (Resource rn : routeNodes) {
1202 if (!graph.hasStatement(rn, MOD.IsTemplatized)) {
1203 graph.claim(rn, MOD.IsTemplatized, MOD.IsTemplatized, rn);
1210 private static double distance(RouteLine l1, RouteLine l2) {
1211 double dist = Math.abs(l2.getPosition() - l1.getPosition());
1212 dist *= l2.isHorizontal() == l1.isHorizontal() ? 1 : 1000;
1216 private boolean connectRouteNodes(WriteGraph graph, TypicalInfo typicalInfo, Collection<Resource> sourceRouteNodes) throws DatabaseException {
1217 boolean changed = false;
1218 for (Resource src : sourceRouteNodes) {
1219 Resource dst = s2t.get(src);
1221 throw new DatabaseException("TARGET ROUTE NODE == NULL FOR SRC: " + NameUtils.getSafeName(graph, src));
1224 Collection<Resource> connectedToSrcs = graph.getObjects(src, DIA.AreConnected);
1225 Collection<Resource> connectedToDsts = graph.getObjects(dst, DIA.AreConnected);
1227 // Remove excess statements
1228 for (Resource connectedToDst : connectedToDsts) {
1229 Resource connectedToSrc = t2s.get(connectedToDst);
1230 if (connectedToSrc == null) {
1231 throw new DatabaseException("CONNECTED TO SRC == NULL FOR DST: " + NameUtils.getSafeName(graph, connectedToDst));
1233 if (connectedToSrc == null || !graph.hasStatement(src, DIA.AreConnected, connectedToSrc)) {
1234 graph.deny(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);
1236 typicalInfo.messageLog.add("\t\t\tdisconnected route nodes (" + NameUtils.getSafeName(graph, dst) + ", " + NameUtils.getSafeName(graph, connectedToDst) + ")");
1240 // Add necessary statements
1241 for (Resource connectedToSrc : connectedToSrcs) {
1242 Resource connectedToDst = s2t.get(connectedToSrc);
1243 if (connectedToDst == null) {
1244 throw new DatabaseException("CONNECTED TO DST == NULL FOR SRC: " + NameUtils.getSafeName(graph, connectedToSrc));
1246 if (!graph.hasStatement(dst, DIA.AreConnected, connectedToDst)) {
1247 graph.claim(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);
1249 typicalInfo.messageLog.add("\t\t\tconnected route nodes (" + NameUtils.getSafeName(graph, dst) + ", " + NameUtils.getSafeName(graph, connectedToDst) + ")");
1256 private void setRouteLine(WriteGraph graph, Resource line, double position, boolean horizontal) throws DatabaseException {
1257 graph.claimLiteral(line, DIA.HasPosition, L0.Double, position, Bindings.DOUBLE);
1258 graph.claimLiteral(line, DIA.IsHorizontal, L0.Boolean, horizontal, Bindings.BOOLEAN);
1261 private void setRouteLine(WriteGraph graph, Resource line, RouteLine rl) throws DatabaseException {
1262 setRouteLine(graph, line, rl.getPosition(), rl.isHorizontal());
1265 private void copyRouteLine(WriteGraph graph, Resource src, Resource tgt) throws DatabaseException {
1266 Double pos = graph.getPossibleRelatedValue(src, DIA.HasPosition, Bindings.DOUBLE);
1267 Boolean hor = graph.getPossibleRelatedValue(src, DIA.IsHorizontal, Bindings.BOOLEAN);
1272 graph.claimLiteral(tgt, DIA.HasPosition, L0.Double, pos, Bindings.DOUBLE);
1273 graph.claimLiteral(tgt, DIA.IsHorizontal, L0.Boolean, hor, Bindings.BOOLEAN);
1276 private static String safeNameAndType(ReadGraph graph, Resource r) throws DatabaseException {
1277 StringBuilder sb = new StringBuilder();
1278 sb.append(NameUtils.getSafeName(graph, r, true));
1280 boolean first = true;
1281 for (Resource type : graph.getPrincipalTypes(r)) {
1285 sb.append(NameUtils.getSafeName(graph, type, true));
1288 return sb.toString();
1291 private static <K, V> Map<K, V> newOrClear(Map<K, V> current) {
1292 if (current == null)
1293 return new THashMap<>();
1298 private void debug(TypicalInfo typicalInfo, String message) {
1300 System.out.println(message);
1301 typicalInfo.messageLog.add(message);