InputStream returns -1 on EOF instead of throwing IOException
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / typicals / SyncTypicalTemplatesToInstances.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2012 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.modeling.typicals;\r
13 \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
25 \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
85 \r
86 import gnu.trove.map.hash.THashMap;\r
87 import gnu.trove.set.hash.THashSet;\r
88 \r
89 /**\r
90  * A write request that synchronizes typical master templates and their\r
91  * instances as specified.\r
92  * \r
93  * <p>\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
98  * \r
99  * @author Tuukka Lehtonen\r
100  * \r
101  * @see ReadTypicalInfo\r
102  * @see TypicalInfo\r
103  * @see TypicalSynchronizationMetadata\r
104  */\r
105 public class SyncTypicalTemplatesToInstances extends WriteRequest {\r
106     private static final Logger LOGGER = LoggerFactory.getLogger(SyncTypicalTemplatesToInstances.class);\r
107 \r
108     /**\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
112      * \r
113      * This is useful for forcing complete synchronization and unit testing. \r
114      */\r
115     public static final EmptyMapSet ALL = EmptyMapSet.INSTANCE;\r
116 \r
117     public static class EmptyMapSet extends MapSet<Resource, Resource> {\r
118 \r
119         public static final EmptyMapSet INSTANCE = new EmptyMapSet();\r
120 \r
121         public EmptyMapSet() {\r
122             this.sets = Collections.emptyMap();\r
123         }\r
124 \r
125         @Override\r
126         protected Set<Resource> getOrCreateSet(Resource key) {\r
127             throw new UnsupportedOperationException("immutable constant instance");\r
128         }\r
129 \r
130     };\r
131 \r
132     protected static final boolean           DEBUG = false;\r
133 \r
134     // Input\r
135 \r
136     final private IProgressMonitor monitor;\r
137     /**\r
138      * Typical diagram rules to apply\r
139      */\r
140     protected Set<Resource>                                     selectedRules;\r
141     \r
142     /**\r
143      * Typical diagram templates to synchronize with their instances.\r
144      */\r
145     protected Resource[]                     templates;\r
146 \r
147     /**\r
148      * Typical diagram instances to synchronize with their templates.\r
149      */\r
150     protected Resource[]                     instances;\r
151 \r
152     /**\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
157      * \r
158      * If the value is {@link #ALL}, all elements of the template shall be fully\r
159      * synchronized.\r
160      */\r
161     protected MapSet<Resource, Resource>     changedElementsByDiagram;\r
162 \r
163     // Temporary data\r
164 \r
165     protected Layer0                         L0;\r
166     protected StructuralResource2            STR;\r
167     protected DiagramResource                DIA;\r
168     protected ModelingResources              MOD;\r
169 \r
170     /**\r
171      * Needed for using {@link Paster} in\r
172      * {@link #addMissingElements(WriteGraph, TypicalInfo, Resource, Resource, Set)}\r
173      */\r
174     protected GraphSynchronizationContext    syncCtx;\r
175 \r
176     /**\r
177      * For collecting commit metadata during the processing of this request.\r
178      */\r
179     protected TypicalSynchronizationMetadata metadata;\r
180 \r
181     /**\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
184      * PasteOperation.\r
185      */\r
186     protected IDiagram                       temporaryDiagram;\r
187 \r
188     protected ConnectionUtil                 cu;\r
189 \r
190     /**\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
193      */\r
194     protected Map<Resource, Resource>        s2t;\r
195 \r
196     /**\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
199      */\r
200     protected Map<Resource, Resource>        t2s;\r
201 \r
202     /**\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
206      */\r
207     protected Map<Object, Object>            copyMap;\r
208 \r
209     final private Map<Resource, List<String>> messageLogs = new HashMap<Resource, List<String>>();\r
210     \r
211     public List<Resource> logs = new ArrayList<Resource>();\r
212 \r
213         private boolean writeLog;\r
214 \r
215     /**\r
216      * For SCL API.\r
217      * \r
218      * @param graph\r
219      * @param selectedRules\r
220      * @param templates\r
221      * @param instances\r
222      * @throws DatabaseException\r
223      */\r
224     public static void syncTypicals(WriteGraph graph, boolean log, List<Resource> templates, List<Resource> instances) throws DatabaseException {\r
225         graph.syncRequest(\r
226                 new SyncTypicalTemplatesToInstances(\r
227                         null,\r
228                         templates.toArray(Resource.NONE),\r
229                         instances.toArray(Resource.NONE),\r
230                         ALL,\r
231                         null)\r
232                 .logging(log));\r
233     }\r
234 \r
235     /**\r
236      * @param templates typical diagram templates to completely synchronize with\r
237      *        their instances\r
238      */\r
239     public SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource... templates) {\r
240         this(selectedRules, templates, null, ALL, null);\r
241     }\r
242 \r
243     /**\r
244      * @param templates typical diagram templates to partially synchronize with\r
245      *        their instances\r
246      * @param changedElementsByDiagram see {@link #changedElementsByDiagram}\r
247      */\r
248     public SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource[] templates, MapSet<Resource, Resource> changedElementsByDiagram) {\r
249         this(selectedRules, templates, null, changedElementsByDiagram, null);\r
250     }\r
251 \r
252     /**\r
253      * Return a write request that completely synchronizes the specified\r
254      * instance diagram with its template.\r
255      * \r
256      * @param instance\r
257      * @return\r
258      */\r
259     public static SyncTypicalTemplatesToInstances syncSingleInstance(Set<Resource> selectedRules, Resource instance) {\r
260         return new SyncTypicalTemplatesToInstances(selectedRules, null, new Resource[] { instance }, ALL, null);\r
261     }\r
262 \r
263     /**\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
267      */\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
274     }\r
275 \r
276     public SyncTypicalTemplatesToInstances logging(boolean writeLog) {\r
277         this.writeLog = writeLog;\r
278         return this;\r
279     }\r
280 \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
285     }\r
286     \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
291     }\r
292     \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
297         if(log == null) {\r
298                 log = new ArrayList<String>();\r
299                 messageLogs.put(indexRoot, log);\r
300         }\r
301         return log;\r
302     }\r
303     \r
304     private String elementName(ReadGraph graph, Resource element) throws DatabaseException {\r
305         \r
306         StringBuilder b = new StringBuilder();\r
307         b.append(safeNameAndType(graph, element));\r
308 \r
309         int spaces = 60-b.length();\r
310                 for(int i=0;i<spaces;i++) b.append(" ");\r
311 \r
312         Resource corr = ModelingUtils.getPossibleElementCorrespondendence(graph, element);\r
313         if(corr != null) {\r
314                 b.append(safeNameAndType(graph, corr));\r
315         } else {\r
316                 b.append("-");\r
317         }\r
318         \r
319         return b.toString();\r
320         \r
321     }\r
322 \r
323     @Override\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
329 \r
330         this.syncCtx = GraphSynchronizationContext.getWriteInstance( graph, new CollectingModificationQueue() );\r
331         this.syncCtx.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph));\r
332 \r
333         this.metadata = new TypicalSynchronizationMetadata();\r
334         this.metadata.synchronizedTypicals = new ArrayList<Resource>();\r
335 \r
336         this.temporaryDiagram = Diagram.spawnNew(DiagramClass.DEFAULT);\r
337         this.temporaryDiagram.setHint(SynchronizationHints.CONTEXT, syncCtx);\r
338 \r
339         this.cu = new ConnectionUtil(graph);\r
340 \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
347                 }\r
348             }\r
349         }\r
350         if (instances != null) {\r
351             for (Resource instance : instances) {\r
352                 syncInstance(graph, instance);\r
353             }\r
354         }\r
355 \r
356         if (writeLog) {\r
357             for(Map.Entry<Resource, List<String>> entry : messageLogs.entrySet()) {\r
358 \r
359                 Resource indexRoot = entry.getKey();\r
360                 List<String> messageLog = entry.getValue();\r
361 \r
362                 Layer0 L0 = Layer0.getInstance(graph);\r
363                 DocumentResource DOC = DocumentResource.getInstance(graph);\r
364 \r
365                 Collection<Resource> libs = graph.syncRequest(new ObjectsWithType(indexRoot, L0.ConsistsOf, DOC.DocumentLibrary));\r
366                 if(libs.isEmpty()) continue;\r
367 \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
372 \r
373                 CommonDBUtils.selectClusterSet(graph, library);\r
374 \r
375                 String text = "--- Created: " + new Date().toString() + " ---\n";\r
376                 text += EString.implode(messageLog);\r
377 \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
383                 logs.add(log);\r
384 \r
385             }\r
386         }\r
387 \r
388         if (!metadata.getTypicals().isEmpty()) {\r
389             graph.addMetadata(metadata);\r
390 \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
394         }\r
395 \r
396         temporaryDiagram = null;\r
397         syncCtx = null;\r
398     }\r
399 \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
405         return instances;\r
406     }\r
407 \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
411             return;\r
412 \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
417             return;\r
418 \r
419         Set<Resource> templateElements = new THashSet<Resource>( graph.syncRequest(\r
420                 new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );\r
421 \r
422         try {\r
423             for (Resource instance : instances) {\r
424                 this.temporaryDiagram.setHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE, instance);\r
425                 syncInstance(graph, template, instance, templateElements);\r
426             }\r
427         } catch (Exception e) {\r
428             LOGGER.error("Template synchronization failed.", e);\r
429         } finally {\r
430             this.temporaryDiagram.removeHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);\r
431         }\r
432     }\r
433 \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
437             return;\r
438 \r
439         Set<Resource> templateElements = new THashSet<Resource>( graph.syncRequest(\r
440                 new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );\r
441 \r
442         try {\r
443             this.temporaryDiagram.setHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE, instance);\r
444             syncInstance(graph, template, instance, templateElements);\r
445         } finally {\r
446             this.temporaryDiagram.removeHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);\r
447         }\r
448     }\r
449     \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
454         }\r
455         return null;\r
456     }\r
457     \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
466     }\r
467 \r
468     /**\r
469      * Perform the following synchronization steps for the instance diagram:\r
470      * <ol>\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
475      * changed</li>\r
476      * </ol>\r
477      * \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
482      *        template diagram\r
483      * @throws DatabaseException if anything goes wrong\r
484      */\r
485     private void syncInstance(WriteGraph graph, Resource template, Resource instance, Set<Resource> currentTemplateElements) throws DatabaseException {\r
486 \r
487         List<String> messageLog = getLog(graph, instance);\r
488         \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
491 \r
492         CommonDBUtils.selectClusterSet(graph, instance);\r
493         \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
503 \r
504         TypicalInfo info = new TypicalInfo();\r
505         info.monitor = monitor;\r
506         info.messageLog = messageLog;\r
507         info.bean = typicalInfoBean;\r
508         \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
515         }\r
516 \r
517         int dSizeAbs = Math.abs(typicalInfoBean.instanceElements.size() - currentTemplateElements.size());\r
518 \r
519         if(DEBUG)\r
520                 System.out.println("typical <-> template mapping: " + typicalInfoBean.instanceToTemplate);\r
521 \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
527 \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
532 \r
533         Set<Resource> changedTemplateElements = changedElementsByDiagram.removeValues(template);\r
534 \r
535         if(DEBUG)\r
536                 System.out.println("ADDED: " + templateElementsAddedToTemplate.size() + ", REMOVED: " + instanceElementsRemovedFromTemplate.size() + ", CHANGED: " + changedTemplateElements.size());\r
537         \r
538         // Validate\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
551                                         if(stm != null) {\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
555                                                                 return;\r
556                                                         }\r
557                                                 }\r
558                                                 }\r
559                                                 }\r
560                                 }\r
561                                         }\r
562                                 }\r
563                         }\r
564                 }\r
565         }\r
566         \r
567         // Perform changes\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
573 \r
574         if (changed)\r
575             metadata.addTypical(instance);\r
576     }\r
577 \r
578     /**\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
581      * \r
582      * @param graph\r
583      * @param typicalInfo\r
584      * @param template\r
585      * @param instance\r
586      * @return if any changes were made. \r
587      * @throws DatabaseException\r
588      */\r
589     private boolean synchronizeDiagramChanges(\r
590             WriteGraph graph,\r
591             TypicalInfo typicalInfo,\r
592             Resource template,\r
593             Resource instance)\r
594                     throws DatabaseException\r
595     {\r
596         boolean changed = false;\r
597         for (Resource rule : graph.getObjects(template, MOD.HasTypicalSynchronizationRule)) {\r
598             if (selectedRules != null && !selectedRules.contains(rule))\r
599                 continue;\r
600             ITypicalSynchronizationRule r = graph.getPossibleAdapter(rule, ITypicalSynchronizationRule.class);\r
601             if (r != null)\r
602                 changed |= r.synchronize(graph, template, instance, typicalInfo);\r
603         }\r
604         return changed;\r
605     }\r
606 \r
607     /**\r
608      * Add elements from template that do not yet exist in the instance.\r
609      * \r
610      * @param graph\r
611      * @param template\r
612      * @param instance\r
613      * @param elementsAddedToTemplate\r
614      * @return <code>true</code> if changes were made to the instance\r
615      * @throws DatabaseException\r
616      */\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
621             return false;\r
622 \r
623         CopyAdvisor copyAdvisor = graph.syncRequest(new Adapter<CopyAdvisor>(instance, CopyAdvisor.class));\r
624         this.temporaryDiagram.setHint(SynchronizationHints.COPY_ADVISOR, copyAdvisor);\r
625 \r
626         ElementObjectAssortment assortment = new ElementObjectAssortment(graph, elementsAddedToTemplate);\r
627         if (copyMap == null)\r
628             copyMap = new THashMap<Object, Object>();\r
629         else\r
630             copyMap.clear();\r
631 \r
632         if (DEBUG)\r
633             System.out.println("ADD MISSING ELEMENTS: " + assortment);\r
634 \r
635         // initialCopyMap argument is needed for copying just connections\r
636         // when their end-points are not copied at the same time.\r
637 \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
643 \r
644         new Paster(graph.getSession(), pasteOp).perform(graph);\r
645 \r
646         boolean changed = false;\r
647 \r
648         if(!elementsAddedToTemplate.isEmpty())\r
649                 typicalInfo.messageLog.add("\tadded elements");\r
650         \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
656 \r
657                 typicalInfo.bean.instanceElements.add(copyElement);\r
658                 typicalInfo.bean.instanceToTemplate.put(copyElement, addedElement);\r
659                 typicalInfo.bean.templateToInstance.put(addedElement, copyElement);\r
660 \r
661                 typicalInfo.messageLog.add("\t\t" + safeNameAndType(graph, copyElement));\r
662 \r
663                 changed = true;\r
664             }\r
665         }\r
666 \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
670 \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
677 \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
684                 }\r
685             }\r
686         }\r
687 \r
688         if (instanceComposite != null)\r
689             TypicalUtil.applySelectedModuleNames(graph, instanceComposite, instanceComponents);\r
690 \r
691         return changed;\r
692     }\r
693 \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
699         }\r
700     }\r
701 \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
719                     }\r
720                 }\r
721             }\r
722         }\r
723     }\r
724 \r
725     private boolean removeElements(WriteGraph graph, TypicalInfo typicalInfo, Set<Resource> elementsRemovedFromTemplate) throws DatabaseException {\r
726         if (elementsRemovedFromTemplate.isEmpty())\r
727             return false;\r
728 \r
729         // Remove mapped elements from instance that are removed from the template.\r
730         boolean changed = false;\r
731         \r
732         if(!elementsRemovedFromTemplate.isEmpty())\r
733                 typicalInfo.messageLog.add("\tremoved elements");\r
734         \r
735         for (Resource removedElement : elementsRemovedFromTemplate) {\r
736                 typicalInfo.messageLog.add("\t\t" + safeNameAndType(graph, removedElement));\r
737 \r
738             RemoverUtil.remove(graph, removedElement);\r
739 \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
744 \r
745             changed = true;\r
746         }\r
747         return changed;\r
748     }\r
749 \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
757                 if(DEBUG)\r
758                         System.out.println("No instance correspondence for template element " + NameUtils.getSafeName(graph, templateElement, true) + " => add");\r
759                 result.add(templateElement);\r
760             }\r
761         }\r
762         return result;\r
763     }\r
764 \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
771                         if(DEBUG)\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
774                 }\r
775             }\r
776         }\r
777         return result;\r
778     }\r
779 \r
780     /**\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
784      * \r
785      * @param graph\r
786      * @param typicalInfo\r
787      * @param template\r
788      * @param instance\r
789      * @param changedTemplateElements\r
790      * @param addedElements\r
791      *            elements that have been added and thus need not be\r
792      *            synchronized\r
793      * @param synchronizeAllElements\r
794      * @return\r
795      * @throws DatabaseException\r
796      */\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
802 \r
803         if (synchronizeAllElements) {\r
804             // For unit testing purposes.\r
805             changedTemplateElements = graph.syncRequest(new ObjectsWithType(template, L0.ConsistsOf, DIA.Element));\r
806         }\r
807 \r
808         if (changedTemplateElements.isEmpty())\r
809             return false;\r
810 \r
811         boolean changed = false;\r
812 \r
813         typicalInfo.messageLog.add("\telement change analysis");\r
814         int analysisLogPosition = typicalInfo.messageLog.size();\r
815 \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
820                 continue;\r
821 \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
826                 continue;\r
827             }\r
828             \r
829             typicalInfo.messageLog.add("\t\t" + elementName(graph, changedTemplateElement));\r
830             int currentLogSize = typicalInfo.messageLog.size();\r
831 \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
836 \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
846             }\r
847 \r
848             changed |= ProfileMonitorRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
849             \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
853                 if (r != null)\r
854                     changed |= r.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
855             }\r
856             \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
860             \r
861         }\r
862 \r
863         if (s2t != null)\r
864             s2t.clear();\r
865         if (t2s != null)\r
866             t2s.clear();\r
867 \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
871 \r
872         return changed;\r
873     }\r
874 \r
875     /**\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
880      * here.\r
881      * \r
882      * @param graph\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
888      */\r
889     private boolean synchronizeConnection(WriteGraph graph, Resource sourceConnection, Resource targetConnection, TypicalInfo typicalInfo)\r
890             throws DatabaseException {\r
891 \r
892         if(DEBUG)\r
893                 System.out.println("connection " + NameUtils.getSafeName(graph, sourceConnection, true) + " to target connection " + NameUtils.getSafeName(graph, targetConnection, true));\r
894 \r
895         boolean changed = false;\r
896 \r
897         // Initialize utilities and data maps\r
898         s2t = newOrClear(s2t);\r
899         t2s = newOrClear(t2s);\r
900 \r
901         if (cu == null)\r
902             cu = new ConnectionUtil(graph);\r
903 \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
911                 return false;\r
912             }\r
913 \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
917                 return false;\r
918 \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
930 \r
931                             if (DEBUG)\r
932                                 System.out.println("Mapping connector "\r
933                                         + NameUtils.getSafeName(graph, templateConnector, true)\r
934                                         + " to " + NameUtils.getSafeName(graph, targetConnector, true));\r
935                         }\r
936                     }\r
937                 }\r
938             }\r
939         }\r
940 \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
946 \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
951 \r
952         nextSourceLine:\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
961                         sourceIt.remove();\r
962                         targetIt.remove();\r
963 \r
964                         if (DEBUG)\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
970 \r
971                         continue nextSourceLine;\r
972                     }\r
973                 }\r
974             }\r
975 \r
976         if (DEBUG) {\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
979         }\r
980 \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
986                 changed = true;\r
987             }\r
988         }\r
989 \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
999                 }\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
1004                 }\r
1005                 Resource hasConnector = graph.getInverse(sourceIsConnectorOf.getPredicate());\r
1006 \r
1007                 Resource newTargetConnector = cu.newConnector(targetConnection, hasConnector);\r
1008                 graph.claim(newTargetConnector, connects.getPredicate(), connectsInstanceElement);\r
1009                 changed = true;\r
1010 \r
1011                 s2t.put(sourceConnector, newTargetConnector);\r
1012                 t2s.put(newTargetConnector, sourceConnector);\r
1013 \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
1015             }\r
1016         }\r
1017 \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
1021         //      route lines.\r
1022 \r
1023         Resource[] targetRouteLines = targetToRouteLine.keySet().toArray(Resource.NONE);\r
1024         int targetRouteLine = targetRouteLines.length - 1;\r
1025 \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
1030 \r
1031             typicalInfo.messageLog.add("\t\t\tassign an instance-side routeline complement for " + NameUtils.getSafeName(graph, source, true) + " - " + sourceLine);\r
1032 \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
1039                 changed = true;\r
1040             } else {\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
1046                 changed = true;\r
1047             }\r
1048             s2t.put(source, target);\r
1049             t2s.put(target, source);\r
1050 \r
1051             typicalInfo.messageLog.add("\t\t\tmapped source route line " + NameUtils.getSafeName(graph, source) + " to target route line " + NameUtils.getSafeName(graph, target));\r
1052         }\r
1053 \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
1059             }\r
1060         }\r
1061 \r
1062         if (DEBUG) {\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
1065         }\r
1066 \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
1070 \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
1074 \r
1075         return changed;\r
1076     }\r
1077 \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
1084             }\r
1085 \r
1086             Collection<Resource> connectedToSrcs = graph.getObjects(src, DIA.AreConnected);\r
1087             Collection<Resource> connectedToDsts = graph.getObjects(dst, DIA.AreConnected);\r
1088 \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
1094                 }\r
1095                 if (connectedToSrc == null || !graph.hasStatement(src, DIA.AreConnected, connectedToSrc)) {\r
1096                     graph.deny(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);\r
1097                     changed = true;\r
1098                     typicalInfo.messageLog.add("\t\t\tdisconnected route nodes (" + NameUtils.getSafeName(graph, dst) + ", " + NameUtils.getSafeName(graph, connectedToDst) + ")");\r
1099                 }\r
1100             }\r
1101 \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
1107                 }\r
1108                 if (!graph.hasStatement(dst, DIA.AreConnected, connectedToDst)) {\r
1109                     graph.claim(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);\r
1110                     changed = true;\r
1111                     typicalInfo.messageLog.add("\t\t\tconnected route nodes (" + NameUtils.getSafeName(graph, dst) + ", " + NameUtils.getSafeName(graph, connectedToDst) + ")");\r
1112                 }\r
1113             }\r
1114         }\r
1115         return changed;\r
1116     }\r
1117 \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
1121         if (pos == null)\r
1122             pos = 0.0;\r
1123         if (hor == null)\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
1127     }\r
1128 \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
1135             if (!first)\r
1136                 sb.append(",");\r
1137             first = false;\r
1138             sb.append(NameUtils.getSafeName(graph, type, true));\r
1139         }\r
1140         sb.append("]");\r
1141         return sb.toString();\r
1142     }\r
1143 \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
1147         current.clear();\r
1148         return current;\r
1149     }\r
1150 \r
1151 }