1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2012 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.modeling.typicals;
\r
14 import java.awt.geom.Point2D;
\r
15 import java.util.ArrayList;
\r
16 import java.util.Collection;
\r
17 import java.util.Collections;
\r
18 import java.util.Date;
\r
19 import java.util.HashMap;
\r
20 import java.util.HashSet;
\r
21 import java.util.Iterator;
\r
22 import java.util.List;
\r
23 import java.util.Map;
\r
24 import java.util.Set;
\r
26 import org.eclipse.core.runtime.IProgressMonitor;
\r
27 import org.simantics.Simantics;
\r
28 import org.simantics.databoard.Bindings;
\r
29 import org.simantics.db.ReadGraph;
\r
30 import org.simantics.db.Resource;
\r
31 import org.simantics.db.Statement;
\r
32 import org.simantics.db.WriteGraph;
\r
33 import org.simantics.db.common.CommentMetadata;
\r
34 import org.simantics.db.common.NamedResource;
\r
35 import org.simantics.db.common.primitiverequest.Adapter;
\r
36 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
\r
37 import org.simantics.db.common.request.ObjectsWithType;
\r
38 import org.simantics.db.common.request.PossibleIndexRoot;
\r
39 import org.simantics.db.common.request.WriteRequest;
\r
40 import org.simantics.db.common.uri.UnescapedChildMapOfResource;
\r
41 import org.simantics.db.common.utils.CommonDBUtils;
\r
42 import org.simantics.db.common.utils.NameUtils;
\r
43 import org.simantics.db.exception.DatabaseException;
\r
44 import org.simantics.db.layer0.adapter.Instances;
\r
45 import org.simantics.db.layer0.request.ActiveModels;
\r
46 import org.simantics.db.layer0.util.RemoverUtil;
\r
47 import org.simantics.diagram.content.ConnectionUtil;
\r
48 import org.simantics.diagram.handler.CopyPasteStrategy;
\r
49 import org.simantics.diagram.handler.ElementObjectAssortment;
\r
50 import org.simantics.diagram.handler.PasteOperation;
\r
51 import org.simantics.diagram.handler.Paster;
\r
52 import org.simantics.diagram.stubs.DiagramResource;
\r
53 import org.simantics.diagram.synchronization.CollectingModificationQueue;
\r
54 import org.simantics.diagram.synchronization.CopyAdvisor;
\r
55 import org.simantics.diagram.synchronization.SynchronizationHints;
\r
56 import org.simantics.diagram.synchronization.graph.GraphSynchronizationContext;
\r
57 import org.simantics.diagram.ui.DiagramModelHints;
\r
58 import org.simantics.document.DocumentResource;
\r
59 import org.simantics.g2d.canvas.ICanvasContext;
\r
60 import org.simantics.g2d.diagram.DiagramClass;
\r
61 import org.simantics.g2d.diagram.IDiagram;
\r
62 import org.simantics.g2d.diagram.impl.Diagram;
\r
63 import org.simantics.layer0.Layer0;
\r
64 import org.simantics.modeling.ModelingResources;
\r
65 import org.simantics.modeling.ModelingUtils;
\r
66 import org.simantics.modeling.mapping.ModelingSynchronizationHints;
\r
67 import org.simantics.modeling.typicals.rules.AuxKeys;
\r
68 import org.simantics.modeling.typicals.rules.FlagRule;
\r
69 import org.simantics.modeling.typicals.rules.InstanceOfRule;
\r
70 import org.simantics.modeling.typicals.rules.LabelRule;
\r
71 import org.simantics.modeling.typicals.rules.MonitorRule;
\r
72 import org.simantics.modeling.typicals.rules.NameRule;
\r
73 import org.simantics.modeling.typicals.rules.ProfileMonitorRule;
\r
74 import org.simantics.modeling.typicals.rules.SVGElementRule;
\r
75 import org.simantics.modeling.typicals.rules.TransformRule;
\r
76 import org.simantics.scenegraph.g2d.events.command.Commands;
\r
77 import org.simantics.scl.runtime.function.Function4;
\r
78 import org.simantics.structural.stubs.StructuralResource2;
\r
79 import org.simantics.utils.datastructures.MapSet;
\r
80 import org.simantics.utils.strings.AlphanumComparator;
\r
81 import org.simantics.utils.strings.EString;
\r
82 import org.simantics.utils.ui.ErrorLogger;
\r
83 import org.slf4j.Logger;
\r
84 import org.slf4j.LoggerFactory;
\r
86 import gnu.trove.map.hash.THashMap;
\r
87 import gnu.trove.set.hash.THashSet;
\r
90 * A write request that synchronizes typical master templates and their
\r
91 * instances as specified.
\r
94 * Use {@link #SyncTypicalTemplatesToInstances(Resource[], MapSet)} to
\r
95 * synchronize all instances of specified templates. Use
\r
96 * {@link #syncSingleInstance(Resource)} to synchronize a single typical
\r
97 * instance with its master template.
\r
99 * @author Tuukka Lehtonen
\r
101 * @see ReadTypicalInfo
\r
103 * @see TypicalSynchronizationMetadata
\r
105 public class SyncTypicalTemplatesToInstances extends WriteRequest {
\r
106 private static final Logger LOGGER = LoggerFactory.getLogger(SyncTypicalTemplatesToInstances.class);
\r
109 * A constant used as the second argument to
\r
110 * {@link #SyncTemplates(Resource[], MapSet)} for stating that all specified
\r
111 * templates should be fully synchronized to their instances.
\r
113 * This is useful for forcing complete synchronization and unit testing.
\r
115 public static final EmptyMapSet ALL = EmptyMapSet.INSTANCE;
\r
117 public static class EmptyMapSet extends MapSet<Resource, Resource> {
\r
119 public static final EmptyMapSet INSTANCE = new EmptyMapSet();
\r
121 public EmptyMapSet() {
\r
122 this.sets = Collections.emptyMap();
\r
126 protected Set<Resource> getOrCreateSet(Resource key) {
\r
127 throw new UnsupportedOperationException("immutable constant instance");
\r
132 protected static final boolean DEBUG = false;
\r
136 final private IProgressMonitor monitor;
\r
138 * Typical diagram rules to apply
\r
140 protected Set<Resource> selectedRules;
\r
143 * Typical diagram templates to synchronize with their instances.
\r
145 protected Resource[] templates;
\r
148 * Typical diagram instances to synchronize with their templates.
\r
150 protected Resource[] instances;
\r
153 * For each template diagram in {@link #templates}, shall contain a set of
\r
154 * elements that have changed and should be synchronized into the instance
\r
155 * diagrams. Provided as an argument by the client. Allows optimizing
\r
156 * real-time synchronization by not processing everything all the time.
\r
158 * If the value is {@link #ALL}, all elements of the template shall be fully
\r
161 protected MapSet<Resource, Resource> changedElementsByDiagram;
\r
165 protected Layer0 L0;
\r
166 protected StructuralResource2 STR;
\r
167 protected DiagramResource DIA;
\r
168 protected ModelingResources MOD;
\r
171 * Needed for using {@link Paster} in
\r
172 * {@link #addMissingElements(WriteGraph, TypicalInfo, Resource, Resource, Set)}
\r
174 protected GraphSynchronizationContext syncCtx;
\r
177 * For collecting commit metadata during the processing of this request.
\r
179 protected TypicalSynchronizationMetadata metadata;
\r
182 * Necessary for using {@link CopyPasteStrategy} and {@link PasteOperation}
\r
183 * for now. Will be removed in the future once IDiagram is removed from
\r
186 protected IDiagram temporaryDiagram;
\r
188 protected ConnectionUtil cu;
\r
191 * Maps source -> target connection route nodes, i.e. connectors and
\r
192 * interior route nodes (route lines). Inverse mapping of {@link #t2s}.
\r
194 protected Map<Resource, Resource> s2t;
\r
197 * Maps target -> source connection route nodes, i.e. connectors and
\r
198 * interior route nodes (route lines). Inverse mapping of {@link #s2t}.
\r
200 protected Map<Resource, Resource> t2s;
\r
203 * An auxiliary resource map for extracting the correspondences between
\r
204 * originals and copied resource when diagram contents are copied from
\r
205 * template to instance.
\r
207 protected Map<Object, Object> copyMap;
\r
209 final private Map<Resource, List<String>> messageLogs = new HashMap<Resource, List<String>>();
\r
211 public List<Resource> logs = new ArrayList<Resource>();
\r
213 private boolean writeLog;
\r
219 * @param selectedRules
\r
222 * @throws DatabaseException
\r
224 public static void syncTypicals(WriteGraph graph, boolean log, List<Resource> templates, List<Resource> instances) throws DatabaseException {
\r
226 new SyncTypicalTemplatesToInstances(
\r
228 templates.toArray(Resource.NONE),
\r
229 instances.toArray(Resource.NONE),
\r
236 * @param templates typical diagram templates to completely synchronize with
\r
239 public SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource... templates) {
\r
240 this(selectedRules, templates, null, ALL, null);
\r
244 * @param templates typical diagram templates to partially synchronize with
\r
246 * @param changedElementsByDiagram see {@link #changedElementsByDiagram}
\r
248 public SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource[] templates, MapSet<Resource, Resource> changedElementsByDiagram) {
\r
249 this(selectedRules, templates, null, changedElementsByDiagram, null);
\r
253 * Return a write request that completely synchronizes the specified
\r
254 * instance diagram with its template.
\r
259 public static SyncTypicalTemplatesToInstances syncSingleInstance(Set<Resource> selectedRules, Resource instance) {
\r
260 return new SyncTypicalTemplatesToInstances(selectedRules, null, new Resource[] { instance }, ALL, null);
\r
264 * @param templates typical diagram templates to synchronize with their instances
\r
265 * @param instances typical diagram instances to synchronize with their templates
\r
266 * @param changedElementsByDiagram see {@link #changedElementsByDiagram}
\r
268 private SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource[] templates, Resource[] instances, MapSet<Resource, Resource> changedElementsByDiagram, IProgressMonitor monitor) {
\r
269 this.selectedRules = selectedRules;
\r
270 this.templates = templates;
\r
271 this.instances = instances;
\r
272 this.changedElementsByDiagram = changedElementsByDiagram;
\r
273 this.monitor = monitor;
\r
276 public SyncTypicalTemplatesToInstances logging(boolean writeLog) {
\r
277 this.writeLog = writeLog;
\r
281 private Resource getDiagramNameResource(ReadGraph graph, Resource diagram) throws DatabaseException {
\r
282 Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
\r
283 if(composite != null) return composite;
\r
284 else return diagram;
\r
287 private Resource getElementNameResource(ReadGraph graph, Resource element) throws DatabaseException {
\r
288 Resource corr = ModelingUtils.getPossibleElementCorrespondendence(graph, element);
\r
289 if(corr != null) return corr;
\r
290 else return element;
\r
293 private List<String> getLog(ReadGraph graph, Resource diagram) throws DatabaseException {
\r
294 Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(diagram));
\r
295 if(indexRoot == null) throw new DatabaseException("FATAL: Diagram is not under any index root.");
\r
296 List<String> log = messageLogs.get(indexRoot);
\r
298 log = new ArrayList<String>();
\r
299 messageLogs.put(indexRoot, log);
\r
304 private String elementName(ReadGraph graph, Resource element) throws DatabaseException {
\r
306 StringBuilder b = new StringBuilder();
\r
307 b.append(safeNameAndType(graph, element));
\r
309 int spaces = 60-b.length();
\r
310 for(int i=0;i<spaces;i++) b.append(" ");
\r
312 Resource corr = ModelingUtils.getPossibleElementCorrespondendence(graph, element);
\r
314 b.append(safeNameAndType(graph, corr));
\r
319 return b.toString();
\r
324 public void perform(WriteGraph graph) throws DatabaseException {
\r
325 this.L0 = Layer0.getInstance(graph);
\r
326 this.STR = StructuralResource2.getInstance(graph);
\r
327 this.DIA = DiagramResource.getInstance(graph);
\r
328 this.MOD = ModelingResources.getInstance(graph);
\r
330 this.syncCtx = GraphSynchronizationContext.getWriteInstance( graph, new CollectingModificationQueue() );
\r
331 this.syncCtx.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph));
\r
333 this.metadata = new TypicalSynchronizationMetadata();
\r
334 this.metadata.synchronizedTypicals = new ArrayList<Resource>();
\r
336 this.temporaryDiagram = Diagram.spawnNew(DiagramClass.DEFAULT);
\r
337 this.temporaryDiagram.setHint(SynchronizationHints.CONTEXT, syncCtx);
\r
339 this.cu = new ConnectionUtil(graph);
\r
341 if (templates != null) {
\r
342 // Look for typical template instances from the currently active models only.
\r
343 Collection<Resource> activeModels = graph.syncRequest(new ActiveModels(Simantics.getProjectResource()));
\r
344 if (!activeModels.isEmpty()) {
\r
345 for (Resource template : templates) {
\r
346 syncTemplate(graph, template, activeModels);
\r
350 if (instances != null) {
\r
351 for (Resource instance : instances) {
\r
352 syncInstance(graph, instance);
\r
357 for(Map.Entry<Resource, List<String>> entry : messageLogs.entrySet()) {
\r
359 Resource indexRoot = entry.getKey();
\r
360 List<String> messageLog = entry.getValue();
\r
362 Layer0 L0 = Layer0.getInstance(graph);
\r
363 DocumentResource DOC = DocumentResource.getInstance(graph);
\r
365 Collection<Resource> libs = graph.syncRequest(new ObjectsWithType(indexRoot, L0.ConsistsOf, DOC.DocumentLibrary));
\r
366 if(libs.isEmpty()) continue;
\r
368 List<NamedResource> nrs = new ArrayList<NamedResource>();
\r
369 for(Resource lib : libs) nrs.add(new NamedResource(NameUtils.getSafeName(graph, lib), lib));
\r
370 Collections.sort(nrs, AlphanumComparator.CASE_INSENSITIVE_COMPARATOR);
\r
371 Resource library = nrs.iterator().next().getResource();
\r
373 CommonDBUtils.selectClusterSet(graph, library);
\r
375 String text = "--- Created: " + new Date().toString() + " ---\n";
\r
376 text += EString.implode(messageLog);
\r
378 Resource log = graph.newResource();
\r
379 graph.claim(log, L0.InstanceOf, null, DOC.PlainTextDocument);
\r
380 graph.claimLiteral(log, L0.HasName, L0.String, "Typical Sync " + new Date().toString());
\r
381 graph.claim(library, L0.ConsistsOf, L0.PartOf, log);
\r
382 graph.claimLiteral(log, DOC.PlainTextDocument_text, L0.String, text);
\r
388 if (!metadata.getTypicals().isEmpty()) {
\r
389 graph.addMetadata(metadata);
\r
391 // Add comment to change set.
\r
392 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
\r
393 graph.addMetadata( cm.add("Synchronized " + metadata.getTypicals().size() + " typical diagram instances (" + metadata.getTypicals() + ") with their templates.") );
\r
396 temporaryDiagram = null;
\r
400 private Collection<Resource> findInstances(ReadGraph graph, Resource ofType, Collection<Resource> indexRoots) throws DatabaseException {
\r
401 Instances index = graph.adapt(ofType, Instances.class);
\r
402 Set<Resource> instances = new HashSet<>();
\r
403 for (Resource indexRoot : indexRoots)
\r
404 instances.addAll( index.find(graph, indexRoot) );
\r
408 private void syncTemplate(WriteGraph graph, Resource template, Collection<Resource> indexRoots) throws DatabaseException {
\r
409 Resource templateType = graph.getPossibleType(template, DIA.Diagram);
\r
410 if (templateType == null)
\r
413 Collection<Resource> instances = findInstances(graph, templateType, indexRoots);
\r
414 // Do not include the template itself as it is also an instance of templateType
\r
415 instances.remove(template);
\r
416 if (instances.isEmpty())
\r
419 Set<Resource> templateElements = new THashSet<Resource>( graph.syncRequest(
\r
420 new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );
\r
423 for (Resource instance : instances) {
\r
424 this.temporaryDiagram.setHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE, instance);
\r
425 syncInstance(graph, template, instance, templateElements);
\r
427 } catch (Exception e) {
\r
428 LOGGER.error("Template synchronization failed.", e);
\r
430 this.temporaryDiagram.removeHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
\r
434 private void syncInstance(WriteGraph graph, Resource instance) throws DatabaseException {
\r
435 Resource template = graph.getPossibleObject(instance, MOD.HasDiagramSource);
\r
436 if (template == null)
\r
439 Set<Resource> templateElements = new THashSet<Resource>( graph.syncRequest(
\r
440 new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );
\r
443 this.temporaryDiagram.setHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE, instance);
\r
444 syncInstance(graph, template, instance, templateElements);
\r
446 this.temporaryDiagram.removeHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
\r
450 private Resource findInstanceCounterpart(ReadGraph graph, Resource instanceDiagram, Resource templateElement) throws DatabaseException {
\r
451 Map<String,Resource> children = graph.syncRequest(new UnescapedChildMapOfResource(instanceDiagram));
\r
452 for(Resource child : children.values()) {
\r
453 if(graph.hasStatement(child, MOD.HasElementSource, templateElement)) return child;
\r
458 private boolean isSynchronizedConnector(ReadGraph graph, Resource templateConnection, Resource instanceConnector) throws DatabaseException {
\r
459 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
460 Resource instanceConnection = graph.getPossibleObject(instanceConnector, DIA.IsConnectorOf);
\r
461 return graph.hasStatement(instanceConnection, MOD.HasElementSource, templateConnection)
\r
462 // If the master connection has been removed, this is all that's left
\r
463 // to identify a connection that at least was originally synchronized
\r
464 // from the typical master to this instance.
\r
465 || graph.hasStatement(instanceConnection, MOD.IsTemplatized);
\r
469 * Perform the following synchronization steps for the instance diagram:
\r
471 * <li>remove such templatized elements from the instance diagram whose
\r
472 * template counterpart no longer exists</li>
\r
473 * <li>add elements to the instance diagram that are only in the template</li>
\r
474 * <li>synchronize elements of the instance diagram that have been deemed
\r
478 * @param graph database write access
\r
479 * @param template the synchronization source diagram
\r
480 * @param instance the synchronization target diagram
\r
481 * @param currentTemplateElements the set of all elements currently in the
\r
483 * @throws DatabaseException if anything goes wrong
\r
485 private void syncInstance(WriteGraph graph, Resource template, Resource instance, Set<Resource> currentTemplateElements) throws DatabaseException {
\r
487 List<String> messageLog = getLog(graph, instance);
\r
489 messageLog.add("Synchronization of changed typical template: " + SyncTypicalTemplatesToInstances.safeNameAndType(graph, getDiagramNameResource(graph, template)));
\r
490 messageLog.add("----\n\ttypical instance: " + safeNameAndType(graph, getDiagramNameResource(graph, instance)));
\r
492 CommonDBUtils.selectClusterSet(graph, instance);
\r
494 // Form instance element <-> template element bijection
\r
495 TypicalInfoBean typicalInfoBean = graph.syncRequest(
\r
496 new ReadTypicalInfo(instance),
\r
497 TransientCacheListener.<TypicalInfoBean> instance());
\r
498 // Must be able to modify the typicalInfo structure,
\r
499 // therefore clone the query result.
\r
500 typicalInfoBean = (TypicalInfoBean) typicalInfoBean.clone();
\r
501 typicalInfoBean.templateElements = currentTemplateElements;
\r
502 typicalInfoBean.auxiliary = new HashMap<Object, Object>(1);
\r
504 TypicalInfo info = new TypicalInfo();
\r
505 info.monitor = monitor;
\r
506 info.messageLog = messageLog;
\r
507 info.bean = typicalInfoBean;
\r
509 // Resolve naming function for this typical instance.
\r
510 Resource compositeInstance = graph.getPossibleObject(instance, MOD.DiagramToComposite);
\r
511 if (compositeInstance != null) {
\r
512 Function4<ReadGraph, Resource, Resource, String, String> namingFunction = TypicalUtil.getTypicalNamingFunction(graph, compositeInstance);
\r
513 if (namingFunction != null)
\r
514 typicalInfoBean.auxiliary.put(AuxKeys.KEY_TYPICAL_NAMING_FUNCTION, namingFunction);
\r
517 int dSizeAbs = Math.abs(typicalInfoBean.instanceElements.size() - currentTemplateElements.size());
\r
520 System.out.println("typical <-> template mapping: " + typicalInfoBean.instanceToTemplate);
\r
522 // Find elements to be removed from instance by looking for all
\r
523 // instance elements that do not have a MOD.HasElementSource
\r
524 // relation but have a MOD.IsTemplatized tag.
\r
525 Set<Resource> instanceElementsRemovedFromTemplate = findInstanceElementsRemovedFromTemplate(
\r
526 graph, info, new THashSet<Resource>(dSizeAbs));
\r
528 // Find elements in template that do not yet exist in the instance
\r
529 Set<Resource> templateElementsAddedToTemplate = findTemplateElementsMissingFromInstance(
\r
530 graph, currentTemplateElements, info,
\r
531 new THashSet<Resource>(dSizeAbs));
\r
533 Set<Resource> changedTemplateElements = changedElementsByDiagram.removeValues(template);
\r
536 System.out.println("ADDED: " + templateElementsAddedToTemplate.size() + ", REMOVED: " + instanceElementsRemovedFromTemplate.size() + ", CHANGED: " + changedTemplateElements.size());
\r
539 for(Resource templateElement : graph.getObjects(template, L0.ConsistsOf)) {
\r
540 if(graph.isInstanceOf(templateElement, DIA.RouteGraphConnection)) {
\r
541 for(Resource connector : graph.getObjects(templateElement, DIA.HasConnector)) {
\r
542 for(Statement elementStm : graph.getStatements(connector, STR.Connects)) {
\r
543 Resource otherElement = elementStm.getObject();
\r
544 if(!otherElement.equals(templateElement)) {
\r
545 Resource counterPartElement = findInstanceCounterpart(graph, instance, otherElement);
\r
546 if(counterPartElement != null) {
\r
547 Resource diagramConnectionPoint = graph.getInverse(elementStm.getPredicate());
\r
548 Resource connectionPoint = graph.getPossibleObject(diagramConnectionPoint, MOD.DiagramConnectionRelationToConnectionRelation);
\r
549 if(connectionPoint != null) {
\r
550 Statement stm = graph.getPossibleStatement(counterPartElement, diagramConnectionPoint);
\r
552 if(graph.isInstanceOf(connectionPoint, L0.FunctionalRelation)) {
\r
553 if(!isSynchronizedConnector(graph, templateElement, stm.getObject())) {
\r
554 messageLog.add("\t\tABORTED: tried to connect to an already connected terminal " + NameUtils.getSafeName(graph, counterPartElement) + " " + NameUtils.getSafeName(graph, connectionPoint));
\r
568 boolean changed = false;
\r
569 changed |= synchronizeDiagramChanges(graph, info, template, instance);
\r
570 changed |= removeElements(graph, info, instanceElementsRemovedFromTemplate);
\r
571 changed |= addMissingElements(graph, info, template, instance, templateElementsAddedToTemplate);
\r
572 changed |= synchronizeChangedElements(graph, info, template, instance, changedTemplateElements, templateElementsAddedToTemplate, changedElementsByDiagram == ALL);
\r
575 metadata.addTypical(instance);
\r
579 * Synchronize any configurable aspects of the typical diagram instance itself.
\r
580 * Every rule executed here comes from the ontology, nothing is fixed.
\r
583 * @param typicalInfo
\r
586 * @return if any changes were made.
\r
587 * @throws DatabaseException
\r
589 private boolean synchronizeDiagramChanges(
\r
591 TypicalInfo typicalInfo,
\r
594 throws DatabaseException
\r
596 boolean changed = false;
\r
597 for (Resource rule : graph.getObjects(template, MOD.HasTypicalSynchronizationRule)) {
\r
598 if (selectedRules != null && !selectedRules.contains(rule))
\r
600 ITypicalSynchronizationRule r = graph.getPossibleAdapter(rule, ITypicalSynchronizationRule.class);
\r
602 changed |= r.synchronize(graph, template, instance, typicalInfo);
\r
608 * Add elements from template that do not yet exist in the instance.
\r
613 * @param elementsAddedToTemplate
\r
614 * @return <code>true</code> if changes were made to the instance
\r
615 * @throws DatabaseException
\r
617 private boolean addMissingElements(WriteGraph graph, TypicalInfo typicalInfo, Resource template,
\r
618 Resource instance, Set<Resource> elementsAddedToTemplate)
\r
619 throws DatabaseException {
\r
620 if (elementsAddedToTemplate.isEmpty())
\r
623 CopyAdvisor copyAdvisor = graph.syncRequest(new Adapter<CopyAdvisor>(instance, CopyAdvisor.class));
\r
624 this.temporaryDiagram.setHint(SynchronizationHints.COPY_ADVISOR, copyAdvisor);
\r
626 ElementObjectAssortment assortment = new ElementObjectAssortment(graph, elementsAddedToTemplate);
\r
627 if (copyMap == null)
\r
628 copyMap = new THashMap<Object, Object>();
\r
633 System.out.println("ADD MISSING ELEMENTS: " + assortment);
\r
635 // initialCopyMap argument is needed for copying just connections
\r
636 // when their end-points are not copied at the same time.
\r
638 PasteOperation pasteOp = new PasteOperation(Commands.COPY,
\r
639 (ICanvasContext) null, template, instance, temporaryDiagram,
\r
640 assortment, false, new Point2D.Double(0, 0),
\r
641 typicalInfo.bean.templateToInstance, copyMap)
\r
642 .options(PasteOperation.ForceCopyReferences.INSTANCE);
\r
644 new Paster(graph.getSession(), pasteOp).perform(graph);
\r
646 boolean changed = false;
\r
648 if(!elementsAddedToTemplate.isEmpty())
\r
649 typicalInfo.messageLog.add("\tadded elements");
\r
651 for (Resource addedElement : elementsAddedToTemplate) {
\r
652 Resource copyElement = (Resource) copyMap.get(addedElement);
\r
653 if (copyElement != null) {
\r
654 graph.claim(copyElement, MOD.IsTemplatized, MOD.IsTemplatized, copyElement);
\r
655 graph.claim(copyElement, MOD.HasElementSource, MOD.ElementHasInstance, addedElement);
\r
657 typicalInfo.bean.instanceElements.add(copyElement);
\r
658 typicalInfo.bean.instanceToTemplate.put(copyElement, addedElement);
\r
659 typicalInfo.bean.templateToInstance.put(addedElement, copyElement);
\r
661 typicalInfo.messageLog.add("\t\t" + safeNameAndType(graph, copyElement));
\r
667 ModelingResources MOD = ModelingResources.getInstance(graph);
\r
668 Resource instanceComposite = graph.getPossibleObject(instance, MOD.DiagramToComposite);
\r
669 List<Resource> instanceComponents = new ArrayList<Resource>(elementsAddedToTemplate.size());
\r
671 // Post-process added elements after typicalInfo has been updated and
\r
672 // template mapping statements are in place.
\r
673 for (Resource addedElement : elementsAddedToTemplate) {
\r
674 Resource copyElement = (Resource) copyMap.get(addedElement);
\r
675 if (copyElement != null) {
\r
676 postProcessAddedElement(graph, addedElement, copyElement, typicalInfo);
\r
678 if (instanceComponents != null) {
\r
679 // Gather all instance typical components for applying naming
\r
680 // strategy on them.
\r
681 Resource component = graph.getPossibleObject(copyElement, MOD.ElementToComponent);
\r
682 if (component != null)
\r
683 instanceComponents.add(component);
\r
688 if (instanceComposite != null)
\r
689 TypicalUtil.applySelectedModuleNames(graph, instanceComposite, instanceComponents);
\r
694 private void postProcessAddedElement(WriteGraph graph,
\r
695 Resource addedTemplateElement, Resource addedInstanceElement,
\r
696 TypicalInfo typicalInfo) throws DatabaseException {
\r
697 if (graph.isInstanceOf(addedInstanceElement, DIA.Monitor)) {
\r
698 postProcessAddedMonitor(graph, addedTemplateElement, addedInstanceElement, typicalInfo);
\r
702 private void postProcessAddedMonitor(WriteGraph graph,
\r
703 Resource addedTemplateMonitor, Resource addedInstanceMonitor,
\r
704 TypicalInfo typicalInfo) throws DatabaseException {
\r
705 Resource monitor = addedInstanceMonitor;
\r
706 Resource monitoredComponent = graph.getPossibleObject(monitor, DIA.HasMonitorComponent);
\r
707 if (monitoredComponent != null) {
\r
708 Resource monitoredTemplateElement = graph.getPossibleObject(monitoredComponent, MOD.ComponentToElement);
\r
709 if (monitoredTemplateElement != null) {
\r
710 Resource monitoredInstanceElement = typicalInfo.bean.templateToInstance.get(monitoredTemplateElement);
\r
711 if (monitoredInstanceElement != null) {
\r
712 Resource monitoredInstanceComponent = graph.getPossibleObject(monitoredInstanceElement, MOD.ElementToComponent);
\r
713 if (monitoredInstanceComponent != null) {
\r
714 // Ok, the monitor refers to a component within the
\r
715 // template composite. Change it to refer to the
\r
716 // instance composite.
\r
717 graph.deny(monitor, DIA.HasMonitorComponent);
\r
718 graph.claim(monitor, DIA.HasMonitorComponent, monitoredInstanceComponent);
\r
725 private boolean removeElements(WriteGraph graph, TypicalInfo typicalInfo, Set<Resource> elementsRemovedFromTemplate) throws DatabaseException {
\r
726 if (elementsRemovedFromTemplate.isEmpty())
\r
729 // Remove mapped elements from instance that are removed from the template.
\r
730 boolean changed = false;
\r
732 if(!elementsRemovedFromTemplate.isEmpty())
\r
733 typicalInfo.messageLog.add("\tremoved elements");
\r
735 for (Resource removedElement : elementsRemovedFromTemplate) {
\r
736 typicalInfo.messageLog.add("\t\t" + safeNameAndType(graph, removedElement));
\r
738 RemoverUtil.remove(graph, removedElement);
\r
740 typicalInfo.bean.instanceElements.remove(removedElement);
\r
741 Resource template = typicalInfo.bean.instanceToTemplate.remove(removedElement);
\r
742 if (template != null)
\r
743 typicalInfo.bean.templateToInstance.remove(template);
\r
750 private Set<Resource> findTemplateElementsMissingFromInstance(
\r
751 WriteGraph graph, Collection<Resource> currentTemplateElements,
\r
752 TypicalInfo typicalInfo, THashSet<Resource> result)
\r
753 throws DatabaseException {
\r
754 for (Resource templateElement : currentTemplateElements) {
\r
755 Resource instanceElement = typicalInfo.bean.templateToInstance.get(templateElement);
\r
756 if (instanceElement == null) {
\r
758 System.out.println("No instance correspondence for template element " + NameUtils.getSafeName(graph, templateElement, true) + " => add");
\r
759 result.add(templateElement);
\r
765 public Set<Resource> findInstanceElementsRemovedFromTemplate(
\r
766 ReadGraph graph, TypicalInfo typicalInfo,
\r
767 THashSet<Resource> result) throws DatabaseException {
\r
768 for (Resource instanceElement : typicalInfo.bean.instanceElements) {
\r
769 if (!typicalInfo.bean.instanceToTemplate.containsKey(instanceElement)) {
\r
770 if (typicalInfo.bean.isTemplatized.contains(instanceElement)) {
\r
772 System.out.println("Templatized typical instance element " + NameUtils.getSafeName(graph, instanceElement, true) + " has no correspondence in template => remove");
\r
773 result.add(instanceElement);
\r
781 * Synchronize basic visual aspects of changed elements. For all elements,
\r
782 * transform and label are synchronized. Otherwise synchronization is
\r
783 * type-specific for connections, flags, monitors and svg elements.
\r
786 * @param typicalInfo
\r
789 * @param changedTemplateElements
\r
790 * @param addedElements
\r
791 * elements that have been added and thus need not be
\r
793 * @param synchronizeAllElements
\r
795 * @throws DatabaseException
\r
797 private boolean synchronizeChangedElements(WriteGraph graph,
\r
798 TypicalInfo typicalInfo, Resource template, Resource instance,
\r
799 Collection<Resource> changedTemplateElements,
\r
800 Set<Resource> addedElements,
\r
801 boolean synchronizeAllElements) throws DatabaseException {
\r
803 if (synchronizeAllElements) {
\r
804 // For unit testing purposes.
\r
805 changedTemplateElements = graph.syncRequest(new ObjectsWithType(template, L0.ConsistsOf, DIA.Element));
\r
808 if (changedTemplateElements.isEmpty())
\r
811 boolean changed = false;
\r
813 typicalInfo.messageLog.add("\telement change analysis");
\r
814 int analysisLogPosition = typicalInfo.messageLog.size();
\r
816 for (Resource changedTemplateElement : changedTemplateElements) {
\r
817 // Skip synchronization of elements that were just added and are
\r
818 // thus already synchronized.
\r
819 if (addedElements.contains(changedTemplateElement))
\r
822 Resource instanceElement = typicalInfo.bean.templateToInstance.get(changedTemplateElement);
\r
823 if (instanceElement == null) {
\r
824 // There's an earlier problem in the sync process if this happens.
\r
825 typicalInfo.messageLog.add("SKIPPING SYNC OF CHANGED TEMPLATE ELEMENT DUE TO MISSING INSTANCE: " + safeNameAndType(graph, getElementNameResource(graph, changedTemplateElement)));
\r
829 typicalInfo.messageLog.add("\t\t" + elementName(graph, changedTemplateElement));
\r
830 int currentLogSize = typicalInfo.messageLog.size();
\r
832 changed |= InstanceOfRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
\r
833 changed |= NameRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
\r
834 changed |= TransformRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
\r
835 changed |= LabelRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
\r
837 Collection<Resource> types = graph.getTypes(changedTemplateElement);
\r
838 if (types.contains(DIA.RouteGraphConnection)) {
\r
839 changed |= synchronizeConnection(graph, changedTemplateElement, instanceElement, typicalInfo);
\r
840 } else if (types.contains(DIA.Flag)) {
\r
841 changed |= FlagRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
\r
842 } else if (types.contains(DIA.Monitor)) {
\r
843 changed |= MonitorRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
\r
844 } else if (types.contains(DIA.SVGElement)) {
\r
845 changed |= SVGElementRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
\r
848 changed |= ProfileMonitorRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
\r
850 for (Resource rule : graph.getObjects(changedTemplateElement, MOD.HasTypicalSynchronizationRule)) {
\r
851 if(selectedRules != null && !selectedRules.contains(rule)) continue;
\r
852 ITypicalSynchronizationRule r = graph.getPossibleAdapter(rule, ITypicalSynchronizationRule.class);
\r
854 changed |= r.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);
\r
857 // Show element only if something has happened
\r
858 if(currentLogSize == typicalInfo.messageLog.size())
\r
859 typicalInfo.messageLog.remove(typicalInfo.messageLog.size()-1);
\r
868 // Show analysis header only if something has happened
\r
869 if(analysisLogPosition == typicalInfo.messageLog.size())
\r
870 typicalInfo.messageLog.remove(typicalInfo.messageLog.size()-1);
\r
876 * Synchronizes two route graph connection topologies if and only if the
\r
877 * destination connection is not attached to any node elements besides
\r
878 * the ones that exist in the source. This means that connections that
\r
879 * have instance-specific connections to non-template nodes are ignored
\r
883 * @param sourceConnection
\r
884 * @param targetConnection
\r
885 * @param typicalInfo
\r
886 * @return <code>true</code> if changes were made
\r
887 * @throws DatabaseException
\r
889 private boolean synchronizeConnection(WriteGraph graph, Resource sourceConnection, Resource targetConnection, TypicalInfo typicalInfo)
\r
890 throws DatabaseException {
\r
893 System.out.println("connection " + NameUtils.getSafeName(graph, sourceConnection, true) + " to target connection " + NameUtils.getSafeName(graph, targetConnection, true));
\r
895 boolean changed = false;
\r
897 // Initialize utilities and data maps
\r
898 s2t = newOrClear(s2t);
\r
899 t2s = newOrClear(t2s);
\r
902 cu = new ConnectionUtil(graph);
\r
904 // 0.1. find mappings between source and target connection connectors
\r
905 Collection<Resource> targetConnectors = graph.getObjects(targetConnection, DIA.HasConnector);
\r
906 for (Resource targetConnector : targetConnectors) {
\r
907 Statement toNode = cu.getConnectedComponentStatement(targetConnection, targetConnector);
\r
908 if (toNode == null) {
\r
909 // Corrupted target connection!
\r
910 ErrorLogger.defaultLogError("Encountered corrupted typical template connection " + NameUtils.getSafeName(graph, targetConnection, true) + " with a stray DIA.Connector instance " + NameUtils.getSafeName(graph, targetConnector, true), new Exception("trace"));
\r
914 // Check that the target connections does not connect to
\r
915 // non-templatized elements before syncing.
\r
916 if (!graph.hasStatement(toNode.getObject(), MOD.IsTemplatized))
\r
919 //Resource templateNode = typicalInfo.instanceToTemplate.get(toNode.getObject());
\r
920 Resource templateNode = graph.getPossibleObject(toNode.getObject(), MOD.HasElementSource);
\r
921 if (templateNode != null) {
\r
922 Resource isConnectedTo = graph.getPossibleInverse(toNode.getPredicate());
\r
923 if (isConnectedTo != null) {
\r
924 Resource templateConnector = graph.getPossibleObject(templateNode, isConnectedTo);
\r
925 if (templateConnector != null) {
\r
926 Resource connectionOfTemplateConnector = ConnectionUtil.tryGetConnection(graph, templateConnector);
\r
927 if (sourceConnection.equals(connectionOfTemplateConnector)) {
\r
928 s2t.put(templateConnector, targetConnector);
\r
929 t2s.put(targetConnector, templateConnector);
\r
932 System.out.println("Mapping connector "
\r
933 + NameUtils.getSafeName(graph, templateConnector, true)
\r
934 + " to " + NameUtils.getSafeName(graph, targetConnector, true));
\r
941 // 0.2. find mapping between source and target route lines
\r
942 Collection<Resource> sourceInteriorRouteNodes = graph.getObjects(sourceConnection, DIA.HasInteriorRouteNode);
\r
943 Collection<Resource> targetInteriorRouteNodes = graph.getObjects(targetConnection, DIA.HasInteriorRouteNode);
\r
944 Map<Resource, Paster.RouteLine> sourceToRouteLine = new THashMap<Resource, Paster.RouteLine>();
\r
945 Map<Resource, Paster.RouteLine> targetToRouteLine = new THashMap<Resource, Paster.RouteLine>();
\r
947 for (Resource source : sourceInteriorRouteNodes)
\r
948 sourceToRouteLine.put(source, Paster.readRouteLine(graph, source));
\r
949 for (Resource target : targetInteriorRouteNodes)
\r
950 targetToRouteLine.put(target, Paster.readRouteLine(graph, target));
\r
953 for (Iterator<Map.Entry<Resource, Paster.RouteLine>> sourceIt = sourceToRouteLine.entrySet().iterator(); !targetToRouteLine.isEmpty() && sourceIt.hasNext();) {
\r
954 Map.Entry<Resource, Paster.RouteLine> sourceEntry = sourceIt.next();
\r
955 Paster.RouteLine sourceLine = sourceEntry.getValue();
\r
956 for (Iterator<Map.Entry<Resource, Paster.RouteLine>> targetIt = targetToRouteLine.entrySet().iterator(); targetIt.hasNext();) {
\r
957 Map.Entry<Resource, Paster.RouteLine> targetEntry = targetIt.next();
\r
958 if (sourceLine.equals(targetEntry.getValue())) {
\r
959 s2t.put(sourceEntry.getKey(), targetEntry.getKey());
\r
960 t2s.put(targetEntry.getKey(), sourceEntry.getKey());
\r
965 System.out.println("Mapping routeline "
\r
966 + NameUtils.getSafeName(graph, sourceEntry.getKey(), true)
\r
967 + " - " + sourceEntry.getValue()
\r
968 + " to " + NameUtils.getSafeName(graph, targetEntry.getKey(), true)
\r
969 + " - " + targetEntry.getValue());
\r
971 continue nextSourceLine;
\r
977 System.out.println("Take 1: Source to target route nodes map : " + s2t);
\r
978 System.out.println("Take 1: Target to source route nodes map : " + t2s);
\r
981 // 1.1 remove excess connectors
\r
982 for (Resource targetConnector : targetConnectors) {
\r
983 if (!t2s.containsKey(targetConnector)) {
\r
984 typicalInfo.messageLog.add("\t\t\tremove excess connector from target connection: " + NameUtils.getSafeName(graph, targetConnector));
\r
985 cu.removeConnectionPart(targetConnector);
\r
990 // 1.2 add missing connectors to target
\r
991 Collection<Resource> sourceConnectors = graph.getObjects(sourceConnection, DIA.HasConnector);
\r
992 for (Resource sourceConnector : sourceConnectors) {
\r
993 if (!s2t.containsKey(sourceConnector)) {
\r
994 Statement sourceIsConnectorOf = graph.getSingleStatement(sourceConnector, DIA.IsConnectorOf);
\r
995 Statement connects = cu.getConnectedComponentStatement(sourceConnection, sourceConnector);
\r
996 if (connects == null) {
\r
997 // TODO: serious error!
\r
998 throw new DatabaseException("ERROR: connector is astray, i.e. not connected to a node element: " + safeNameAndType(graph, sourceConnector));
\r
1000 Resource connectsInstanceElement = typicalInfo.bean.templateToInstance.get(connects.getObject());
\r
1001 if (connectsInstanceElement == null) {
\r
1002 // TODO: serious error!
\r
1003 throw new DatabaseException("ERROR: could not find instance element to which template element " + safeNameAndType(graph, connects.getObject()) + " is connected to");
\r
1005 Resource hasConnector = graph.getInverse(sourceIsConnectorOf.getPredicate());
\r
1007 Resource newTargetConnector = cu.newConnector(targetConnection, hasConnector);
\r
1008 graph.claim(newTargetConnector, connects.getPredicate(), connectsInstanceElement);
\r
1011 s2t.put(sourceConnector, newTargetConnector);
\r
1012 t2s.put(newTargetConnector, sourceConnector);
\r
1014 typicalInfo.messageLog.add("\t\t\tadd new connector to target connection: " + NameUtils.getSafeName(graph, newTargetConnector) + " to map to source connector " + NameUtils.getSafeName(graph, sourceConnector));
\r
1018 // 2. sync route lines and their connectivity:
\r
1019 // 2.1. assign correspondences in target for each source route line
\r
1020 // by reusing excess routelines in target and by creating new
\r
1023 Resource[] targetRouteLines = targetToRouteLine.keySet().toArray(Resource.NONE);
\r
1024 int targetRouteLine = targetRouteLines.length - 1;
\r
1026 for (Iterator<Map.Entry<Resource, Paster.RouteLine>> sourceIt = sourceToRouteLine.entrySet().iterator(); sourceIt.hasNext();) {
\r
1027 Map.Entry<Resource, Paster.RouteLine> sourceEntry = sourceIt.next();
\r
1028 Resource source = sourceEntry.getKey();
\r
1029 Paster.RouteLine sourceLine = sourceEntry.getValue();
\r
1031 typicalInfo.messageLog.add("\t\t\tassign an instance-side routeline complement for " + NameUtils.getSafeName(graph, source, true) + " - " + sourceLine);
\r
1033 // Assign target route line for source
\r
1034 Resource target = null;
\r
1035 if (targetRouteLine < 0) {
\r
1036 // by creating new route lines
\r
1037 target = cu.newRouteLine(targetConnection, sourceLine.getPosition(), sourceLine.isHorizontal());
\r
1038 typicalInfo.messageLog.add("\t\t\tcreate new route line " + NameUtils.getSafeName(graph, target));
\r
1041 // by reusing existing route line
\r
1042 target = targetRouteLines[targetRouteLine--];
\r
1043 copyRouteLine(graph, source, target);
\r
1044 cu.disconnectFromAllRouteNodes(target);
\r
1045 typicalInfo.messageLog.add("\t\t\treused existing route line " + NameUtils.getSafeName(graph, target));
\r
1048 s2t.put(source, target);
\r
1049 t2s.put(target, source);
\r
1051 typicalInfo.messageLog.add("\t\t\tmapped source route line " + NameUtils.getSafeName(graph, source) + " to target route line " + NameUtils.getSafeName(graph, target));
\r
1054 if (targetRouteLine >= 0) {
\r
1055 typicalInfo.messageLog.add("\t\t\tremove excess route lines (" + (targetRouteLine + 1) + ") from target connection");
\r
1056 for (; targetRouteLine >= 0; targetRouteLine--) {
\r
1057 typicalInfo.messageLog.add("\t\t\t\tremove excess route line: " + NameUtils.getSafeName(graph, targetRouteLines[targetRouteLine], true));
\r
1058 cu.removeConnectionPart(targetRouteLines[targetRouteLine]);
\r
1063 System.out.println("Take 2: Source to target route nodes map : " + s2t);
\r
1064 System.out.println("Take 2: Target to source route nodes map : " + t2s);
\r
1067 // 2.2. Synchronize target connection topology (DIA.AreConnected)
\r
1068 changed |= connectRouteNodes(graph, typicalInfo, sourceInteriorRouteNodes);
\r
1069 changed |= connectRouteNodes(graph, typicalInfo, sourceConnectors);
\r
1071 // 3. remove excess routelines & connectors from target connection
\r
1072 changed |= cu.removeExtraInteriorRouteNodes(targetConnection) > 0;
\r
1073 changed |= cu.removeUnusedConnectors(targetConnection) > 0;
\r
1078 private boolean connectRouteNodes(WriteGraph graph, TypicalInfo typicalInfo, Collection<Resource> sourceRouteNodes) throws DatabaseException {
\r
1079 boolean changed = false;
\r
1080 for (Resource src : sourceRouteNodes) {
\r
1081 Resource dst = s2t.get(src);
\r
1082 if (dst == null) {
\r
1083 throw new DatabaseException("TARGET ROUTE NODE == NULL FOR SRC: " + NameUtils.getSafeName(graph, src));
\r
1086 Collection<Resource> connectedToSrcs = graph.getObjects(src, DIA.AreConnected);
\r
1087 Collection<Resource> connectedToDsts = graph.getObjects(dst, DIA.AreConnected);
\r
1089 // Remove excess statements
\r
1090 for (Resource connectedToDst : connectedToDsts) {
\r
1091 Resource connectedToSrc = t2s.get(connectedToDst);
\r
1092 if (connectedToSrc == null) {
\r
1093 throw new DatabaseException("CONNECTED TO SRC == NULL FOR DST: " + NameUtils.getSafeName(graph, connectedToDst));
\r
1095 if (connectedToSrc == null || !graph.hasStatement(src, DIA.AreConnected, connectedToSrc)) {
\r
1096 graph.deny(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);
\r
1098 typicalInfo.messageLog.add("\t\t\tdisconnected route nodes (" + NameUtils.getSafeName(graph, dst) + ", " + NameUtils.getSafeName(graph, connectedToDst) + ")");
\r
1102 // Add necessary statements
\r
1103 for (Resource connectedToSrc : connectedToSrcs) {
\r
1104 Resource connectedToDst = s2t.get(connectedToSrc);
\r
1105 if (connectedToDst == null) {
\r
1106 throw new DatabaseException("CONNECTED TO DST == NULL FOR SRC: " + NameUtils.getSafeName(graph, connectedToSrc));
\r
1108 if (!graph.hasStatement(dst, DIA.AreConnected, connectedToDst)) {
\r
1109 graph.claim(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);
\r
1111 typicalInfo.messageLog.add("\t\t\tconnected route nodes (" + NameUtils.getSafeName(graph, dst) + ", " + NameUtils.getSafeName(graph, connectedToDst) + ")");
\r
1118 private void copyRouteLine(WriteGraph graph, Resource src, Resource tgt) throws DatabaseException {
\r
1119 Double pos = graph.getPossibleRelatedValue(src, DIA.HasPosition, Bindings.DOUBLE);
\r
1120 Boolean hor = graph.getPossibleRelatedValue(src, DIA.IsHorizontal, Bindings.BOOLEAN);
\r
1124 hor = Boolean.TRUE;
\r
1125 graph.claimLiteral(tgt, DIA.HasPosition, L0.Double, pos, Bindings.DOUBLE);
\r
1126 graph.claimLiteral(tgt, DIA.IsHorizontal, L0.Boolean, hor, Bindings.BOOLEAN);
\r
1129 private static String safeNameAndType(ReadGraph graph, Resource r) throws DatabaseException {
\r
1130 StringBuilder sb = new StringBuilder();
\r
1131 sb.append(NameUtils.getSafeName(graph, r, true));
\r
1132 sb.append(" : [");
\r
1133 boolean first = true;
\r
1134 for (Resource type : graph.getPrincipalTypes(r)) {
\r
1138 sb.append(NameUtils.getSafeName(graph, type, true));
\r
1141 return sb.toString();
\r
1144 private static <K, V> Map<K, V> newOrClear(Map<K, V> current) {
\r
1145 if (current == null)
\r
1146 return new THashMap<K, V>();
\r