]> gerrit.simantics Code Review - simantics/platform.git/blob
55f5aef3dabf1869b305b5376d14633abbcf9850
[simantics/platform.git] /
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 gnu.trove.map.hash.THashMap;\r
15 import gnu.trove.set.hash.THashSet;\r
16 \r
17 import java.awt.geom.Point2D;\r
18 import java.util.ArrayList;\r
19 import java.util.Collection;\r
20 import java.util.Collections;\r
21 import java.util.Date;\r
22 import java.util.HashMap;\r
23 import java.util.HashSet;\r
24 import java.util.Iterator;\r
25 import java.util.List;\r
26 import java.util.Map;\r
27 import java.util.Set;\r
28 \r
29 import org.eclipse.core.runtime.IProgressMonitor;\r
30 import org.simantics.Logger;\r
31 import org.simantics.Simantics;\r
32 import org.simantics.databoard.Bindings;\r
33 import org.simantics.db.ReadGraph;\r
34 import org.simantics.db.Resource;\r
35 import org.simantics.db.Statement;\r
36 import org.simantics.db.WriteGraph;\r
37 import org.simantics.db.common.CommentMetadata;\r
38 import org.simantics.db.common.NamedResource;\r
39 import org.simantics.db.common.primitiverequest.Adapter;\r
40 import org.simantics.db.common.procedure.adapter.TransientCacheListener;\r
41 import org.simantics.db.common.request.ObjectsWithType;\r
42 import org.simantics.db.common.request.PossibleIndexRoot;\r
43 import org.simantics.db.common.request.WriteRequest;\r
44 import org.simantics.db.common.uri.UnescapedChildMapOfResource;\r
45 import org.simantics.db.common.utils.CommonDBUtils;\r
46 import org.simantics.db.common.utils.NameUtils;\r
47 import org.simantics.db.exception.DatabaseException;\r
48 import org.simantics.db.layer0.adapter.Instances;\r
49 import org.simantics.db.layer0.request.ActiveModels;\r
50 import org.simantics.db.layer0.util.RemoverUtil;\r
51 import org.simantics.diagram.content.ConnectionUtil;\r
52 import org.simantics.diagram.handler.CopyPasteStrategy;\r
53 import org.simantics.diagram.handler.ElementObjectAssortment;\r
54 import org.simantics.diagram.handler.PasteOperation;\r
55 import org.simantics.diagram.handler.Paster;\r
56 import org.simantics.diagram.stubs.DiagramResource;\r
57 import org.simantics.diagram.synchronization.CollectingModificationQueue;\r
58 import org.simantics.diagram.synchronization.CopyAdvisor;\r
59 import org.simantics.diagram.synchronization.SynchronizationHints;\r
60 import org.simantics.diagram.synchronization.graph.GraphSynchronizationContext;\r
61 import org.simantics.diagram.ui.DiagramModelHints;\r
62 import org.simantics.document.DocumentResource;\r
63 import org.simantics.g2d.canvas.ICanvasContext;\r
64 import org.simantics.g2d.diagram.DiagramClass;\r
65 import org.simantics.g2d.diagram.IDiagram;\r
66 import org.simantics.g2d.diagram.impl.Diagram;\r
67 import org.simantics.layer0.Layer0;\r
68 import org.simantics.modeling.ModelingResources;\r
69 import org.simantics.modeling.ModelingUtils;\r
70 import org.simantics.modeling.mapping.ModelingSynchronizationHints;\r
71 import org.simantics.modeling.typicals.rules.AuxKeys;\r
72 import org.simantics.modeling.typicals.rules.FlagRule;\r
73 import org.simantics.modeling.typicals.rules.InstanceOfRule;\r
74 import org.simantics.modeling.typicals.rules.LabelRule;\r
75 import org.simantics.modeling.typicals.rules.MonitorRule;\r
76 import org.simantics.modeling.typicals.rules.NameRule;\r
77 import org.simantics.modeling.typicals.rules.ProfileMonitorRule;\r
78 import org.simantics.modeling.typicals.rules.SVGElementRule;\r
79 import org.simantics.modeling.typicals.rules.TransformRule;\r
80 import org.simantics.scenegraph.g2d.events.command.Commands;\r
81 import org.simantics.scl.runtime.function.Function4;\r
82 import org.simantics.structural.stubs.StructuralResource2;\r
83 import org.simantics.utils.datastructures.MapSet;\r
84 import org.simantics.utils.strings.AlphanumComparator;\r
85 import org.simantics.utils.strings.EString;\r
86 import org.simantics.utils.ui.ErrorLogger;\r
87 \r
88 /**\r
89  * A write request that synchronizes typical master templates and their\r
90  * instances as specified.\r
91  * \r
92  * <p>\r
93  * Use {@link #SyncTypicalTemplatesToInstances(Resource[], MapSet)} to\r
94  * synchronize all instances of specified templates. Use\r
95  * {@link #syncSingleInstance(Resource)} to synchronize a single typical\r
96  * instance with its master template.\r
97  * \r
98  * @author Tuukka Lehtonen\r
99  * \r
100  * @see ReadTypicalInfo\r
101  * @see TypicalInfo\r
102  * @see TypicalSynchronizationMetadata\r
103  */\r
104 public class SyncTypicalTemplatesToInstances extends WriteRequest {\r
105 \r
106     /**\r
107      * A constant used as the second argument to\r
108      * {@link #SyncTemplates(Resource[], MapSet)} for stating that all specified\r
109      * templates should be fully synchronized to their instances.\r
110      * \r
111      * This is useful for forcing complete synchronization and unit testing. \r
112      */\r
113     public static final EmptyMapSet ALL = EmptyMapSet.INSTANCE;\r
114 \r
115     public static class EmptyMapSet extends MapSet<Resource, Resource> {\r
116 \r
117         public static final EmptyMapSet INSTANCE = new EmptyMapSet();\r
118 \r
119         public EmptyMapSet() {\r
120             this.sets = Collections.emptyMap();\r
121         }\r
122 \r
123         @Override\r
124         protected Set<Resource> getOrCreateSet(Resource key) {\r
125             throw new UnsupportedOperationException("immutable constant instance");\r
126         }\r
127 \r
128     };\r
129 \r
130     protected static final boolean           DEBUG = false;\r
131 \r
132     // Input\r
133 \r
134     final private IProgressMonitor monitor;\r
135     /**\r
136      * Typical diagram rules to apply\r
137      */\r
138     protected Set<Resource>                                     selectedRules;\r
139     \r
140     /**\r
141      * Typical diagram templates to synchronize with their instances.\r
142      */\r
143     protected Resource[]                     templates;\r
144 \r
145     /**\r
146      * Typical diagram instances to synchronize with their templates.\r
147      */\r
148     protected Resource[]                     instances;\r
149 \r
150     /**\r
151      * For each template diagram in {@link #templates}, shall contain a set of\r
152      * elements that have changed and should be synchronized into the instance\r
153      * diagrams. Provided as an argument by the client. Allows optimizing\r
154      * real-time synchronization by not processing everything all the time.\r
155      * \r
156      * If the value is {@link #ALL}, all elements of the template shall be fully\r
157      * synchronized.\r
158      */\r
159     protected MapSet<Resource, Resource>     changedElementsByDiagram;\r
160 \r
161     // Temporary data\r
162 \r
163     protected Layer0                         L0;\r
164     protected StructuralResource2            STR;\r
165     protected DiagramResource                DIA;\r
166     protected ModelingResources              MOD;\r
167 \r
168     /**\r
169      * Needed for using {@link Paster} in\r
170      * {@link #addMissingElements(WriteGraph, TypicalInfo, Resource, Resource, Set)}\r
171      */\r
172     protected GraphSynchronizationContext    syncCtx;\r
173 \r
174     /**\r
175      * For collecting commit metadata during the processing of this request.\r
176      */\r
177     protected TypicalSynchronizationMetadata metadata;\r
178 \r
179     /**\r
180      * Necessary for using {@link CopyPasteStrategy} and {@link PasteOperation}\r
181      * for now. Will be removed in the future once IDiagram is removed from\r
182      * PasteOperation.\r
183      */\r
184     protected IDiagram                       temporaryDiagram;\r
185 \r
186     protected ConnectionUtil                 cu;\r
187 \r
188     /**\r
189      * Maps source -> target connection route nodes, i.e. connectors and\r
190      * interior route nodes (route lines). Inverse mapping of {@link #t2s}.\r
191      */\r
192     protected Map<Resource, Resource>        s2t;\r
193 \r
194     /**\r
195      * Maps target -> source connection route nodes, i.e. connectors and\r
196      * interior route nodes (route lines). Inverse mapping of {@link #s2t}.\r
197      */\r
198     protected Map<Resource, Resource>        t2s;\r
199 \r
200     /**\r
201      * An auxiliary resource map for extracting the correspondences between\r
202      * originals and copied resource when diagram contents are copied from\r
203      * template to instance.\r
204      */\r
205     protected Map<Object, Object>            copyMap;\r
206 \r
207     final private Map<Resource, List<String>> messageLogs = new HashMap<Resource, List<String>>();\r
208     \r
209     public List<Resource> logs = new ArrayList<Resource>();\r
210 \r
211         private boolean writeLog;\r
212 \r
213     /**\r
214      * @param templates typical diagram templates to completely synchronize with\r
215      *        their instances\r
216      */\r
217     public SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource... templates) {\r
218         this(selectedRules, templates, null, ALL, null);\r
219     }\r
220 \r
221     /**\r
222      * @param templates typical diagram templates to partially synchronize with\r
223      *        their instances\r
224      * @param changedElementsByDiagram see {@link #changedElementsByDiagram}\r
225      */\r
226     public SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource[] templates, MapSet<Resource, Resource> changedElementsByDiagram) {\r
227         this(selectedRules, templates, null, changedElementsByDiagram, null);\r
228     }\r
229 \r
230     /**\r
231      * Return a write request that completely synchronizes the specified\r
232      * instance diagram with its template.\r
233      * \r
234      * @param instance\r
235      * @return\r
236      */\r
237     public static SyncTypicalTemplatesToInstances syncSingleInstance(Set<Resource> selectedRules, Resource instance) {\r
238         return new SyncTypicalTemplatesToInstances(selectedRules, null, new Resource[] { instance }, ALL, null);\r
239     }\r
240 \r
241     /**\r
242      * @param templates typical diagram templates to synchronize with their instances\r
243      * @param instances typical diagram instances to synchronize with their templates\r
244      * @param changedElementsByDiagram see {@link #changedElementsByDiagram}\r
245      */\r
246     private SyncTypicalTemplatesToInstances(Set<Resource> selectedRules, Resource[] templates, Resource[] instances, MapSet<Resource, Resource> changedElementsByDiagram, IProgressMonitor monitor) {\r
247         this.selectedRules = selectedRules;\r
248         this.templates = templates;\r
249         this.instances = instances;\r
250         this.changedElementsByDiagram = changedElementsByDiagram;\r
251         this.monitor = monitor;\r
252     }\r
253 \r
254     public SyncTypicalTemplatesToInstances logging(boolean writeLog) {\r
255         this.writeLog = writeLog;\r
256         return this;\r
257     }\r
258 \r
259     private Resource getDiagramNameResource(ReadGraph graph, Resource diagram) throws DatabaseException {\r
260         Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);\r
261         if(composite != null) return composite;\r
262         else return diagram;\r
263     }\r
264     \r
265     private Resource getElementNameResource(ReadGraph graph, Resource element) throws DatabaseException {\r
266         Resource corr = ModelingUtils.getPossibleElementCorrespondendence(graph, element);\r
267         if(corr != null) return corr;\r
268         else return element;\r
269     }\r
270     \r
271     private List<String> getLog(ReadGraph graph, Resource diagram) throws DatabaseException {\r
272         Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(diagram));\r
273         if(indexRoot == null) throw new DatabaseException("FATAL: Diagram is not under any index root.");\r
274         List<String> log = messageLogs.get(indexRoot);\r
275         if(log == null) {\r
276                 log = new ArrayList<String>();\r
277                 messageLogs.put(indexRoot, log);\r
278         }\r
279         return log;\r
280     }\r
281     \r
282     private String elementName(ReadGraph graph, Resource element) throws DatabaseException {\r
283         \r
284         StringBuilder b = new StringBuilder();\r
285         b.append(safeNameAndType(graph, element));\r
286 \r
287         int spaces = 60-b.length();\r
288                 for(int i=0;i<spaces;i++) b.append(" ");\r
289 \r
290         Resource corr = ModelingUtils.getPossibleElementCorrespondendence(graph, element);\r
291         if(corr != null) {\r
292                 b.append(safeNameAndType(graph, corr));\r
293         } else {\r
294                 b.append("-");\r
295         }\r
296         \r
297         return b.toString();\r
298         \r
299     }\r
300 \r
301     @Override\r
302     public void perform(WriteGraph graph) throws DatabaseException {\r
303         this.L0 = Layer0.getInstance(graph);\r
304         this.STR = StructuralResource2.getInstance(graph);\r
305         this.DIA = DiagramResource.getInstance(graph);\r
306         this.MOD = ModelingResources.getInstance(graph);\r
307 \r
308         this.syncCtx = GraphSynchronizationContext.getWriteInstance( graph, new CollectingModificationQueue() );\r
309         this.syncCtx.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph));\r
310 \r
311         this.metadata = new TypicalSynchronizationMetadata();\r
312         this.metadata.synchronizedTypicals = new ArrayList<Resource>();\r
313 \r
314         this.temporaryDiagram = Diagram.spawnNew(DiagramClass.DEFAULT);\r
315         this.temporaryDiagram.setHint(SynchronizationHints.CONTEXT, syncCtx);\r
316 \r
317         this.cu = new ConnectionUtil(graph);\r
318 \r
319         if (templates != null) {\r
320             // Look for typical template instances from the currently active models only.\r
321             Collection<Resource> activeModels = graph.syncRequest(new ActiveModels(Simantics.getProjectResource()));\r
322             if (!activeModels.isEmpty()) {\r
323                 for (Resource template : templates) {\r
324                     syncTemplate(graph, template, activeModels);\r
325                 }\r
326             }\r
327         }\r
328         if (instances != null) {\r
329             for (Resource instance : instances) {\r
330                 syncInstance(graph, instance);\r
331             }\r
332         }\r
333 \r
334         if (writeLog) {\r
335             for(Map.Entry<Resource, List<String>> entry : messageLogs.entrySet()) {\r
336 \r
337                 Resource indexRoot = entry.getKey();\r
338                 List<String> messageLog = entry.getValue();\r
339 \r
340                 Layer0 L0 = Layer0.getInstance(graph);\r
341                 DocumentResource DOC = DocumentResource.getInstance(graph);\r
342 \r
343                 Collection<Resource> libs = graph.syncRequest(new ObjectsWithType(indexRoot, L0.ConsistsOf, DOC.DocumentLibrary));\r
344                 if(libs.isEmpty()) continue;\r
345 \r
346                 List<NamedResource> nrs = new ArrayList<NamedResource>();\r
347                 for(Resource lib : libs) nrs.add(new NamedResource(NameUtils.getSafeName(graph, lib), lib));\r
348                 Collections.sort(nrs, AlphanumComparator.CASE_INSENSITIVE_COMPARATOR);\r
349                 Resource library = nrs.iterator().next().getResource();\r
350 \r
351                 CommonDBUtils.selectClusterSet(graph, library);\r
352 \r
353                 String text = "--- Created: " + new Date().toString() + " ---\n";\r
354                 text += EString.implode(messageLog);\r
355 \r
356                 Resource log = graph.newResource();\r
357                 graph.claim(log, L0.InstanceOf, null, DOC.PlainTextDocument);\r
358                 graph.claimLiteral(log, L0.HasName, L0.String, "Typical Sync " + new Date().toString());\r
359                 graph.claim(library, L0.ConsistsOf, L0.PartOf, log);\r
360                 graph.claimLiteral(log, DOC.PlainTextDocument_text, L0.String, text);\r
361                 logs.add(log);\r
362 \r
363             }\r
364         }\r
365 \r
366         if (!metadata.getTypicals().isEmpty()) {\r
367             graph.addMetadata(metadata);\r
368 \r
369             // Add comment to change set.\r
370             CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
371             graph.addMetadata( cm.add("Synchronized " + metadata.getTypicals().size() + " typical diagram instances (" + metadata.getTypicals() + ") with their templates.") );\r
372         }\r
373 \r
374         temporaryDiagram = null;\r
375         syncCtx = null;\r
376     }\r
377 \r
378     private Collection<Resource> findInstances(ReadGraph graph, Resource ofType, Collection<Resource> indexRoots) throws DatabaseException {\r
379         Instances index = graph.adapt(ofType, Instances.class);\r
380         Set<Resource> instances = new HashSet<>();\r
381         for (Resource indexRoot : indexRoots)\r
382             instances.addAll( index.find(graph, indexRoot) );\r
383         return instances;\r
384     }\r
385 \r
386     private void syncTemplate(WriteGraph graph, Resource template, Collection<Resource> indexRoots) throws DatabaseException {\r
387         Resource templateType = graph.getPossibleType(template, DIA.Diagram);\r
388         if (templateType == null)\r
389             return;\r
390 \r
391         Collection<Resource> instances = findInstances(graph, templateType, indexRoots);\r
392         // Do not include the template itself as it is also an instance of templateType\r
393         instances.remove(template);\r
394         if (instances.isEmpty())\r
395             return;\r
396 \r
397         Set<Resource> templateElements = new THashSet<Resource>( graph.syncRequest(\r
398                 new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );\r
399 \r
400         try {\r
401             for (Resource instance : instances) {\r
402                 this.temporaryDiagram.setHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE, instance);\r
403                 syncInstance(graph, template, instance, templateElements);\r
404             }\r
405         } catch (Exception e) {\r
406                 Logger.defaultLogError(e);\r
407                 e.printStackTrace();\r
408         } finally {\r
409             this.temporaryDiagram.removeHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);\r
410         }\r
411     }\r
412 \r
413     private void syncInstance(WriteGraph graph, Resource instance) throws DatabaseException {\r
414         Resource template = graph.getPossibleObject(instance, MOD.HasDiagramSource);\r
415         if (template == null)\r
416             return;\r
417 \r
418         Set<Resource> templateElements = new THashSet<Resource>( graph.syncRequest(\r
419                 new ObjectsWithType(template, L0.ConsistsOf, DIA.Element) ) );\r
420 \r
421         try {\r
422             this.temporaryDiagram.setHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE, instance);\r
423             syncInstance(graph, template, instance, templateElements);\r
424         } finally {\r
425             this.temporaryDiagram.removeHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);\r
426         }\r
427     }\r
428     \r
429     private Resource findInstanceCounterpart(ReadGraph graph, Resource instanceDiagram, Resource templateElement) throws DatabaseException {\r
430         Map<String,Resource> children = graph.syncRequest(new UnescapedChildMapOfResource(instanceDiagram));\r
431         for(Resource child : children.values()) {\r
432                 if(graph.hasStatement(child, MOD.HasElementSource, templateElement)) return child;\r
433         }\r
434         return null;\r
435     }\r
436     \r
437     private boolean isSynchronizedConnector(ReadGraph graph, Resource templateConnection, Resource instanceConnector) throws DatabaseException {\r
438         DiagramResource DIA = DiagramResource.getInstance(graph);\r
439         Resource instanceConnection = graph.getPossibleObject(instanceConnector, DIA.IsConnectorOf);\r
440         return graph.hasStatement(instanceConnection, MOD.HasElementSource, templateConnection)\r
441                         // If the master connection has been removed, this is all that's left\r
442                         // to identify a connection that at least was originally synchronized\r
443                         // from the typical master to this instance.\r
444                         || graph.hasStatement(instanceConnection, MOD.IsTemplatized);\r
445     }\r
446 \r
447     /**\r
448      * Perform the following synchronization steps for the instance diagram:\r
449      * <ol>\r
450      * <li>remove such templatized elements from the instance diagram whose\r
451      * template counterpart no longer exists</li>\r
452      * <li>add elements to the instance diagram that are only in the template</li>\r
453      * <li>synchronize elements of the instance diagram that have been deemed\r
454      * changed</li>\r
455      * </ol>\r
456      * \r
457      * @param graph database write access\r
458      * @param template the synchronization source diagram\r
459      * @param instance the synchronization target diagram\r
460      * @param currentTemplateElements the set of all elements currently in the\r
461      *        template diagram\r
462      * @throws DatabaseException if anything goes wrong\r
463      */\r
464     private void syncInstance(WriteGraph graph, Resource template, Resource instance, Set<Resource> currentTemplateElements) throws DatabaseException {\r
465 \r
466         List<String> messageLog = getLog(graph, instance);\r
467         \r
468         messageLog.add("Synchronization of changed typical template: " + SyncTypicalTemplatesToInstances.safeNameAndType(graph, getDiagramNameResource(graph, template)));\r
469         messageLog.add("----\n\ttypical instance: " + safeNameAndType(graph, getDiagramNameResource(graph, instance)));\r
470 \r
471         CommonDBUtils.selectClusterSet(graph, instance);\r
472         \r
473         // Form instance element <-> template element bijection\r
474         TypicalInfoBean typicalInfoBean = graph.syncRequest(\r
475                 new ReadTypicalInfo(instance),\r
476                 TransientCacheListener.<TypicalInfoBean> instance());\r
477         // Must be able to modify the typicalInfo structure,\r
478         // therefore clone the query result.\r
479         typicalInfoBean = (TypicalInfoBean) typicalInfoBean.clone();\r
480         typicalInfoBean.templateElements = currentTemplateElements;\r
481         typicalInfoBean.auxiliary = new HashMap<Object, Object>(1);\r
482 \r
483         TypicalInfo info = new TypicalInfo();\r
484         info.monitor = monitor;\r
485         info.messageLog = messageLog;\r
486         info.bean = typicalInfoBean;\r
487         \r
488         // Resolve naming function for this typical instance.\r
489         Resource compositeInstance = graph.getPossibleObject(instance, MOD.DiagramToComposite);\r
490         if (compositeInstance != null) {\r
491                 Function4<ReadGraph, Resource, Resource, String, String> namingFunction = TypicalUtil.getTypicalNamingFunction(graph, compositeInstance);\r
492             if (namingFunction != null)\r
493                 typicalInfoBean.auxiliary.put(AuxKeys.KEY_TYPICAL_NAMING_FUNCTION, namingFunction);\r
494         }\r
495 \r
496         int dSizeAbs = Math.abs(typicalInfoBean.instanceElements.size() - currentTemplateElements.size());\r
497 \r
498         if(DEBUG)\r
499                 System.out.println("typical <-> template mapping: " + typicalInfoBean.instanceToTemplate);\r
500 \r
501         // Find elements to be removed from instance by looking for all\r
502         // instance elements that do not have a MOD.HasElementSource\r
503         // relation but have a MOD.IsTemplatized tag.\r
504         Set<Resource> instanceElementsRemovedFromTemplate = findInstanceElementsRemovedFromTemplate(\r
505                 graph, info, new THashSet<Resource>(dSizeAbs));\r
506 \r
507         // Find elements in template that do not yet exist in the instance\r
508         Set<Resource> templateElementsAddedToTemplate = findTemplateElementsMissingFromInstance(\r
509                 graph, currentTemplateElements, info,\r
510                 new THashSet<Resource>(dSizeAbs));\r
511 \r
512         Set<Resource> changedTemplateElements = changedElementsByDiagram.removeValues(template);\r
513 \r
514         if(DEBUG)\r
515                 System.out.println("ADDED: " + templateElementsAddedToTemplate.size() + ", REMOVED: " + instanceElementsRemovedFromTemplate.size() + ", CHANGED: " + changedTemplateElements.size());\r
516         \r
517         // Validate\r
518         for(Resource templateElement : graph.getObjects(template, L0.ConsistsOf)) {\r
519                 if(graph.isInstanceOf(templateElement, DIA.RouteGraphConnection)) {\r
520                         for(Resource connector : graph.getObjects(templateElement, DIA.HasConnector)) {\r
521                                 for(Statement elementStm : graph.getStatements(connector, STR.Connects)) {\r
522                                         Resource otherElement = elementStm.getObject(); \r
523                                         if(!otherElement.equals(templateElement)) {\r
524                                 Resource counterPartElement = findInstanceCounterpart(graph, instance, otherElement);\r
525                                 if(counterPartElement != null) {\r
526                                                 Resource diagramConnectionPoint = graph.getInverse(elementStm.getPredicate());\r
527                                                 Resource connectionPoint = graph.getPossibleObject(diagramConnectionPoint, MOD.DiagramConnectionRelationToConnectionRelation);\r
528                                                 if(connectionPoint != null) {\r
529                                                         Statement stm = graph.getPossibleStatement(counterPartElement, diagramConnectionPoint);\r
530                                         if(stm != null) {\r
531                                                 if(graph.isInstanceOf(connectionPoint, L0.FunctionalRelation)) {\r
532                                                         if(!isSynchronizedConnector(graph, templateElement, stm.getObject())) {\r
533                                                                 messageLog.add("\t\tABORTED: tried to connect to an already connected terminal " + NameUtils.getSafeName(graph, counterPartElement) + " " + NameUtils.getSafeName(graph, connectionPoint));\r
534                                                                 return;\r
535                                                         }\r
536                                                 }\r
537                                                 }\r
538                                                 }\r
539                                 }\r
540                                         }\r
541                                 }\r
542                         }\r
543                 }\r
544         }\r
545         \r
546         // Perform changes\r
547         boolean changed = false;\r
548         changed |= synchronizeDiagramChanges(graph, info, template, instance);\r
549         changed |= removeElements(graph, info, instanceElementsRemovedFromTemplate);\r
550         changed |= addMissingElements(graph, info, template, instance, templateElementsAddedToTemplate);\r
551         changed |= synchronizeChangedElements(graph, info, template, instance, changedTemplateElements, templateElementsAddedToTemplate, changedElementsByDiagram == ALL);\r
552 \r
553         if (changed)\r
554             metadata.addTypical(instance);\r
555     }\r
556 \r
557     /**\r
558      * Synchronize any configurable aspects of the typical diagram instance itself.\r
559      * Every rule executed here comes from the ontology, nothing is fixed. \r
560      * \r
561      * @param graph\r
562      * @param typicalInfo\r
563      * @param template\r
564      * @param instance\r
565      * @return if any changes were made. \r
566      * @throws DatabaseException\r
567      */\r
568     private boolean synchronizeDiagramChanges(\r
569             WriteGraph graph,\r
570             TypicalInfo typicalInfo,\r
571             Resource template,\r
572             Resource instance)\r
573                     throws DatabaseException\r
574     {\r
575         boolean changed = false;\r
576         for (Resource rule : graph.getObjects(template, MOD.HasTypicalSynchronizationRule)) {\r
577             if (selectedRules != null && !selectedRules.contains(rule))\r
578                 continue;\r
579             ITypicalSynchronizationRule r = graph.getPossibleAdapter(rule, ITypicalSynchronizationRule.class);\r
580             if (r != null)\r
581                 changed |= r.synchronize(graph, template, instance, typicalInfo);\r
582         }\r
583         return changed;\r
584     }\r
585 \r
586     /**\r
587      * Add elements from template that do not yet exist in the instance.\r
588      * \r
589      * @param graph\r
590      * @param template\r
591      * @param instance\r
592      * @param elementsAddedToTemplate\r
593      * @return <code>true</code> if changes were made to the instance\r
594      * @throws DatabaseException\r
595      */\r
596     private boolean addMissingElements(WriteGraph graph, TypicalInfo typicalInfo, Resource template,\r
597             Resource instance, Set<Resource> elementsAddedToTemplate)\r
598                     throws DatabaseException {\r
599         if (elementsAddedToTemplate.isEmpty())\r
600             return false;\r
601 \r
602         CopyAdvisor copyAdvisor = graph.syncRequest(new Adapter<CopyAdvisor>(instance, CopyAdvisor.class));\r
603         this.temporaryDiagram.setHint(SynchronizationHints.COPY_ADVISOR, copyAdvisor);\r
604 \r
605         ElementObjectAssortment assortment = new ElementObjectAssortment(graph, elementsAddedToTemplate);\r
606         if (copyMap == null)\r
607             copyMap = new THashMap<Object, Object>();\r
608         else\r
609             copyMap.clear();\r
610 \r
611         if (DEBUG)\r
612             System.out.println("ADD MISSING ELEMENTS: " + assortment);\r
613 \r
614         // initialCopyMap argument is needed for copying just connections\r
615         // when their end-points are not copied at the same time.\r
616 \r
617         PasteOperation pasteOp = new PasteOperation(Commands.COPY,\r
618                 (ICanvasContext) null, template, instance, temporaryDiagram,\r
619                 assortment, false, new Point2D.Double(0, 0),\r
620                 typicalInfo.bean.templateToInstance, copyMap)\r
621         .options(PasteOperation.ForceCopyReferences.INSTANCE);\r
622 \r
623         new Paster(graph.getSession(), pasteOp).perform(graph);\r
624 \r
625         boolean changed = false;\r
626 \r
627         if(!elementsAddedToTemplate.isEmpty())\r
628                 typicalInfo.messageLog.add("\tadded elements");\r
629         \r
630         for (Resource addedElement : elementsAddedToTemplate) {\r
631             Resource copyElement = (Resource) copyMap.get(addedElement);\r
632             if (copyElement != null) {\r
633                 graph.claim(copyElement, MOD.IsTemplatized, MOD.IsTemplatized, copyElement);\r
634                 graph.claim(copyElement, MOD.HasElementSource, MOD.ElementHasInstance, addedElement);\r
635 \r
636                 typicalInfo.bean.instanceElements.add(copyElement);\r
637                 typicalInfo.bean.instanceToTemplate.put(copyElement, addedElement);\r
638                 typicalInfo.bean.templateToInstance.put(addedElement, copyElement);\r
639 \r
640                 typicalInfo.messageLog.add("\t\t" + safeNameAndType(graph, copyElement));\r
641 \r
642                 changed = true;\r
643             }\r
644         }\r
645 \r
646         ModelingResources MOD = ModelingResources.getInstance(graph);\r
647         Resource instanceComposite = graph.getPossibleObject(instance, MOD.DiagramToComposite);\r
648         List<Resource> instanceComponents = new ArrayList<Resource>(elementsAddedToTemplate.size());\r
649 \r
650         // Post-process added elements after typicalInfo has been updated and\r
651         // template mapping statements are in place.\r
652         for (Resource addedElement : elementsAddedToTemplate) {\r
653             Resource copyElement = (Resource) copyMap.get(addedElement);\r
654             if (copyElement != null) {\r
655                 postProcessAddedElement(graph, addedElement, copyElement, typicalInfo);\r
656 \r
657                 if (instanceComponents != null) {\r
658                     // Gather all instance typical components for applying naming\r
659                     // strategy on them.\r
660                     Resource component = graph.getPossibleObject(copyElement, MOD.ElementToComponent);\r
661                     if (component != null)\r
662                         instanceComponents.add(component);\r
663                 }\r
664             }\r
665         }\r
666 \r
667         if (instanceComposite != null)\r
668             TypicalUtil.applySelectedModuleNames(graph, instanceComposite, instanceComponents);\r
669 \r
670         return changed;\r
671     }\r
672 \r
673     private void postProcessAddedElement(WriteGraph graph,\r
674             Resource addedTemplateElement, Resource addedInstanceElement,\r
675             TypicalInfo typicalInfo) throws DatabaseException {\r
676         if (graph.isInstanceOf(addedInstanceElement, DIA.Monitor)) {\r
677             postProcessAddedMonitor(graph, addedTemplateElement, addedInstanceElement, typicalInfo);\r
678         }\r
679     }\r
680 \r
681     private void postProcessAddedMonitor(WriteGraph graph,\r
682             Resource addedTemplateMonitor, Resource addedInstanceMonitor,\r
683             TypicalInfo typicalInfo) throws DatabaseException {\r
684         Resource monitor = addedInstanceMonitor;\r
685         Resource monitoredComponent = graph.getPossibleObject(monitor, DIA.HasMonitorComponent);\r
686         if (monitoredComponent != null) {\r
687             Resource monitoredTemplateElement = graph.getPossibleObject(monitoredComponent, MOD.ComponentToElement);\r
688             if (monitoredTemplateElement != null) {\r
689                 Resource monitoredInstanceElement = typicalInfo.bean.templateToInstance.get(monitoredTemplateElement);\r
690                 if (monitoredInstanceElement != null) {\r
691                     Resource monitoredInstanceComponent = graph.getPossibleObject(monitoredInstanceElement, MOD.ElementToComponent);\r
692                     if (monitoredInstanceComponent != null) {\r
693                         // Ok, the monitor refers to a component within the\r
694                         // template composite. Change it to refer to the\r
695                         // instance composite.\r
696                         graph.deny(monitor, DIA.HasMonitorComponent);\r
697                         graph.claim(monitor, DIA.HasMonitorComponent, monitoredInstanceComponent);\r
698                     }\r
699                 }\r
700             }\r
701         }\r
702     }\r
703 \r
704     private boolean removeElements(WriteGraph graph, TypicalInfo typicalInfo, Set<Resource> elementsRemovedFromTemplate) throws DatabaseException {\r
705         if (elementsRemovedFromTemplate.isEmpty())\r
706             return false;\r
707 \r
708         // Remove mapped elements from instance that are removed from the template.\r
709         boolean changed = false;\r
710         \r
711         if(!elementsRemovedFromTemplate.isEmpty())\r
712                 typicalInfo.messageLog.add("\tremoved elements");\r
713         \r
714         for (Resource removedElement : elementsRemovedFromTemplate) {\r
715                 typicalInfo.messageLog.add("\t\t" + safeNameAndType(graph, removedElement));\r
716 \r
717             RemoverUtil.remove(graph, removedElement);\r
718 \r
719             typicalInfo.bean.instanceElements.remove(removedElement);\r
720             Resource template = typicalInfo.bean.instanceToTemplate.remove(removedElement);\r
721             if (template != null)\r
722                 typicalInfo.bean.templateToInstance.remove(template);\r
723 \r
724             changed = true;\r
725         }\r
726         return changed;\r
727     }\r
728 \r
729     private Set<Resource> findTemplateElementsMissingFromInstance(\r
730             WriteGraph graph, Collection<Resource> currentTemplateElements,\r
731             TypicalInfo typicalInfo, THashSet<Resource> result)\r
732                   throws DatabaseException {\r
733         for (Resource templateElement : currentTemplateElements) {\r
734             Resource instanceElement = typicalInfo.bean.templateToInstance.get(templateElement);\r
735             if (instanceElement == null) {\r
736                 if(DEBUG)\r
737                         System.out.println("No instance correspondence for template element " + NameUtils.getSafeName(graph, templateElement, true) + " => add");\r
738                 result.add(templateElement);\r
739             }\r
740         }\r
741         return result;\r
742     }\r
743 \r
744     public Set<Resource> findInstanceElementsRemovedFromTemplate(\r
745             ReadGraph graph, TypicalInfo typicalInfo,\r
746             THashSet<Resource> result) throws DatabaseException {\r
747         for (Resource instanceElement : typicalInfo.bean.instanceElements) {\r
748             if (!typicalInfo.bean.instanceToTemplate.containsKey(instanceElement)) {\r
749                 if (typicalInfo.bean.isTemplatized.contains(instanceElement)) {\r
750                         if(DEBUG)\r
751                                 System.out.println("Templatized typical instance element " + NameUtils.getSafeName(graph, instanceElement, true) + " has no correspondence in template => remove");\r
752                     result.add(instanceElement);\r
753                 }\r
754             }\r
755         }\r
756         return result;\r
757     }\r
758 \r
759     /**\r
760      * Synchronize basic visual aspects of changed elements. For all elements,\r
761      * transform and label are synchronized. Otherwise synchronization is\r
762      * type-specific for connections, flags, monitors and svg elements.\r
763      * \r
764      * @param graph\r
765      * @param typicalInfo\r
766      * @param template\r
767      * @param instance\r
768      * @param changedTemplateElements\r
769      * @param addedElements\r
770      *            elements that have been added and thus need not be\r
771      *            synchronized\r
772      * @param synchronizeAllElements\r
773      * @return\r
774      * @throws DatabaseException\r
775      */\r
776     private boolean synchronizeChangedElements(WriteGraph graph,\r
777             TypicalInfo typicalInfo, Resource template, Resource instance,\r
778             Collection<Resource> changedTemplateElements,\r
779             Set<Resource> addedElements,\r
780             boolean synchronizeAllElements) throws DatabaseException {\r
781 \r
782         if (synchronizeAllElements) {\r
783             // For unit testing purposes.\r
784             changedTemplateElements = graph.syncRequest(new ObjectsWithType(template, L0.ConsistsOf, DIA.Element));\r
785         }\r
786 \r
787         if (changedTemplateElements.isEmpty())\r
788             return false;\r
789 \r
790         boolean changed = false;\r
791 \r
792         typicalInfo.messageLog.add("\telement change analysis");\r
793         int analysisLogPosition = typicalInfo.messageLog.size();\r
794 \r
795         for (Resource changedTemplateElement : changedTemplateElements) {\r
796             // Skip synchronization of elements that were just added and are\r
797             // thus already synchronized.\r
798             if (addedElements.contains(changedTemplateElement))\r
799                 continue;\r
800 \r
801             Resource instanceElement = typicalInfo.bean.templateToInstance.get(changedTemplateElement);\r
802             if (instanceElement == null) {\r
803                 // There's an earlier problem in the sync process if this happens.\r
804                 typicalInfo.messageLog.add("SKIPPING SYNC OF CHANGED TEMPLATE ELEMENT DUE TO MISSING INSTANCE: " + safeNameAndType(graph, getElementNameResource(graph, changedTemplateElement)));\r
805                 continue;\r
806             }\r
807             \r
808             typicalInfo.messageLog.add("\t\t" + elementName(graph, changedTemplateElement));\r
809             int currentLogSize = typicalInfo.messageLog.size();\r
810 \r
811             changed |= InstanceOfRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
812             changed |= NameRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
813             changed |= TransformRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
814             changed |= LabelRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
815 \r
816             Collection<Resource> types = graph.getTypes(changedTemplateElement);\r
817             if (types.contains(DIA.RouteGraphConnection)) {\r
818                 changed |= synchronizeConnection(graph, changedTemplateElement, instanceElement, typicalInfo);\r
819             } else if (types.contains(DIA.Flag)) {\r
820                 changed |= FlagRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
821             } else if (types.contains(DIA.Monitor)) {\r
822                 changed |= MonitorRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
823             } else if (types.contains(DIA.SVGElement)) {\r
824                 changed |= SVGElementRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
825             }\r
826 \r
827             changed |= ProfileMonitorRule.INSTANCE.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
828             \r
829             for (Resource rule : graph.getObjects(changedTemplateElement, MOD.HasTypicalSynchronizationRule)) {\r
830                 if(selectedRules != null && !selectedRules.contains(rule)) continue;\r
831                 ITypicalSynchronizationRule r = graph.getPossibleAdapter(rule, ITypicalSynchronizationRule.class);\r
832                 if (r != null)\r
833                     changed |= r.synchronize(graph, changedTemplateElement, instanceElement, typicalInfo);\r
834             }\r
835             \r
836             // Show element only if something has happened\r
837             if(currentLogSize == typicalInfo.messageLog.size())\r
838                 typicalInfo.messageLog.remove(typicalInfo.messageLog.size()-1);\r
839             \r
840         }\r
841 \r
842         if (s2t != null)\r
843             s2t.clear();\r
844         if (t2s != null)\r
845             t2s.clear();\r
846 \r
847         // Show analysis header only if something has happened\r
848         if(analysisLogPosition == typicalInfo.messageLog.size())\r
849                 typicalInfo.messageLog.remove(typicalInfo.messageLog.size()-1);\r
850 \r
851         return changed;\r
852     }\r
853 \r
854     /**\r
855      * Synchronizes two route graph connection topologies if and only if the\r
856      * destination connection is not attached to any node elements besides\r
857      * the ones that exist in the source. This means that connections that\r
858      * have instance-specific connections to non-template nodes are ignored\r
859      * here.\r
860      * \r
861      * @param graph\r
862      * @param sourceConnection\r
863      * @param targetConnection\r
864      * @param typicalInfo\r
865      * @return <code>true</code> if changes were made \r
866      * @throws DatabaseException\r
867      */\r
868     private boolean synchronizeConnection(WriteGraph graph, Resource sourceConnection, Resource targetConnection, TypicalInfo typicalInfo)\r
869             throws DatabaseException {\r
870 \r
871         if(DEBUG)\r
872                 System.out.println("connection " + NameUtils.getSafeName(graph, sourceConnection, true) + " to target connection " + NameUtils.getSafeName(graph, targetConnection, true));\r
873 \r
874         boolean changed = false;\r
875 \r
876         // Initialize utilities and data maps\r
877         s2t = newOrClear(s2t);\r
878         t2s = newOrClear(t2s);\r
879 \r
880         if (cu == null)\r
881             cu = new ConnectionUtil(graph);\r
882 \r
883         // 0.1. find mappings between source and target connection connectors\r
884         Collection<Resource> targetConnectors = graph.getObjects(targetConnection, DIA.HasConnector);\r
885         for (Resource targetConnector : targetConnectors) {\r
886             Statement toNode = cu.getConnectedComponentStatement(targetConnection, targetConnector);\r
887             if (toNode == null) {\r
888                 // Corrupted target connection!\r
889                 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
890                 return false;\r
891             }\r
892 \r
893             // Check that the target connections does not connect to\r
894             // non-templatized elements before syncing.\r
895             if (!graph.hasStatement(toNode.getObject(), MOD.IsTemplatized))\r
896                 return false;\r
897 \r
898             //Resource templateNode = typicalInfo.instanceToTemplate.get(toNode.getObject());\r
899             Resource templateNode = graph.getPossibleObject(toNode.getObject(), MOD.HasElementSource);\r
900             if (templateNode != null) {\r
901                 Resource isConnectedTo = graph.getPossibleInverse(toNode.getPredicate());\r
902                 if (isConnectedTo != null) {\r
903                     Resource templateConnector = graph.getPossibleObject(templateNode, isConnectedTo);\r
904                     if (templateConnector != null) {\r
905                         Resource connectionOfTemplateConnector = ConnectionUtil.tryGetConnection(graph, templateConnector);\r
906                         if (sourceConnection.equals(connectionOfTemplateConnector)) {\r
907                             s2t.put(templateConnector, targetConnector);\r
908                             t2s.put(targetConnector, templateConnector);\r
909 \r
910                             if (DEBUG)\r
911                                 System.out.println("Mapping connector "\r
912                                         + NameUtils.getSafeName(graph, templateConnector, true)\r
913                                         + " to " + NameUtils.getSafeName(graph, targetConnector, true));\r
914                         }\r
915                     }\r
916                 }\r
917             }\r
918         }\r
919 \r
920         // 0.2. find mapping between source and target route lines\r
921         Collection<Resource> sourceInteriorRouteNodes = graph.getObjects(sourceConnection, DIA.HasInteriorRouteNode);\r
922         Collection<Resource> targetInteriorRouteNodes = graph.getObjects(targetConnection, DIA.HasInteriorRouteNode);\r
923         Map<Resource, Paster.RouteLine> sourceToRouteLine = new THashMap<Resource, Paster.RouteLine>();\r
924         Map<Resource, Paster.RouteLine> targetToRouteLine = new THashMap<Resource, Paster.RouteLine>();\r
925 \r
926         for (Resource source : sourceInteriorRouteNodes)\r
927             sourceToRouteLine.put(source, Paster.readRouteLine(graph, source));\r
928         for (Resource target : targetInteriorRouteNodes)\r
929             targetToRouteLine.put(target, Paster.readRouteLine(graph, target));\r
930 \r
931         nextSourceLine:\r
932             for (Iterator<Map.Entry<Resource, Paster.RouteLine>> sourceIt = sourceToRouteLine.entrySet().iterator(); !targetToRouteLine.isEmpty() && sourceIt.hasNext();) {\r
933                 Map.Entry<Resource, Paster.RouteLine> sourceEntry = sourceIt.next();\r
934                 Paster.RouteLine sourceLine = sourceEntry.getValue();\r
935                 for (Iterator<Map.Entry<Resource, Paster.RouteLine>> targetIt = targetToRouteLine.entrySet().iterator(); targetIt.hasNext();) {\r
936                     Map.Entry<Resource, Paster.RouteLine> targetEntry = targetIt.next();\r
937                     if (sourceLine.equals(targetEntry.getValue())) {\r
938                         s2t.put(sourceEntry.getKey(), targetEntry.getKey());\r
939                         t2s.put(targetEntry.getKey(), sourceEntry.getKey());\r
940                         sourceIt.remove();\r
941                         targetIt.remove();\r
942 \r
943                         if (DEBUG)\r
944                             System.out.println("Mapping routeline "\r
945                                     + NameUtils.getSafeName(graph, sourceEntry.getKey(), true)\r
946                                     + " - " + sourceEntry.getValue()\r
947                                     + " to " + NameUtils.getSafeName(graph, targetEntry.getKey(), true)\r
948                                     + " - " + targetEntry.getValue());\r
949 \r
950                         continue nextSourceLine;\r
951                     }\r
952                 }\r
953             }\r
954 \r
955         if (DEBUG) {\r
956             System.out.println("Take 1: Source to target route nodes map : " + s2t);\r
957             System.out.println("Take 1: Target to source route nodes map : " + t2s);\r
958         }\r
959 \r
960         // 1.1 remove excess connectors\r
961         for (Resource targetConnector : targetConnectors) {\r
962             if (!t2s.containsKey(targetConnector)) {\r
963                 typicalInfo.messageLog.add("\t\t\tremove excess connector from target connection: " + NameUtils.getSafeName(graph, targetConnector));\r
964                 cu.removeConnectionPart(targetConnector);\r
965                 changed = true;\r
966             }\r
967         }\r
968 \r
969         // 1.2 add missing connectors to target\r
970         Collection<Resource> sourceConnectors = graph.getObjects(sourceConnection, DIA.HasConnector);\r
971         for (Resource sourceConnector : sourceConnectors) {\r
972             if (!s2t.containsKey(sourceConnector)) {\r
973                 Statement sourceIsConnectorOf = graph.getSingleStatement(sourceConnector, DIA.IsConnectorOf);\r
974                 Statement connects = cu.getConnectedComponentStatement(sourceConnection, sourceConnector);\r
975                 if (connects == null) {\r
976                     // TODO: serious error!\r
977                     throw new DatabaseException("ERROR: connector is astray, i.e. not connected to a node element: " + safeNameAndType(graph, sourceConnector));\r
978                 }\r
979                 Resource connectsInstanceElement = typicalInfo.bean.templateToInstance.get(connects.getObject());\r
980                 if (connectsInstanceElement == null) {\r
981                     // TODO: serious error!\r
982                     throw new DatabaseException("ERROR: could not find instance element to which template element " + safeNameAndType(graph, connects.getObject()) + " is connected to");\r
983                 }\r
984                 Resource hasConnector = graph.getInverse(sourceIsConnectorOf.getPredicate());\r
985 \r
986                 Resource newTargetConnector = cu.newConnector(targetConnection, hasConnector);\r
987                 graph.claim(newTargetConnector, connects.getPredicate(), connectsInstanceElement);\r
988                 changed = true;\r
989 \r
990                 s2t.put(sourceConnector, newTargetConnector);\r
991                 t2s.put(newTargetConnector, sourceConnector);\r
992 \r
993                 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
994             }\r
995         }\r
996 \r
997         // 2. sync route lines and their connectivity:\r
998         // 2.1. assign correspondences in target for each source route line\r
999         //      by reusing excess routelines in target and by creating new\r
1000         //      route lines.\r
1001 \r
1002         Resource[] targetRouteLines = targetToRouteLine.keySet().toArray(Resource.NONE);\r
1003         int targetRouteLine = targetRouteLines.length - 1;\r
1004 \r
1005         for (Iterator<Map.Entry<Resource, Paster.RouteLine>> sourceIt = sourceToRouteLine.entrySet().iterator(); sourceIt.hasNext();) {\r
1006             Map.Entry<Resource, Paster.RouteLine> sourceEntry = sourceIt.next();\r
1007             Resource source = sourceEntry.getKey();\r
1008             Paster.RouteLine sourceLine = sourceEntry.getValue();\r
1009 \r
1010             typicalInfo.messageLog.add("\t\t\tassign an instance-side routeline complement for " + NameUtils.getSafeName(graph, source, true) + " - " + sourceLine);\r
1011 \r
1012             // Assign target route line for source\r
1013             Resource target = null;\r
1014             if (targetRouteLine < 0) {\r
1015                 // by creating new route lines\r
1016                 target = cu.newRouteLine(targetConnection, sourceLine.getPosition(), sourceLine.isHorizontal());\r
1017                 typicalInfo.messageLog.add("\t\t\tcreate new route line " + NameUtils.getSafeName(graph, target));\r
1018                 changed = true;\r
1019             } else {\r
1020                 // by reusing existing route line\r
1021                 target = targetRouteLines[targetRouteLine--];\r
1022                 copyRouteLine(graph, source, target);\r
1023                 cu.disconnectFromAllRouteNodes(target);\r
1024                 typicalInfo.messageLog.add("\t\t\treused existing route line " + NameUtils.getSafeName(graph, target));\r
1025                 changed = true;\r
1026             }\r
1027             s2t.put(source, target);\r
1028             t2s.put(target, source);\r
1029 \r
1030             typicalInfo.messageLog.add("\t\t\tmapped source route line " + NameUtils.getSafeName(graph, source) + " to target route line " + NameUtils.getSafeName(graph, target));\r
1031         }\r
1032 \r
1033         if (targetRouteLine >= 0) {\r
1034                 typicalInfo.messageLog.add("\t\t\tremove excess route lines (" + (targetRouteLine + 1) + ") from target connection");\r
1035             for (; targetRouteLine >= 0; targetRouteLine--) {\r
1036                 typicalInfo.messageLog.add("\t\t\t\tremove excess route line: " + NameUtils.getSafeName(graph, targetRouteLines[targetRouteLine], true));\r
1037                 cu.removeConnectionPart(targetRouteLines[targetRouteLine]);\r
1038             }\r
1039         }\r
1040 \r
1041         if (DEBUG) {\r
1042             System.out.println("Take 2: Source to target route nodes map : " + s2t);\r
1043             System.out.println("Take 2: Target to source route nodes map : " + t2s);\r
1044         }\r
1045 \r
1046         // 2.2. Synchronize target connection topology (DIA.AreConnected)\r
1047         changed |= connectRouteNodes(graph, typicalInfo, sourceInteriorRouteNodes);\r
1048         changed |= connectRouteNodes(graph, typicalInfo, sourceConnectors);\r
1049 \r
1050         // 3. remove excess routelines & connectors from target connection\r
1051         changed |= cu.removeExtraInteriorRouteNodes(targetConnection) > 0;\r
1052         changed |= cu.removeUnusedConnectors(targetConnection) > 0;\r
1053 \r
1054         return changed;\r
1055     }\r
1056 \r
1057     private boolean connectRouteNodes(WriteGraph graph, TypicalInfo typicalInfo, Collection<Resource> sourceRouteNodes) throws DatabaseException {\r
1058         boolean changed = false;\r
1059         for (Resource src : sourceRouteNodes) {\r
1060             Resource dst = s2t.get(src);\r
1061             if (dst == null) {\r
1062                 throw new DatabaseException("TARGET ROUTE NODE == NULL FOR SRC: " + NameUtils.getSafeName(graph, src));\r
1063             }\r
1064 \r
1065             Collection<Resource> connectedToSrcs = graph.getObjects(src, DIA.AreConnected);\r
1066             Collection<Resource> connectedToDsts = graph.getObjects(dst, DIA.AreConnected);\r
1067 \r
1068             // Remove excess statements\r
1069             for (Resource connectedToDst : connectedToDsts) {\r
1070                 Resource connectedToSrc = t2s.get(connectedToDst);\r
1071                 if (connectedToSrc == null) {\r
1072                     throw new DatabaseException("CONNECTED TO SRC == NULL FOR DST: " + NameUtils.getSafeName(graph, connectedToDst));\r
1073                 }\r
1074                 if (connectedToSrc == null || !graph.hasStatement(src, DIA.AreConnected, connectedToSrc)) {\r
1075                     graph.deny(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);\r
1076                     changed = true;\r
1077                     typicalInfo.messageLog.add("\t\t\tdisconnected route nodes (" + NameUtils.getSafeName(graph, dst) + ", " + NameUtils.getSafeName(graph, connectedToDst) + ")");\r
1078                 }\r
1079             }\r
1080 \r
1081             // Add necessary statements\r
1082             for (Resource connectedToSrc : connectedToSrcs) {\r
1083                 Resource connectedToDst = s2t.get(connectedToSrc);\r
1084                 if (connectedToDst == null) {\r
1085                     throw new DatabaseException("CONNECTED TO DST == NULL FOR SRC: " + NameUtils.getSafeName(graph, connectedToSrc));\r
1086                 }\r
1087                 if (!graph.hasStatement(dst, DIA.AreConnected, connectedToDst)) {\r
1088                     graph.claim(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);\r
1089                     changed = true;\r
1090                     typicalInfo.messageLog.add("\t\t\tconnected route nodes (" + NameUtils.getSafeName(graph, dst) + ", " + NameUtils.getSafeName(graph, connectedToDst) + ")");\r
1091                 }\r
1092             }\r
1093         }\r
1094         return changed;\r
1095     }\r
1096 \r
1097     private void copyRouteLine(WriteGraph graph, Resource src, Resource tgt) throws DatabaseException {\r
1098         Double pos = graph.getPossibleRelatedValue(src, DIA.HasPosition, Bindings.DOUBLE);\r
1099         Boolean hor = graph.getPossibleRelatedValue(src, DIA.IsHorizontal, Bindings.BOOLEAN);\r
1100         if (pos == null)\r
1101             pos = 0.0;\r
1102         if (hor == null)\r
1103             hor = Boolean.TRUE;\r
1104         graph.claimLiteral(tgt, DIA.HasPosition, L0.Double, pos, Bindings.DOUBLE);\r
1105         graph.claimLiteral(tgt, DIA.IsHorizontal, L0.Boolean, hor, Bindings.BOOLEAN);\r
1106     }\r
1107 \r
1108     private static String safeNameAndType(ReadGraph graph, Resource r) throws DatabaseException {\r
1109         StringBuilder sb = new StringBuilder();\r
1110         sb.append(NameUtils.getSafeName(graph, r, true));\r
1111         sb.append(" : [");\r
1112         boolean first = true;\r
1113         for (Resource type : graph.getPrincipalTypes(r)) {\r
1114             if (!first)\r
1115                 sb.append(",");\r
1116             first = false;\r
1117             sb.append(NameUtils.getSafeName(graph, type, true));\r
1118         }\r
1119         sb.append("]");\r
1120         return sb.toString();\r
1121     }\r
1122 \r
1123     private static <K, V> Map<K, V> newOrClear(Map<K, V> current) {\r
1124         if (current == null)\r
1125             return new THashMap<K, V>();\r
1126         current.clear();\r
1127         return current;\r
1128     }\r
1129 \r
1130 }