--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2012 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.modeling.typicals;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+import java.awt.geom.Point2D;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Date;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.simantics.Logger;\r
+import org.simantics.Simantics;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Statement;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.CommentMetadata;\r
+import org.simantics.db.common.NamedResource;\r
+import org.simantics.db.common.primitiverequest.Adapter;\r
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;\r
+import org.simantics.db.common.request.ObjectsWithType;\r
+import org.simantics.db.common.request.PossibleIndexRoot;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.common.uri.UnescapedChildMapOfResource;\r
+import org.simantics.db.common.utils.CommonDBUtils;\r
+import org.simantics.db.common.utils.NameUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.adapter.Instances;\r
+import org.simantics.db.layer0.request.ActiveModels;\r
+import org.simantics.db.layer0.util.RemoverUtil;\r
+import org.simantics.diagram.content.ConnectionUtil;\r
+import org.simantics.diagram.handler.CopyPasteStrategy;\r
+import org.simantics.diagram.handler.ElementObjectAssortment;\r
+import org.simantics.diagram.handler.PasteOperation;\r
+import org.simantics.diagram.handler.Paster;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.synchronization.CollectingModificationQueue;\r
+import org.simantics.diagram.synchronization.CopyAdvisor;\r
+import org.simantics.diagram.synchronization.SynchronizationHints;\r
+import org.simantics.diagram.synchronization.graph.GraphSynchronizationContext;\r
+import org.simantics.diagram.ui.DiagramModelHints;\r
+import org.simantics.document.DocumentResource;\r
+import org.simantics.g2d.canvas.ICanvasContext;\r
+import org.simantics.g2d.diagram.DiagramClass;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.diagram.impl.Diagram;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.modeling.ModelingUtils;\r
+import org.simantics.modeling.mapping.ModelingSynchronizationHints;\r
+import org.simantics.modeling.typicals.rules.AuxKeys;\r
+import org.simantics.modeling.typicals.rules.FlagRule;\r
+import org.simantics.modeling.typicals.rules.InstanceOfRule;\r
+import org.simantics.modeling.typicals.rules.LabelRule;\r
+import org.simantics.modeling.typicals.rules.MonitorRule;\r
+import org.simantics.modeling.typicals.rules.NameRule;\r
+import org.simantics.modeling.typicals.rules.ProfileMonitorRule;\r
+import org.simantics.modeling.typicals.rules.SVGElementRule;\r
+import org.simantics.modeling.typicals.rules.TransformRule;\r
+import org.simantics.scenegraph.g2d.events.command.Commands;\r
+import org.simantics.scl.runtime.function.Function4;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+import org.simantics.utils.datastructures.MapSet;\r
+import org.simantics.utils.strings.AlphanumComparator;\r
+import org.simantics.utils.strings.EString;\r
+import org.simantics.utils.ui.ErrorLogger;\r
+\r
+/**\r
+ * A write request that synchronizes typical master templates and their\r
+ * instances as specified.\r
+ * \r
+ * <p>\r
+ * Use {@link #SyncTypicalTemplatesToInstances(Resource[], MapSet)} to\r
+ * synchronize all instances of specified templates. Use\r
+ * {@link #syncSingleInstance(Resource)} to synchronize a single typical\r
+ * instance with its master template.\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ * \r
+ * @see ReadTypicalInfo\r
+ * @see TypicalInfo\r
+ * @see TypicalSynchronizationMetadata\r
+ */\r
+public class SyncTypicalTemplatesToInstances extends WriteRequest {\r
+\r
+ /**\r
+ * A constant used as the second argument to\r
+ * {@link #SyncTemplates(Resource[], MapSet)} for stating that all specified\r
+ * templates should be fully synchronized to their instances.\r
+ * \r
+ * This is useful for forcing complete synchronization and unit testing. \r
+ */\r
+ public static final EmptyMapSet ALL = EmptyMapSet.INSTANCE;\r
+\r
+ public static class EmptyMapSet extends MapSet<Resource, Resource> {\r
+\r
+ public static final EmptyMapSet INSTANCE = new EmptyMapSet();\r
+\r
+ public EmptyMapSet() {\r
+ this.sets = Collections.emptyMap();\r
+ }\r
+\r
+ @Override\r
+ protected Set<Resource> getOrCreateSet(Resource key) {\r
+ throw new UnsupportedOperationException("immutable constant instance");\r
+ }\r
+\r
+ };\r
+\r
+ protected static final boolean DEBUG = false;\r
+\r
+ // Input\r
+\r
+ final private IProgressMonitor monitor;\r
+ /**\r
+ * Typical diagram rules to apply\r
+ */\r
+ protected Set<Resource> selectedRules;\r
+ \r
+ /**\r
+ * Typical diagram templates to synchronize with their instances.\r
+ */\r
+ protected Resource[] templates;\r
+\r
+ /**\r
+ * Typical diagram instances to synchronize with their templates.\r
+ */\r
+ protected Resource[] instances;\r
+\r
+ /**\r
+ * For each template diagram in {@link #templates}, shall contain a set of\r
+ * elements that have changed and should be synchronized into the instance\r
+ * diagrams. Provided as an argument by the client. Allows optimizing\r
+ * real-time synchronization by not processing everything all the time.\r
+ * \r
+ * If the value is {@link #ALL}, all elements of the template shall be fully\r
+ * synchronized.\r
+ */\r
+ protected MapSet<Resource, Resource> changedElementsByDiagram;\r
+\r
+ // Temporary data\r
+\r
+ protected Layer0 L0;\r
+ protected StructuralResource2 STR;\r
+ protected DiagramResource DIA;\r
+ protected ModelingResources MOD;\r
+\r
+ /**\r
+ * Needed for using {@link Paster} in\r
+ * {@link #addMissingElements(WriteGraph, TypicalInfo, Resource, Resource, Set)}\r
+ */\r
+ protected GraphSynchronizationContext syncCtx;\r
+\r
+ /**\r
+ * For collecting commit metadata during the processing of this request.\r
+ */\r
+ protected TypicalSynchronizationMetadata metadata;\r
+\r
+ /**\r
+ * Necessary for using {@link CopyPasteStrategy} and {@link PasteOperation}\r
+ * for now. Will be removed in the future once IDiagram is removed from\r
+ * PasteOperation.\r
+ */\r
+ protected IDiagram temporaryDiagram;\r
+\r
+ protected ConnectionUtil cu;\r
+\r
+ /**\r
+ * Maps source -> target connection route nodes, i.e. connectors and\r
+ * interior route nodes (route lines). Inverse mapping of {@link #t2s}.\r
+ */\r
+ protected Map<Resource, Resource> s2t;\r
+\r
+ /**\r
+ * Maps target -> source connection route nodes, i.e. connectors and\r
+ * interior route nodes (route lines). Inverse mapping of {@link #s2t}.\r
+ */\r
+ protected Map<Resource, Resource> t2s;\r
+\r
+ /**\r
+ * An auxiliary resource map for extracting the correspondences between\r
+ * originals and copied resource when diagram contents are copied from\r
+ * template to instance.\r
+ */\r
+ protected Map<Object, Object> copyMap;\r
+\r
+ final private Map<Resource, List<String>> messageLogs = new HashMap<Resource, List<String>>();\r
+ \r
+ public List<Resource> logs = new ArrayList<Resource>();\r
+\r
+ private boolean writeLog;\r
+\r
+ /**\r
+ * @param templates typical diagram templates to completely synchronize with\r
+ * their instances\r
+ */\r
+ public SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource... templates) {\r
+ this(selectedRules, templates, null, ALL, null);\r
+ }\r
+\r
+ /**\r
+ * @param templates typical diagram templates to partially synchronize with\r
+ * their instances\r
+ * @param changedElementsByDiagram see {@link #changedElementsByDiagram}\r
+ */\r
+ public SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource[] templates, MapSet<Resource, Resource> changedElementsByDiagram) {\r
+ this(selectedRules, templates, null, changedElementsByDiagram, null);\r
+ }\r
+\r
+ /**\r
+ * Return a write request that completely synchronizes the specified\r
+ * instance diagram with its template.\r
+ * \r
+ * @param instance\r
+ * @return\r
+ */\r
+ public static SyncTypicalTemplatesToInstances syncSingleInstance(Set<Resource> selectedRules, Resource instance) {\r
+ return new SyncTypicalTemplatesToInstances(selectedRules, null, new Resource[] { instance }, ALL, null);\r
+ }\r
+\r
+ /**\r
+ * @param templates typical diagram templates to synchronize with their instances\r
+ * @param instances typical diagram instances to synchronize with their templates\r
+ * @param changedElementsByDiagram see {@link #changedElementsByDiagram}\r
+ */\r
+ private SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource[] templates, Resource[] instances, MapSet<Resource, Resource> changedElementsByDiagram, IProgressMonitor monitor) {\r
+ this.selectedRules = selectedRules;\r
+ this.templates = templates;\r
+ this.instances = instances;\r
+ this.changedElementsByDiagram = changedElementsByDiagram;\r
+ this.monitor = monitor;\r
+ }\r
+\r
+ public SyncTypicalTemplatesToInstances logging(boolean writeLog) {\r
+ this.writeLog = writeLog;\r
+ return this;\r
+ }\r
+\r
+ private Resource getDiagramNameResource(ReadGraph graph, Resource diagram) throws DatabaseException {\r
+ Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);\r
+ if(composite != null) return composite;\r
+ else return diagram;\r
+ }\r
+ \r
+ private Resource getElementNameResource(ReadGraph graph, Resource element) throws DatabaseException {\r
+ Resource corr = ModelingUtils.getPossibleElementCorrespondendence(graph, element);\r
+ if(corr != null) return corr;\r
+ else return element;\r
+ }\r
+ \r
+ private List<String> getLog(ReadGraph graph, Resource diagram) throws DatabaseException {\r
+ Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(diagram));\r
+ if(indexRoot == null) throw new DatabaseException("FATAL: Diagram is not under any index root.");\r
+ List<String> log = messageLogs.get(indexRoot);\r
+ if(log == null) {\r
+ log = new ArrayList<String>();\r
+ messageLogs.put(indexRoot, log);\r
+ }\r
+ return log;\r
+ }\r
+ \r
+ private String elementName(ReadGraph graph, Resource element) throws DatabaseException {\r
+ \r
+ StringBuilder b = new StringBuilder();\r
+ b.append(safeNameAndType(graph, element));\r
+\r
+ int spaces = 60-b.length();\r
+ for(int i=0;i<spaces;i++) b.append(" ");\r
+\r
+ Resource corr = ModelingUtils.getPossibleElementCorrespondendence(graph, element);\r
+ if(corr != null) {\r
+ b.append(safeNameAndType(graph, corr));\r
+ } else {\r
+ b.append("-");\r
+ }\r
+ \r
+ return b.toString();\r
+ \r
+ }\r
+\r
+ @Override\r
+ public void perform(WriteGraph graph) throws DatabaseException {\r
+ this.L0 = Layer0.getInstance(graph);\r
+ this.STR = StructuralResource2.getInstance(graph);\r
+ this.DIA = DiagramResource.getInstance(graph);\r
+ this.MOD = ModelingResources.getInstance(graph);\r
+\r
+ this.syncCtx = GraphSynchronizationContext.getWriteInstance( graph, new CollectingModificationQueue() );\r
+ this.syncCtx.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph));\r
+\r
+ this.metadata = new TypicalSynchronizationMetadata();\r
+ this.metadata.synchronizedTypicals = new ArrayList<Resource>();\r
+\r
+ this.temporaryDiagram = Diagram.spawnNew(DiagramClass.DEFAULT);\r
+ this.temporaryDiagram.setHint(SynchronizationHints.CONTEXT, syncCtx);\r
+\r
+ this.cu = new ConnectionUtil(graph);\r
+\r
+ if (templates != null) {\r
+ // Look for typical template instances from the currently active models only.\r
+ Collection<Resource> activeModels = graph.syncRequest(new ActiveModels(Simantics.getProjectResource()));\r
+ if (!activeModels.isEmpty()) {\r
+ for (Resource template : templates) {\r
+ syncTemplate(graph, template, activeModels);\r
+ }\r
+ }\r
+ }\r
+ if (instances != null) {\r
+ for (Resource instance : instances) {\r
+ syncInstance(graph, instance);\r
+ }\r
+ }\r
+\r
+ if (writeLog) {\r
+ for(Map.Entry<Resource, List<String>> entry : messageLogs.entrySet()) {\r
+\r
+ Resource indexRoot = entry.getKey();\r
+ List<String> messageLog = entry.getValue();\r
+\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ DocumentResource DOC = DocumentResource.getInstance(graph);\r
+\r
+ Collection<Resource> libs = graph.syncRequest(new ObjectsWithType(indexRoot, L0.ConsistsOf, DOC.DocumentLibrary));\r
+ if(libs.isEmpty()) continue;\r
+\r
+ List<NamedResource> nrs = new ArrayList<NamedResource>();\r
+ for(Resource lib : libs) nrs.add(new NamedResource(NameUtils.getSafeName(graph, lib), lib));\r
+ Collections.sort(nrs, AlphanumComparator.CASE_INSENSITIVE_COMPARATOR);\r
+ Resource library = nrs.iterator().next().getResource();\r
+\r
+ CommonDBUtils.selectClusterSet(graph, library);\r
+\r
+ String text = "--- Created: " + new Date().toString() + " ---\n";\r
+ text += EString.implode(messageLog);\r
+\r
+ Resource log = graph.newResource();\r
+ graph.claim(log, L0.InstanceOf, null, DOC.PlainTextDocument);\r
+ graph.claimLiteral(log, L0.HasName, L0.String, "Typical Sync " + new Date().toString());\r
+ graph.claim(library, L0.ConsistsOf, L0.PartOf, log);\r
+ graph.claimLiteral(log, DOC.PlainTextDocument_text, L0.String, text);\r
+ logs.add(log);\r
+\r
+ }\r
+ }\r
+\r
+ if (!metadata.getTypicals().isEmpty()) {\r
+ graph.addMetadata(metadata);\r
+\r
+ // Add comment to change set.\r
+ CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+ graph.addMetadata( cm.add("Synchronized " + metadata.getTypicals().size() + " typical diagram instances (" + metadata.getTypicals() + ") with their templates.") );\r
+ }\r
+\r
+ temporaryDiagram = null;\r
+ syncCtx = null;\r
+ }\r
+\r
+ private Collection<Resource> findInstances(ReadGraph graph, Resource ofType, Collection<Resource> indexRoots) throws DatabaseException {\r
+ Instances index = graph.adapt(ofType, Instances.class);\r
+ Set<Resource> instances = new HashSet<>();\r
+ for (Resource indexRoot : indexRoots)\r
+ instances.addAll( index.find(graph, indexRoot) );\r
+ return instances;\r
+ }\r
+\r
+ private void syncTemplate(WriteGraph graph, Resource template, Collection<Resource> indexRoots) throws DatabaseException {\r
+ Resource templateType = graph.getPossibleType(template, DIA.Diagram);\r
+ if (templateType == null)\r
+ return;\r
+\r
+ Collection<Resource> instances = findInstances(graph, templateType, indexRoots);\r
+ // Do not include the template itself as it is also an instance of templateType\r
+ instances.remove(template);\r
+ if (instances.isEmpty())\r
+ return;\r
+\r
+ Set<Resource> templateElements = new THashSet<Resource>( graph.syncRequest(\r
+ new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );\r
+\r
+ try {\r
+ for (Resource instance : instances) {\r
+ this.temporaryDiagram.setHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE, instance);\r
+ syncInstance(graph, template, instance, templateElements);\r
+ }\r
+ } catch (Exception e) {\r
+ Logger.defaultLogError(e);\r
+ e.printStackTrace();\r
+ } finally {\r
+ this.temporaryDiagram.removeHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);\r
+ }\r
+ }\r
+\r
+ private void syncInstance(WriteGraph graph, Resource instance) throws DatabaseException {\r
+ Resource template = graph.getPossibleObject(instance, MOD.HasDiagramSource);\r
+ if (template == null)\r
+ return;\r
+\r
+ Set<Resource> templateElements = new THashSet<Resource>( graph.syncRequest(\r
+ new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );\r
+\r
+ try {\r
+ this.temporaryDiagram.setHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE, instance);\r
+ syncInstance(graph, template, instance, templateElements);\r
+ } finally {\r
+ this.temporaryDiagram.removeHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);\r
+ }\r
+ }\r
+ \r
+ private Resource findInstanceCounterpart(ReadGraph graph, Resource instanceDiagram, Resource templateElement) throws DatabaseException {\r
+ Map<String,Resource> children = graph.syncRequest(new UnescapedChildMapOfResource(instanceDiagram));\r
+ for(Resource child : children.values()) {\r
+ if(graph.hasStatement(child, MOD.HasElementSource, templateElement)) return child;\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ private boolean isSynchronizedConnector(ReadGraph graph, Resource templateConnection, Resource instanceConnector) throws DatabaseException {\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+ Resource instanceConnection = graph.getPossibleObject(instanceConnector, DIA.IsConnectorOf);\r
+ return graph.hasStatement(instanceConnection, MOD.HasElementSource, templateConnection);\r
+ }\r
+\r
+ /**\r
+ * Perform the following synchronization steps for the instance diagram:\r
+ * <ol>\r
+ * <li>remove such templatized elements from the instance diagram whose\r
+ * template counterpart no longer exists</li>\r
+ * <li>add elements to the instance diagram that are only in the template</li>\r
+ * <li>synchronize elements of the instance diagram that have been deemed\r
+ * changed</li>\r
+ * </ol>\r
+ * \r
+ * @param graph database write access\r
+ * @param template the synchronization source diagram\r
+ * @param instance the synchronization target diagram\r
+ * @param currentTemplateElements the set of all elements currently in the\r
+ * template diagram\r
+ * @throws DatabaseException if anything goes wrong\r
+ */\r
+ private void syncInstance(WriteGraph graph, Resource template, Resource instance, Set<Resource> currentTemplateElements) throws DatabaseException {\r
+\r
+ List<String> messageLog = getLog(graph, instance);\r
+ \r
+ messageLog.add("Synchronization of changed typical template: " + SyncTypicalTemplatesToInstances.safeNameAndType(graph, getDiagramNameResource(graph, template)));\r
+ messageLog.add("----\n\ttypical instance: " + safeNameAndType(graph, getDiagramNameResource(graph, instance)));\r
+\r
+ CommonDBUtils.selectClusterSet(graph, instance);\r
+ \r
+ // Form instance element <-> template element bijection\r
+ TypicalInfoBean typicalInfoBean = graph.syncRequest(\r
+ new ReadTypicalInfo(instance),\r
+ TransientCacheListener.<TypicalInfoBean> instance());\r
+ // Must be able to modify the typicalInfo structure,\r
+ // therefore clone the query result.\r
+ typicalInfoBean = (TypicalInfoBean) typicalInfoBean.clone();\r
+ typicalInfoBean.templateElements = currentTemplateElements;\r
+ typicalInfoBean.auxiliary = new HashMap<Object, Object>(1);\r
+\r
+ TypicalInfo info = new TypicalInfo();\r
+ info.monitor = monitor;\r
+ info.messageLog = messageLog;\r
+ info.bean = typicalInfoBean;\r
+ \r
+ // Resolve naming function for this typical instance.\r
+ Resource compositeInstance = graph.getPossibleObject(instance, MOD.DiagramToComposite);\r
+ if (compositeInstance != null) {\r
+ Function4<ReadGraph, Resource, Resource, String, String> namingFunction = TypicalUtil.getTypicalNamingFunction(graph, compositeInstance);\r
+ if (namingFunction != null)\r
+ typicalInfoBean.auxiliary.put(AuxKeys.KEY_TYPICAL_NAMING_FUNCTION, namingFunction);\r
+ }\r
+\r
+ int dSizeAbs = Math.abs(typicalInfoBean.instanceElements.size() - currentTemplateElements.size());\r
+\r
+ if(DEBUG)\r
+ System.out.println("typical <-> template mapping: " + typicalInfoBean.instanceToTemplate);\r
+\r
+ // Find elements to be removed from instance by looking for all\r
+ // instance elements that do not have a MOD.HasElementSource\r
+ // relation but have a MOD.IsTemplatized tag.\r
+ Set<Resource> instanceElementsRemovedFromTemplate = findInstanceElementsRemovedFromTemplate(\r
+ graph, info, new THashSet<Resource>(dSizeAbs));\r
+\r
+ // Find elements in template that do not yet exist in the instance\r
+ Set<Resource> templateElementsAddedToTemplate = findTemplateElementsMissingFromInstance(\r
+ graph, currentTemplateElements, info,\r
+ new THashSet<Resource>(dSizeAbs));\r
+\r
+ Set<Resource> changedTemplateElements = changedElementsByDiagram.removeValues(template);\r
+\r
+ if(DEBUG)\r
+ System.out.println("ADDED: " + templateElementsAddedToTemplate.size() + ", REMOVED: " + instanceElementsRemovedFromTemplate.size() + ", CHANGED: " + changedTemplateElements.size());\r
+ \r
+ // Validate\r
+ for(Resource templateElement : graph.getObjects(template, L0.ConsistsOf)) {\r
+ if(graph.isInstanceOf(templateElement, DIA.RouteGraphConnection)) {\r
+ for(Resource connector : graph.getObjects(templateElement, DIA.HasConnector)) {\r
+ for(Statement elementStm : graph.getStatements(connector, STR.Connects)) {\r
+ Resource otherElement = elementStm.getObject(); \r
+ if(!otherElement.equals(templateElement)) {\r
+ Resource counterPartElement = findInstanceCounterpart(graph, instance, otherElement);\r
+ if(counterPartElement != null) {\r
+ Resource diagramConnectionPoint = graph.getInverse(elementStm.getPredicate());\r
+ Resource connectionPoint = graph.getPossibleObject(diagramConnectionPoint, MOD.DiagramConnectionRelationToConnectionRelation);\r
+ if(connectionPoint != null) {\r
+ Statement stm = graph.getPossibleStatement(counterPartElement, diagramConnectionPoint);\r
+ if(stm != null) {\r
+ if(graph.isInstanceOf(connectionPoint, L0.FunctionalRelation)) {\r
+ if(!isSynchronizedConnector(graph, templateElement, stm.getObject())) {\r
+ messageLog.add("\t\tABORTED: tried to connect to an already connected terminal " + NameUtils.getSafeName(graph, counterPartElement) + " " + NameUtils.getSafeName(graph, connectionPoint));\r
+ return;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ // Perform changes\r
+ boolean changed = false;\r
+ changed |= removeElements(graph, info, instanceElementsRemovedFromTemplate);\r
+ changed |= addMissingElements(graph, info, template, instance, templateElementsAddedToTemplate);\r
+ changed |= synchronizeChangedElements(graph, info, template, instance, changedTemplateElements, templateElementsAddedToTemplate, changedElementsByDiagram == ALL);\r
+\r
+ if (changed)\r
+ metadata.addTypical(instance);\r
+ }\r
+\r
+ /**\r
+ * Add elements from template that do not yet exist in the instance.\r
+ * \r
+ * @param graph\r
+ * @param template\r
+ * @param instance\r
+ * @param elementsAddedToTemplate\r
+ * @return <code>true</code> if changes were made to the instance\r
+ * @throws DatabaseException\r
+ */\r
+ private boolean addMissingElements(WriteGraph graph, TypicalInfo typicalInfo, Resource template,\r
+ Resource instance, Set<Resource> elementsAddedToTemplate)\r
+ throws DatabaseException {\r
+ if (elementsAddedToTemplate.isEmpty())\r
+ return false;\r
+\r
+ CopyAdvisor copyAdvisor = graph.syncRequest(new Adapter<CopyAdvisor>(instance, CopyAdvisor.class));\r
+ this.temporaryDiagram.setHint(SynchronizationHints.COPY_ADVISOR, copyAdvisor);\r
+\r
+ ElementObjectAssortment assortment = new ElementObjectAssortment(graph, elementsAddedToTemplate);\r
+ if (copyMap == null)\r
+ copyMap = new THashMap<Object, Object>();\r
+ else\r
+ copyMap.clear();\r
+\r
+ if (DEBUG)\r
+ System.out.println("ADD MISSING ELEMENTS: " + assortment);\r
+\r
+ // initialCopyMap argument is needed for copying just connections\r
+ // when their end-points are not copied at the same time.\r
+\r
+ PasteOperation pasteOp = new PasteOperation(Commands.COPY,\r
+ (ICanvasContext) null, template, instance, temporaryDiagram,\r
+ assortment, false, new Point2D.Double(0, 0),\r
+ typicalInfo.bean.templateToInstance, copyMap)\r
+ .options(PasteOperation.ForceCopyReferences.INSTANCE);\r
+\r
+ new Paster(graph.getSession(), pasteOp).perform(graph);\r
+\r
+ boolean changed = false;\r
+\r
+ if(!elementsAddedToTemplate.isEmpty())\r
+ typicalInfo.messageLog.add("\tadded elements");\r
+ \r
+ for (Resource addedElement : elementsAddedToTemplate) {\r
+ Resource copyElement = (Resource) copyMap.get(addedElement);\r
+ if (copyElement != null) {\r
+ graph.claim(copyElement, MOD.IsTemplatized, MOD.IsTemplatized, copyElement);\r
+ graph.claim(copyElement, MOD.HasElementSource, MOD.ElementHasInstance, addedElement);\r
+\r
+ typicalInfo.bean.instanceElements.add(copyElement);\r
+ typicalInfo.bean.instanceToTemplate.put(copyElement, addedElement);\r
+ typicalInfo.bean.templateToInstance.put(addedElement, copyElement);\r
+\r
+ typicalInfo.messageLog.add("\t\t" + safeNameAndType(graph, copyElement));\r
+\r
+ changed = true;\r
+ }\r
+ }\r
+\r
+ ModelingResources MOD = ModelingResources.getInstance(graph);\r
+ Resource instanceComposite = graph.getPossibleObject(instance, MOD.DiagramToComposite);\r
+ List<Resource> instanceComponents = new ArrayList<Resource>(elementsAddedToTemplate.size());\r
+\r
+ // Post-process added elements after typicalInfo has been updated and\r
+ // template mapping statements are in place.\r
+ for (Resource addedElement : elementsAddedToTemplate) {\r
+ Resource copyElement = (Resource) copyMap.get(addedElement);\r
+ if (copyElement != null) {\r
+ postProcessAddedElement(graph, addedElement, copyElement, typicalInfo);\r
+\r
+ if (instanceComponents != null) {\r
+ // Gather all instance typical components for applying naming\r
+ // strategy on them.\r
+ Resource component = graph.getPossibleObject(copyElement, MOD.ElementToComponent);\r
+ if (component != null)\r
+ instanceComponents.add(component);\r
+ }\r
+ }\r
+ }\r
+\r
+ if (instanceComposite != null)\r
+ TypicalUtil.applySelectedModuleNames(graph, instanceComposite, instanceComponents);\r
+\r
+ return changed;\r
+ }\r
+\r
+ private void postProcessAddedElement(WriteGraph graph,\r
+ Resource addedTemplateElement, Resource addedInstanceElement,\r
+ TypicalInfo typicalInfo) throws DatabaseException {\r
+ if (graph.isInstanceOf(addedInstanceElement, DIA.Monitor)) {\r
+ postProcessAddedMonitor(graph, addedTemplateElement, addedInstanceElement, typicalInfo);\r
+ }\r
+ }\r
+\r
+ private void postProcessAddedMonitor(WriteGraph graph,\r
+ Resource addedTemplateMonitor, Resource addedInstanceMonitor,\r
+ TypicalInfo typicalInfo) throws DatabaseException {\r
+ Resource monitor = addedInstanceMonitor;\r
+ Resource monitoredComponent = graph.getPossibleObject(monitor, DIA.HasMonitorComponent);\r
+ if (monitoredComponent != null) {\r
+ Resource monitoredTemplateElement = graph.getPossibleObject(monitoredComponent, MOD.ComponentToElement);\r
+ if (monitoredTemplateElement != null) {\r
+ Resource monitoredInstanceElement = typicalInfo.bean.templateToInstance.get(monitoredTemplateElement);\r
+ if (monitoredInstanceElement != null) {\r
+ Resource monitoredInstanceComponent = graph.getPossibleObject(monitoredInstanceElement, MOD.ElementToComponent);\r
+ if (monitoredInstanceComponent != null) {\r
+ // Ok, the monitor refers to a component within the\r
+ // template composite. Change it to refer to the\r
+ // instance composite.\r
+ graph.deny(monitor, DIA.HasMonitorComponent);\r
+ graph.claim(monitor, DIA.HasMonitorComponent, monitoredInstanceComponent);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ private boolean removeElements(WriteGraph graph, TypicalInfo typicalInfo, Set<Resource> elementsRemovedFromTemplate) throws DatabaseException {\r
+ if (elementsRemovedFromTemplate.isEmpty())\r
+ return false;\r
+\r
+ // Remove mapped elements from instance that are removed from the template.\r
+ boolean changed = false;\r
+ \r
+ if(!elementsRemovedFromTemplate.isEmpty())\r
+ typicalInfo.messageLog.add("\tremoved elements");\r
+ \r
+ for (Resource removedElement : elementsRemovedFromTemplate) {\r
+ typicalInfo.messageLog.add("\t\t" + safeNameAndType(graph, removedElement));\r
+\r
+ RemoverUtil.remove(graph, removedElement);\r
+\r
+ typicalInfo.bean.instanceElements.remove(removedElement);\r
+ Resource template = typicalInfo.bean.instanceToTemplate.remove(removedElement);\r
+ if (template != null)\r
+ typicalInfo.bean.templateToInstance.remove(template);\r
+\r
+ changed = true;\r
+ }\r
+ return changed;\r
+ }\r
+\r
+ private Set<Resource> findTemplateElementsMissingFromInstance(\r
+ WriteGraph graph, Collection<Resource> currentTemplateElements,\r
+ TypicalInfo typicalInfo, THashSet<Resource> result)\r
+ throws DatabaseException {\r
+ for (Resource templateElement : currentTemplateElements) {\r
+ Resource instanceElement = typicalInfo.bean.templateToInstance.get(templateElement);\r
+ if (instanceElement == null) {\r
+ if(DEBUG)\r
+ System.out.println("No instance correspondence for template element " + NameUtils.getSafeName(graph, templateElement, true) + " => add");\r
+ result.add(templateElement);\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ public Set<Resource> findInstanceElementsRemovedFromTemplate(\r
+ ReadGraph graph, TypicalInfo typicalInfo,\r
+ THashSet<Resource> result) throws DatabaseException {\r
+ for (Resource instanceElement : typicalInfo.bean.instanceElements) {\r
+ if (!typicalInfo.bean.instanceToTemplate.containsKey(instanceElement)) {\r
+ if (typicalInfo.bean.isTemplatized.contains(instanceElement)) {\r
+ if(DEBUG)\r
+ System.out.println("Templatized typical instance element " + NameUtils.getSafeName(graph, instanceElement, true) + " has no correspondence in template => remove");\r
+ result.add(instanceElement);\r
+ }\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Synchronize basic visual aspects of changed elements. For all elements,\r
+ * transform and label are synchronized. Otherwise synchronization is\r
+ * type-specific for connections, flags, monitors and svg elements.\r
+ * \r
+ * @param graph\r
+ * @param typicalInfo\r
+ * @param template\r
+ * @param instance\r
+ * @param changedTemplateElements\r
+ * @param addedElements\r
+ * elements that have been added and thus need not be\r
+ * synchronized\r
+ * @param synchronizeAllElements\r
+ * @return\r
+ * @throws DatabaseException\r
+ */\r
+ private boolean synchronizeChangedElements(WriteGraph graph,\r
+ TypicalInfo typicalInfo, Resource template, Resource instance,\r
+ Collection<Resource> changedTemplateElements,\r
+ Set<Resource> addedElements,\r
+ boolean synchronizeAllElements) throws DatabaseException {\r
+\r
+ if (synchronizeAllElements) {\r
+ // For unit testing purposes.\r
+ changedTemplateElements = graph.syncRequest(new ObjectsWithType(template, L0.ConsistsOf, DIA.Element));\r
+ }\r
+\r
+ if (changedTemplateElements.isEmpty())\r
+ return false;\r
+\r
+ boolean changed = false;\r
+\r
+ typicalInfo.messageLog.add("\telement change analysis");\r
+ int analysisLogPosition = typicalInfo.messageLog.size();\r
+\r
+ for (Resource changedTemplateElement : changedTemplateElements) {\r
+ // Skip synchronization of elements that were just added and are\r
+ // thus already synchronized.\r
+ if (addedElements.contains(changedTemplateElement))\r
+ continue;\r
+\r
+ Resource instanceElement = typicalInfo.bean.templateToInstance.get(changedTemplateElement);\r
+ if (instanceElement == null) {\r
+ // There's an earlier problem in the sync process if this happens.\r
+ typicalInfo.messageLog.add("SKIPPING SYNC OF CHANGED TEMPLATE ELEMENT DUE TO MISSING INSTANCE: " + safeNameAndType(graph, getElementNameResource(graph, changedTemplateElement)));\r
+ continue;\r
+ }\r
+ \r
+ typicalInfo.messageLog.add("\t\t" + elementName(graph, changedTemplateElement));\r
+ int currentLogSize = typicalInfo.messageLog.size();\r
+\r
+ changed |= InstanceOfRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
+ changed |= NameRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
+ changed |= TransformRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
+ changed |= LabelRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
+\r
+ Collection<Resource> types = graph.getTypes(changedTemplateElement);\r
+ if (types.contains(DIA.RouteGraphConnection)) {\r
+ changed |= synchronizeConnection(graph, changedTemplateElement, instanceElement, typicalInfo);\r
+ } else if (types.contains(DIA.Flag)) {\r
+ changed |= FlagRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
+ } else if (types.contains(DIA.Monitor)) {\r
+ changed |= MonitorRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
+ } else if (types.contains(DIA.SVGElement)) {\r
+ changed |= SVGElementRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
+ }\r
+\r
+ changed |= ProfileMonitorRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
+ \r
+ for (Resource rule : graph.getObjects(changedTemplateElement, MOD.HasTypicalSynchronizationRule)) {\r
+ if(selectedRules != null && !selectedRules.contains(rule)) continue;\r
+ ITypicalSynchronizationRule r = graph.getPossibleAdapter(rule, ITypicalSynchronizationRule.class);\r
+ if (r != null)\r
+ changed |= r.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
+ }\r
+ \r
+ // Show element only if something has happened\r
+ if(currentLogSize == typicalInfo.messageLog.size())\r
+ typicalInfo.messageLog.remove(typicalInfo.messageLog.size()-1);\r
+ \r
+ }\r
+\r
+ if (s2t != null)\r
+ s2t.clear();\r
+ if (t2s != null)\r
+ t2s.clear();\r
+\r
+ // Show analysis header only if something has happened\r
+ if(analysisLogPosition == typicalInfo.messageLog.size())\r
+ typicalInfo.messageLog.remove(typicalInfo.messageLog.size()-1);\r
+\r
+ return changed;\r
+ }\r
+\r
+ /**\r
+ * Synchronizes two route graph connection topologies if and only if the\r
+ * destination connection is not attached to any node elements besides\r
+ * the ones that exist in the source. This means that connections that\r
+ * have instance-specific connections to non-template nodes are ignored\r
+ * here.\r
+ * \r
+ * @param graph\r
+ * @param sourceConnection\r
+ * @param targetConnection\r
+ * @param typicalInfo\r
+ * @return <code>true</code> if changes were made \r
+ * @throws DatabaseException\r
+ */\r
+ private boolean synchronizeConnection(WriteGraph graph, Resource sourceConnection, Resource targetConnection, TypicalInfo typicalInfo)\r
+ throws DatabaseException {\r
+\r
+ if(DEBUG)\r
+ System.out.println("connection " + NameUtils.getSafeName(graph, sourceConnection, true) + " to target connection " + NameUtils.getSafeName(graph, targetConnection, true));\r
+\r
+ boolean changed = false;\r
+\r
+ // Initialize utilities and data maps\r
+ s2t = newOrClear(s2t);\r
+ t2s = newOrClear(t2s);\r
+\r
+ if (cu == null)\r
+ cu = new ConnectionUtil(graph);\r
+\r
+ // 0.1. find mappings between source and target connection connectors\r
+ Collection<Resource> targetConnectors = graph.getObjects(targetConnection, DIA.HasConnector);\r
+ for (Resource targetConnector : targetConnectors) {\r
+ Statement toNode = cu.getConnectedComponentStatement(targetConnection, targetConnector);\r
+ if (toNode == null) {\r
+ // Corrupted target connection!\r
+ 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
+ return false;\r
+ }\r
+\r
+ // Check that the target connections does not connect to\r
+ // non-templatized elements before syncing.\r
+ if (!graph.hasStatement(toNode.getObject(), MOD.IsTemplatized))\r
+ return false;\r
+\r
+ //Resource templateNode = typicalInfo.instanceToTemplate.get(toNode.getObject());\r
+ Resource templateNode = graph.getPossibleObject(toNode.getObject(), MOD.HasElementSource);\r
+ if (templateNode != null) {\r
+ Resource isConnectedTo = graph.getPossibleInverse(toNode.getPredicate());\r
+ if (isConnectedTo != null) {\r
+ Resource templateConnector = graph.getPossibleObject(templateNode, isConnectedTo);\r
+ if (templateConnector != null) {\r
+ Resource connectionOfTemplateConnector = ConnectionUtil.tryGetConnection(graph, templateConnector);\r
+ if (sourceConnection.equals(connectionOfTemplateConnector)) {\r
+ s2t.put(templateConnector, targetConnector);\r
+ t2s.put(targetConnector, templateConnector);\r
+\r
+ if (DEBUG)\r
+ System.out.println("Mapping connector "\r
+ + NameUtils.getSafeName(graph, templateConnector, true)\r
+ + " to " + NameUtils.getSafeName(graph, targetConnector, true));\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // 0.2. find mapping between source and target route lines\r
+ Collection<Resource> sourceInteriorRouteNodes = graph.getObjects(sourceConnection, DIA.HasInteriorRouteNode);\r
+ Collection<Resource> targetInteriorRouteNodes = graph.getObjects(targetConnection, DIA.HasInteriorRouteNode);\r
+ Map<Resource, Paster.RouteLine> sourceToRouteLine = new THashMap<Resource, Paster.RouteLine>();\r
+ Map<Resource, Paster.RouteLine> targetToRouteLine = new THashMap<Resource, Paster.RouteLine>();\r
+\r
+ for (Resource source : sourceInteriorRouteNodes)\r
+ sourceToRouteLine.put(source, Paster.readRouteLine(graph, source));\r
+ for (Resource target : targetInteriorRouteNodes)\r
+ targetToRouteLine.put(target, Paster.readRouteLine(graph, target));\r
+\r
+ nextSourceLine:\r
+ for (Iterator<Map.Entry<Resource, Paster.RouteLine>> sourceIt = sourceToRouteLine.entrySet().iterator(); !targetToRouteLine.isEmpty() && sourceIt.hasNext();) {\r
+ Map.Entry<Resource, Paster.RouteLine> sourceEntry = sourceIt.next();\r
+ Paster.RouteLine sourceLine = sourceEntry.getValue();\r
+ for (Iterator<Map.Entry<Resource, Paster.RouteLine>> targetIt = targetToRouteLine.entrySet().iterator(); targetIt.hasNext();) {\r
+ Map.Entry<Resource, Paster.RouteLine> targetEntry = targetIt.next();\r
+ if (sourceLine.equals(targetEntry.getValue())) {\r
+ s2t.put(sourceEntry.getKey(), targetEntry.getKey());\r
+ t2s.put(targetEntry.getKey(), sourceEntry.getKey());\r
+ sourceIt.remove();\r
+ targetIt.remove();\r
+\r
+ if (DEBUG)\r
+ System.out.println("Mapping routeline "\r
+ + NameUtils.getSafeName(graph, sourceEntry.getKey(), true)\r
+ + " - " + sourceEntry.getValue()\r
+ + " to " + NameUtils.getSafeName(graph, targetEntry.getKey(), true)\r
+ + " - " + targetEntry.getValue());\r
+\r
+ continue nextSourceLine;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (DEBUG) {\r
+ System.out.println("Take 1: Source to target route nodes map : " + s2t);\r
+ System.out.println("Take 1: Target to source route nodes map : " + t2s);\r
+ }\r
+\r
+ // 1.1 remove excess connectors\r
+ for (Resource targetConnector : targetConnectors) {\r
+ if (!t2s.containsKey(targetConnector)) {\r
+ typicalInfo.messageLog.add("\t\t\tremove excess connector from target connection: " + NameUtils.getSafeName(graph, targetConnector));\r
+ cu.removeConnectionPart(targetConnector);\r
+ changed = true;\r
+ }\r
+ }\r
+\r
+ // 1.2 add missing connectors to target\r
+ Collection<Resource> sourceConnectors = graph.getObjects(sourceConnection, DIA.HasConnector);\r
+ for (Resource sourceConnector : sourceConnectors) {\r
+ if (!s2t.containsKey(sourceConnector)) {\r
+ Statement sourceIsConnectorOf = graph.getSingleStatement(sourceConnector, DIA.IsConnectorOf);\r
+ Statement connects = cu.getConnectedComponentStatement(sourceConnection, sourceConnector);\r
+ if (connects == null) {\r
+ // TODO: serious error!\r
+ throw new DatabaseException("ERROR: connector is astray, i.e. not connected to a node element: " + safeNameAndType(graph, sourceConnector));\r
+ }\r
+ Resource connectsInstanceElement = typicalInfo.bean.templateToInstance.get(connects.getObject());\r
+ if (connectsInstanceElement == null) {\r
+ // TODO: serious error!\r
+ throw new DatabaseException("ERROR: could not find instance element to which template element " + safeNameAndType(graph, connects.getObject()) + " is connected to");\r
+ }\r
+ Resource hasConnector = graph.getInverse(sourceIsConnectorOf.getPredicate());\r
+\r
+ Resource newTargetConnector = cu.newConnector(targetConnection, hasConnector);\r
+ graph.claim(newTargetConnector, connects.getPredicate(), connectsInstanceElement);\r
+ changed = true;\r
+\r
+ s2t.put(sourceConnector, newTargetConnector);\r
+ t2s.put(newTargetConnector, sourceConnector);\r
+\r
+ 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
+ }\r
+ }\r
+\r
+ // 2. sync route lines and their connectivity:\r
+ // 2.1. assign correspondences in target for each source route line\r
+ // by reusing excess routelines in target and by creating new\r
+ // route lines.\r
+\r
+ Resource[] targetRouteLines = targetToRouteLine.keySet().toArray(Resource.NONE);\r
+ int targetRouteLine = targetRouteLines.length - 1;\r
+\r
+ for (Iterator<Map.Entry<Resource, Paster.RouteLine>> sourceIt = sourceToRouteLine.entrySet().iterator(); sourceIt.hasNext();) {\r
+ Map.Entry<Resource, Paster.RouteLine> sourceEntry = sourceIt.next();\r
+ Resource source = sourceEntry.getKey();\r
+ Paster.RouteLine sourceLine = sourceEntry.getValue();\r
+\r
+ typicalInfo.messageLog.add("\t\t\tassign an instance-side routeline complement for " + NameUtils.getSafeName(graph, source, true) + " - " + sourceLine);\r
+\r
+ // Assign target route line for source\r
+ Resource target = null;\r
+ if (targetRouteLine < 0) {\r
+ // by creating new route lines\r
+ target = cu.newRouteLine(targetConnection, sourceLine.getPosition(), sourceLine.isHorizontal());\r
+ typicalInfo.messageLog.add("\t\t\tcreate new route line " + NameUtils.getSafeName(graph, target));\r
+ changed = true;\r
+ } else {\r
+ // by reusing existing route line\r
+ target = targetRouteLines[targetRouteLine--];\r
+ copyRouteLine(graph, source, target);\r
+ cu.disconnectFromAllRouteNodes(target);\r
+ typicalInfo.messageLog.add("\t\t\treused existing route line " + NameUtils.getSafeName(graph, target));\r
+ changed = true;\r
+ }\r
+ s2t.put(source, target);\r
+ t2s.put(target, source);\r
+\r
+ typicalInfo.messageLog.add("\t\t\tmapped source route line " + NameUtils.getSafeName(graph, source) + " to target route line " + NameUtils.getSafeName(graph, target));\r
+ }\r
+\r
+ if (targetRouteLine >= 0) {\r
+ typicalInfo.messageLog.add("\t\t\tremove excess route lines (" + (targetRouteLine + 1) + ") from target connection");\r
+ for (; targetRouteLine >= 0; targetRouteLine--) {\r
+ typicalInfo.messageLog.add("\t\t\t\tremove excess route line: " + NameUtils.getSafeName(graph, targetRouteLines[targetRouteLine], true));\r
+ cu.removeConnectionPart(targetRouteLines[targetRouteLine]);\r
+ }\r
+ }\r
+\r
+ if (DEBUG) {\r
+ System.out.println("Take 2: Source to target route nodes map : " + s2t);\r
+ System.out.println("Take 2: Target to source route nodes map : " + t2s);\r
+ }\r
+\r
+ // 2.2. Synchronize target connection topology (DIA.AreConnected)\r
+ changed |= connectRouteNodes(graph, typicalInfo, sourceInteriorRouteNodes);\r
+ changed |= connectRouteNodes(graph, typicalInfo, sourceConnectors);\r
+\r
+ // 3. remove excess routelines & connectors from target connection\r
+ changed |= cu.removeExtraInteriorRouteNodes(targetConnection) > 0;\r
+ changed |= cu.removeUnusedConnectors(targetConnection) > 0;\r
+\r
+ return changed;\r
+ }\r
+\r
+ private boolean connectRouteNodes(WriteGraph graph, TypicalInfo typicalInfo, Collection<Resource> sourceRouteNodes) throws DatabaseException {\r
+ boolean changed = false;\r
+ for (Resource src : sourceRouteNodes) {\r
+ Resource dst = s2t.get(src);\r
+ if (dst == null) {\r
+ throw new DatabaseException("TARGET ROUTE NODE == NULL FOR SRC: " + NameUtils.getSafeName(graph, src));\r
+ }\r
+\r
+ Collection<Resource> connectedToSrcs = graph.getObjects(src, DIA.AreConnected);\r
+ Collection<Resource> connectedToDsts = graph.getObjects(dst, DIA.AreConnected);\r
+\r
+ // Remove excess statements\r
+ for (Resource connectedToDst : connectedToDsts) {\r
+ Resource connectedToSrc = t2s.get(connectedToDst);\r
+ if (connectedToSrc == null) {\r
+ throw new DatabaseException("CONNECTED TO SRC == NULL FOR DST: " + NameUtils.getSafeName(graph, connectedToDst));\r
+ }\r
+ if (connectedToSrc == null || !graph.hasStatement(src, DIA.AreConnected, connectedToSrc)) {\r
+ graph.deny(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);\r
+ changed = true;\r
+ typicalInfo.messageLog.add("\t\t\tdisconnected route nodes (" + NameUtils.getSafeName(graph, dst) + ", " + NameUtils.getSafeName(graph, connectedToDst) + ")");\r
+ }\r
+ }\r
+\r
+ // Add necessary statements\r
+ for (Resource connectedToSrc : connectedToSrcs) {\r
+ Resource connectedToDst = s2t.get(connectedToSrc);\r
+ if (connectedToDst == null) {\r
+ throw new DatabaseException("CONNECTED TO DST == NULL FOR SRC: " + NameUtils.getSafeName(graph, connectedToSrc));\r
+ }\r
+ if (!graph.hasStatement(dst, DIA.AreConnected, connectedToDst)) {\r
+ graph.claim(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);\r
+ changed = true;\r
+ typicalInfo.messageLog.add("\t\t\tconnected route nodes (" + NameUtils.getSafeName(graph, dst) + ", " + NameUtils.getSafeName(graph, connectedToDst) + ")");\r
+ }\r
+ }\r
+ }\r
+ return changed;\r
+ }\r
+\r
+ private void copyRouteLine(WriteGraph graph, Resource src, Resource tgt) throws DatabaseException {\r
+ Double pos = graph.getPossibleRelatedValue(src, DIA.HasPosition, Bindings.DOUBLE);\r
+ Boolean hor = graph.getPossibleRelatedValue(src, DIA.IsHorizontal, Bindings.BOOLEAN);\r
+ if (pos == null)\r
+ pos = 0.0;\r
+ if (hor == null)\r
+ hor = Boolean.TRUE;\r
+ graph.claimLiteral(tgt, DIA.HasPosition, L0.Double, pos, Bindings.DOUBLE);\r
+ graph.claimLiteral(tgt, DIA.IsHorizontal, L0.Boolean, hor, Bindings.BOOLEAN);\r
+ }\r
+\r
+ private static String safeNameAndType(ReadGraph graph, Resource r) throws DatabaseException {\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append(NameUtils.getSafeName(graph, r, true));\r
+ sb.append(" : [");\r
+ boolean first = true;\r
+ for (Resource type : graph.getPrincipalTypes(r)) {\r
+ if (!first)\r
+ sb.append(",");\r
+ first = false;\r
+ sb.append(NameUtils.getSafeName(graph, type, true));\r
+ }\r
+ sb.append("]");\r
+ return sb.toString();\r
+ }\r
+\r
+ private static <K, V> Map<K, V> newOrClear(Map<K, V> current) {\r
+ if (current == null)\r
+ return new THashMap<K, V>();\r
+ current.clear();\r
+ return current;\r
+ }\r
+\r
+}
\ No newline at end of file