]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.modeling/src/org/simantics/modeling/flags/MergeFlags.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / flags / MergeFlags.java
index 5f26760b09b801f75e44bc7438e5af73a2052c8a..c8babc497adb4d643256ca3d119d7cd3d28e1701 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2012 Association for Decentralized Information Management in\r
- * Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.modeling.flags;\r
-\r
-import gnu.trove.map.hash.THashMap;\r
-import gnu.trove.set.hash.THashSet;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Statement;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.request.IndexRoot;\r
-import org.simantics.db.common.utils.NameUtils;\r
-import org.simantics.db.common.utils.OrderedSetUtils;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.diagram.content.ConnectionUtil;\r
-import org.simantics.diagram.stubs.DiagramResource;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.modeling.ModelingResources;\r
-import org.simantics.scl.commands.Commands;\r
-import org.simantics.scl.runtime.tuple.Tuple2;\r
-import org.simantics.structural.stubs.StructuralResource2;\r
-\r
-public class MergeFlags {\r
-\r
-    private static class CP {\r
-        public final Resource component;\r
-        public final Resource connectionPoint;\r
-        \r
-        public CP(Resource component, Resource connectionPoint) {\r
-            super();\r
-            this.component = component;\r
-            this.connectionPoint = connectionPoint;\r
-        }\r
-\r
-        @Override\r
-        public int hashCode() {\r
-            return component.hashCode() + 31 * connectionPoint.hashCode();\r
-        }\r
-\r
-        @Override\r
-        public boolean equals(Object obj) {\r
-            if (this == obj)\r
-                return true;\r
-            if (obj == null)\r
-                return false;\r
-            if (getClass() != obj.getClass())\r
-                return false;\r
-            CP other = (CP) obj;\r
-            return component.equals(other.component) \r
-                    && connectionPoint.equals(other.connectionPoint);\r
-        }\r
-    }\r
-    \r
-    public static String validateForMerge(ReadGraph g,\r
-            List<Resource> flags) throws DatabaseException {\r
-        if(flags.size() <= 1)\r
-            return "At least two flags must be chosen.";\r
-        \r
-        DiagramResource DIA = DiagramResource.getInstance(g);\r
-        StructuralResource2 STR = StructuralResource2.getInstance(g);\r
-        \r
-        for(Resource flag : flags)\r
-            if(!g.hasStatement(flag, DIA.FlagIsJoinedBy))\r
-                return "All flags are not joined to other flags.";\r
-\r
-        List<Resource> connectors = getPossibleRelated(g, flags, DIA.Flag_ConnectionPoint);\r
-        for(Resource connector : connectors) {\r
-            if(connector == null)\r
-                return "All flags are not connected";\r
-        }\r
-        \r
-        List<Resource> connections = getConnection(g, flags, connectors);\r
-        for(Resource connection : connections) {\r
-            if(connection == null)\r
-                return "Invalid flag. Didn't find configuration connection.";\r
-        }\r
-        \r
-        THashSet<Resource> uniqueConnections = new THashSet<Resource>(connections.size());\r
-        for(Resource connection : connections) {\r
-            uniqueConnections.add(connection);\r
-        }\r
-        \r
-        if(uniqueConnections.size() == 1)\r
-            return null;\r
-                \r
-        Iterator<Resource> it = uniqueConnections.iterator();\r
-        THashSet<CP> cps = getConnectionPoints(g, STR, it.next());\r
-        while(it.hasNext()) {\r
-            cps.retainAll(getConnectionPoints(g, STR, it.next()));\r
-            if(cps.isEmpty())\r
-                return "Flags are not connected to a common terminal.";\r
-        }\r
-        \r
-        return null;\r
-    }\r
-    \r
-    private static THashSet<CP> getConnectionPoints(ReadGraph g, StructuralResource2 STR, Resource connection) throws DatabaseException {\r
-        THashSet<CP> result = new THashSet<CP>(); \r
-        for(Statement stat : g.getStatements(connection, STR.Connects))\r
-            result.add(new CP(stat.getObject(), g.getInverse(stat.getPredicate())));\r
-        return result;\r
-    }\r
-    \r
-    /**\r
-     * Merges all flags in the given list to one or two flags.\r
-     * @param g\r
-     * @param flags to join\r
-     * @return Error message or empty string if the operation succeeded.\r
-     */\r
-    public static String merge(WriteGraph g, List<Resource> flags) throws DatabaseException {\r
-        return (String)Commands.get(g, "Simantics/Flag/mergeFlags").execute(g, g.syncRequest(new IndexRoot(flags.get(0))), flags);\r
-    }\r
-    \r
-    public static String mergeWithoutMetadata(WriteGraph g, List<Resource> flags) throws DatabaseException {\r
-        THashMap<Tuple2, ArrayList<Resource>> groups = \r
-                new THashMap<Tuple2, ArrayList<Resource>>();\r
-        \r
-        DiagramResource DIA = DiagramResource.getInstance(g);\r
-        StructuralResource2 STR = StructuralResource2.getInstance(g);\r
-        for(Resource flag : flags) {\r
-            Resource connector = g.getSingleObject(flag, DIA.Flag_ConnectionPoint);\r
-            Resource connection = null;\r
-            for(Resource temp : g.getObjects(connector, STR.Connects))\r
-                if(!temp.equals(flag)) {\r
-                    connection = temp;\r
-                    break;\r
-                }\r
-            if(connection == null)\r
-                continue;\r
-            \r
-            Resource flagType = g.getSingleObject(flag, DIA.HasFlagType);\r
-            Resource connectionType = g.getPossibleObject(connection, STR.HasConnectionType);\r
-            \r
-            Tuple2 tuple = new Tuple2(flagType, connectionType);\r
-            ArrayList<Resource> group = groups.get(tuple);\r
-            if(group == null) {\r
-                group = new ArrayList<Resource>();\r
-                groups.put(tuple, group);\r
-            }\r
-            group.add(flag);\r
-        }\r
-    \r
-        String errorMessage = "";\r
-        for(ArrayList<Resource> group : groups.values()) {\r
-            if(group.size() > 1) {\r
-                String temp = mergeAux(g, group);\r
-                if(temp != null)\r
-                    errorMessage = temp;\r
-            }\r
-        }\r
-        return errorMessage;\r
-    }\r
-    \r
-    /**\r
-     * Merges all flags in the given list to the first flag of the list.\r
-     * @param g\r
-     * @param flags to join\r
-     * @return Error message or null if the operation succeeded.\r
-     */\r
-    private static String mergeAux(WriteGraph g, List<Resource> flags) throws DatabaseException {\r
-        if(flags.size() <= 1)\r
-            return null; // Nothing to do\r
-        \r
-        DiagramResource DIA = DiagramResource.getInstance(g);\r
-        StructuralResource2 STR = StructuralResource2.getInstance(g);\r
-        \r
-        // Find connectors\r
-        List<Resource> connectors = getPossibleRelated(g, flags, DIA.Flag_ConnectionPoint);\r
-        for(Resource connector : connectors) {\r
-            if(connector == null)\r
-                return "All flags are not connected";\r
-        }        \r
-        \r
-        // Find configuration connections\r
-        List<Resource> connections = getConnection(g, flags, connectors);\r
-        for(Resource connection : connections) {\r
-            if(connection == null)\r
-                return "Invalid flag. Didn't find configuration connection.";\r
-        }\r
-        \r
-        // Choose canonical flag and connection where other flags are merged to\r
-        Resource canonicalFlag = flags.get(0);\r
-        Resource canonicalConnection = connections.get(0);\r
-        \r
-        // Do the merging\r
-        for(int i=1;i<flags.size();++i) {\r
-            Resource flag = flags.get(i);\r
-            Resource connection = connections.get(i);\r
-            \r
-            // Replace references in joins to the canonical flag and connection\r
-            for(Resource join : g.getObjects(flag, DIA.FlagIsJoinedBy)) {\r
-                g.denyStatement(flag, DIA.FlagIsJoinedBy, join);\r
-                g.claim(canonicalFlag, DIA.FlagIsJoinedBy, join);\r
-                g.denyStatement(join, STR.Joins, connection);\r
-                g.claim(join, STR.Joins, canonicalConnection);\r
-            }\r
-            \r
-            // Remove flag and its connector\r
-            removeElement(g, flag);\r
-            g.deny(connectors.get(i));\r
-        }\r
-        \r
-        // Clean up connections (remove extra route lines and complete connections)\r
-        THashSet<Resource> uniqueConnections = new THashSet<Resource>(connections.size());\r
-        for(int i=1;i<connections.size();++i)\r
-            uniqueConnections.add(connections.get(i));\r
-        \r
-        for(Resource connection : uniqueConnections)\r
-            cleanUpConnection(g, connection);\r
-        \r
-        return null;\r
-    }\r
-\r
-    private static void cleanUpConnection(WriteGraph g, Resource connection) throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(g);\r
-        StructuralResource2 STR = StructuralResource2.getInstance(g);\r
-        ModelingResources MOD = ModelingResources.getInstance(g);\r
-        \r
-        Resource diagramConnection = g.getSingleObject(connection, MOD.ConnectionToDiagramConnection);\r
-        \r
-        // If connection is degenerated, remove it\r
-        if(g.getObjects(connection, STR.IsJoinedBy).size() == 0 &&  \r
-                g.getObjects(connection, STR.Connects).size() <= 1) {\r
-            g.deny(connection);\r
-            // Garbage collection removes interior route nodes etc. stuff\r
-            //removeElement(g, diagramConnection);\r
-            // platform #4473: Remove connection completely to not leave behind stray connectors.\r
-            new ConnectionUtil(g).removeConnection(diagramConnection);\r
-        }\r
-        // Otherwise just remove degenerated route nodes\r
-        else {\r
-            boolean modifiedSomething = true;\r
-            while(modifiedSomething) { // loop until no modification are made (O(n^2) algorithm)\r
-                modifiedSomething = false;\r
-                for(Resource routeNode : g.getObjects(diagramConnection, DIA.HasInteriorRouteNode))\r
-                    if(g.getObjects(routeNode, DIA.AreConnected).size() <= 1) {\r
-                        g.deny(routeNode);\r
-                        modifiedSomething = true;\r
-                    }\r
-            }\r
-        }\r
-    }\r
-    \r
-    private static void removeElement(WriteGraph g, Resource element) throws DatabaseException {\r
-        OrderedSetUtils.remove(g, OrderedSetUtils.getSingleOwnerList(g, element), element);\r
-        g.deny(element);\r
-    }\r
-\r
-    private static List<Resource> getConnection(ReadGraph g, List<Resource> flags, List<Resource> connectors) throws DatabaseException {\r
-        ArrayList<Resource> result = new ArrayList<Resource>(flags.size());\r
-        for(int i=0;i<flags.size();++i)\r
-            result.add(getConnection(g, flags.get(i), connectors.get(i)));\r
-        return result;\r
-    }\r
-    \r
-    private static Resource getConnection(ReadGraph g, Resource flag, Resource connector) throws DatabaseException {\r
-        StructuralResource2 STR = StructuralResource2.getInstance(g);\r
-        ModelingResources MOD = ModelingResources.getInstance(g);\r
-        for(Resource diagramConnection : g.getObjects(connector, STR.Connects)) {\r
-            if(!flag.equals(diagramConnection)) {\r
-                return g.getPossibleObject(diagramConnection, MOD.DiagramConnectionToConnection);\r
-            }\r
-        }\r
-        return null;\r
-    }\r
-    \r
-    private static List<Resource> getPossibleRelated(ReadGraph g, List<Resource> subjects, Resource relation) throws DatabaseException {\r
-        ArrayList<Resource> result = new ArrayList<Resource>(subjects.size());\r
-        for(int i=0;i<subjects.size();++i)\r
-            result.add(g.getPossibleObject(subjects.get(i), relation));\r
-        return result;\r
-    }\r
-\r
-       public static void expandFlagSet(ReadGraph graph, List<Resource> flags) throws DatabaseException {\r
-               DiagramResource DIA = DiagramResource.getInstance(graph);\r
-               StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
-                               \r
-               THashSet<Resource> connectionSet = new THashSet<Resource>();\r
-               \r
-               for(Resource flag : flags) {\r
-                       for(Resource connector : graph.getObjects(flag, STR.IsConnectedTo))\r
-                               for(Resource connection : graph.getObjects(connector, STR.Connects))\r
-                                       if(!connection.equals(flag))\r
-                                               connectionSet.add(connection);\r
-               }\r
-               \r
-               for(Resource connection : connectionSet.toArray(new Resource[connectionSet.size()])) {\r
-                       for(Resource connector : graph.getObjects(connection, STR.IsConnectedTo))\r
-                               for(Statement stat : graph.getStatements(connector, STR.Connects))\r
-                                       if(!stat.getObject().equals(connection))\r
-                                               for(Resource connector2 : graph.getObjects(stat.getObject(), graph.getInverse(stat.getPredicate())))\r
-                                                       if(!connector2.equals(connector))\r
-                                                               for(Resource connection2 : graph.getObjects(connector2, STR.Connects))\r
-                                                                       if(graph.isInstanceOf(connection2, DIA.Connection))\r
-                                                                               connectionSet.add(connection2);\r
-               }\r
-               \r
-               THashSet<Resource> visited = new THashSet<Resource>(flags);\r
-                                               \r
-               for(Resource connection : connectionSet) {\r
-                       visited.add(connection);\r
-                       for(Resource connector : graph.getObjects(connection, STR.IsConnectedTo))\r
-                               for(Resource flag : graph.getObjects(connector, STR.Connects))\r
-                                       if(visited.add(flag) && graph.isInstanceOf(flag, DIA.Flag)\r
-                                               && graph.hasStatement(flag, DIA.FlagIsJoinedBy))\r
-                                               flags.add(flag);\r
-               }                                       \r
-       }   \r
-       \r
-       public static void collectFlagGroupsInComposite(ReadGraph g, Resource composite, ArrayList<ArrayList<Resource>> groups) throws DatabaseException {\r
-           DiagramResource DIA = DiagramResource.getInstance(g);\r
-        ModelingResources MOD = ModelingResources.getInstance(g);\r
-        Layer0 L0 = Layer0.getInstance(g);\r
-        \r
-        Resource diagram = g.getPossibleObject(composite, MOD.CompositeToDiagram);\r
-        if(diagram == null)\r
-            return;\r
-        \r
-        THashSet<Resource> flags = new THashSet<Resource>();\r
-        for(Resource element : g.getObjects(diagram, L0.ConsistsOf))\r
-            if(g.isInstanceOf(element, DIA.Flag) && g.hasStatement(element, DIA.FlagIsJoinedBy))\r
-                flags.add(element);\r
-        \r
-        for(Resource flag : flags.toArray(new Resource[flags.size()])) \r
-            if(flags.contains(flag)) {\r
-                ArrayList<Resource> group = new ArrayList<Resource>();\r
-                group.add(flag);\r
-                expandFlagSet(g, group);\r
-                flags.removeAll(group);\r
-                if(group.size() > 1)\r
-                    groups.add(group);\r
-            }\r
-       }\r
-       \r
-       public static void expandCompositeSet(ReadGraph g, THashSet<Resource> composites) throws DatabaseException {\r
-           for(Resource composite : composites.toArray(new Resource[composites.size()]))\r
-               expandCompositeSet(g, composite, composites);\r
-       }\r
-\r
-    private static void expandCompositeSet(ReadGraph g, Resource composite,\r
-            THashSet<Resource> composites) throws DatabaseException {\r
-        Layer0 L0 = Layer0.getInstance(g);\r
-        StructuralResource2 STR = StructuralResource2.getInstance(g);\r
-        for(Resource child : g.getObjects(composite, L0.ConsistsOf))\r
-            if(g.isInstanceOf(child, STR.Composite))\r
-                if(composites.add(child))\r
-                    expandCompositeSet(g, child, composites);\r
-    }\r
-    \r
-    public static String mergeFlags (WriteGraph graph, Resource diagram ) throws DatabaseException {\r
-       \r
-       ArrayList<ArrayList<Resource>> groups = new ArrayList<ArrayList<Resource>>();\r
-       MergeFlags.collectFlagGroupsInComposite(graph, diagram, groups);\r
-        for(ArrayList<Resource> group : groups) {\r
-               MergeFlags.merge(graph, group);\r
-        }\r
-\r
-               return "Merged flags in diagram resource: " + diagram.toString() + " and name: " + NameUtils.getSafeName(graph, diagram);\r
-    }\r
-    \r
-    public static void expandFlags(WriteGraph graph, Resource diagram) throws DatabaseException {\r
-       ArrayList<Resource> groups = new ArrayList<Resource>();\r
-       ExpandFlags.collectGroupedFlags(graph, diagram, groups);\r
-       for(Resource group : groups) {\r
-               ExpandFlags.expandFlag(graph, group);\r
-       }\r
-    }    \r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2012 Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.modeling.flags;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.set.hash.THashSet;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Statement;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.IndexRoot;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.common.utils.OrderedSetUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.diagram.content.ConnectionUtil;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.scl.commands.Commands;
+import org.simantics.scl.runtime.tuple.Tuple2;
+import org.simantics.structural.stubs.StructuralResource2;
+
+public class MergeFlags {
+
+    private static class CP {
+        public final Resource component;
+        public final Resource connectionPoint;
+        
+        public CP(Resource component, Resource connectionPoint) {
+            super();
+            this.component = component;
+            this.connectionPoint = connectionPoint;
+        }
+
+        @Override
+        public int hashCode() {
+            return component.hashCode() + 31 * connectionPoint.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            CP other = (CP) obj;
+            return component.equals(other.component) 
+                    && connectionPoint.equals(other.connectionPoint);
+        }
+    }
+    
+    public static String validateForMerge(ReadGraph g,
+            List<Resource> flags) throws DatabaseException {
+        if(flags.size() <= 1)
+            return "At least two flags must be chosen.";
+        
+        DiagramResource DIA = DiagramResource.getInstance(g);
+        StructuralResource2 STR = StructuralResource2.getInstance(g);
+        
+        for(Resource flag : flags)
+            if(!g.hasStatement(flag, DIA.FlagIsJoinedBy))
+                return "All flags are not joined to other flags.";
+
+        List<Resource> connectors = getPossibleRelated(g, flags, DIA.Flag_ConnectionPoint);
+        for(Resource connector : connectors) {
+            if(connector == null)
+                return "All flags are not connected";
+        }
+        
+        List<Resource> connections = getConnection(g, flags, connectors);
+        for(Resource connection : connections) {
+            if(connection == null)
+                return "Invalid flag. Didn't find configuration connection.";
+        }
+        
+        THashSet<Resource> uniqueConnections = new THashSet<Resource>(connections.size());
+        for(Resource connection : connections) {
+            uniqueConnections.add(connection);
+        }
+        
+        if(uniqueConnections.size() == 1)
+            return null;
+                
+        Iterator<Resource> it = uniqueConnections.iterator();
+        THashSet<CP> cps = getConnectionPoints(g, STR, it.next());
+        while(it.hasNext()) {
+            cps.retainAll(getConnectionPoints(g, STR, it.next()));
+            if(cps.isEmpty())
+                return "Flags are not connected to a common terminal.";
+        }
+        
+        return null;
+    }
+    
+    private static THashSet<CP> getConnectionPoints(ReadGraph g, StructuralResource2 STR, Resource connection) throws DatabaseException {
+        THashSet<CP> result = new THashSet<CP>(); 
+        for(Statement stat : g.getStatements(connection, STR.Connects))
+            result.add(new CP(stat.getObject(), g.getInverse(stat.getPredicate())));
+        return result;
+    }
+    
+    /**
+     * Merges all flags in the given list to one or two flags.
+     * @param g
+     * @param flags to join
+     * @return Error message or empty string if the operation succeeded.
+     */
+    public static String merge(WriteGraph g, List<Resource> flags) throws DatabaseException {
+        return (String)Commands.get(g, "Simantics/Flag/mergeFlags").execute(g, g.syncRequest(new IndexRoot(flags.get(0))), flags);
+    }
+    
+    public static String mergeWithoutMetadata(WriteGraph g, List<Resource> flags) throws DatabaseException {
+        THashMap<Tuple2, ArrayList<Resource>> groups = 
+                new THashMap<Tuple2, ArrayList<Resource>>();
+        
+        DiagramResource DIA = DiagramResource.getInstance(g);
+        StructuralResource2 STR = StructuralResource2.getInstance(g);
+        for(Resource flag : flags) {
+            Resource connector = g.getSingleObject(flag, DIA.Flag_ConnectionPoint);
+            Resource connection = null;
+            for(Resource temp : g.getObjects(connector, STR.Connects))
+                if(!temp.equals(flag)) {
+                    connection = temp;
+                    break;
+                }
+            if(connection == null)
+                continue;
+            
+            Resource flagType = g.getSingleObject(flag, DIA.HasFlagType);
+            Resource connectionType = g.getPossibleObject(connection, STR.HasConnectionType);
+            
+            Tuple2 tuple = new Tuple2(flagType, connectionType);
+            ArrayList<Resource> group = groups.get(tuple);
+            if(group == null) {
+                group = new ArrayList<Resource>();
+                groups.put(tuple, group);
+            }
+            group.add(flag);
+        }
+    
+        String errorMessage = "";
+        for(ArrayList<Resource> group : groups.values()) {
+            if(group.size() > 1) {
+                String temp = mergeAux(g, group);
+                if(temp != null)
+                    errorMessage = temp;
+            }
+        }
+        return errorMessage;
+    }
+    
+    /**
+     * Merges all flags in the given list to the first flag of the list.
+     * @param g
+     * @param flags to join
+     * @return Error message or null if the operation succeeded.
+     */
+    private static String mergeAux(WriteGraph g, List<Resource> flags) throws DatabaseException {
+        if(flags.size() <= 1)
+            return null; // Nothing to do
+        
+        DiagramResource DIA = DiagramResource.getInstance(g);
+        StructuralResource2 STR = StructuralResource2.getInstance(g);
+        
+        // Find connectors
+        List<Resource> connectors = getPossibleRelated(g, flags, DIA.Flag_ConnectionPoint);
+        for(Resource connector : connectors) {
+            if(connector == null)
+                return "All flags are not connected";
+        }        
+        
+        // Find configuration connections
+        List<Resource> connections = getConnection(g, flags, connectors);
+        for(Resource connection : connections) {
+            if(connection == null)
+                return "Invalid flag. Didn't find configuration connection.";
+        }
+        
+        // Choose canonical flag and connection where other flags are merged to
+        Resource canonicalFlag = flags.get(0);
+        Resource canonicalConnection = connections.get(0);
+        
+        // Do the merging
+        for(int i=1;i<flags.size();++i) {
+            Resource flag = flags.get(i);
+            Resource connection = connections.get(i);
+            
+            // Replace references in joins to the canonical flag and connection
+            for(Resource join : g.getObjects(flag, DIA.FlagIsJoinedBy)) {
+                g.denyStatement(flag, DIA.FlagIsJoinedBy, join);
+                g.claim(canonicalFlag, DIA.FlagIsJoinedBy, join);
+                g.denyStatement(join, STR.Joins, connection);
+                g.claim(join, STR.Joins, canonicalConnection);
+            }
+            
+            // Remove flag and its connector
+            removeElement(g, flag);
+            g.deny(connectors.get(i));
+        }
+        
+        // Clean up connections (remove extra route lines and complete connections)
+        THashSet<Resource> uniqueConnections = new THashSet<Resource>(connections.size());
+        for(int i=1;i<connections.size();++i)
+            uniqueConnections.add(connections.get(i));
+        
+        for(Resource connection : uniqueConnections)
+            cleanUpConnection(g, connection);
+        
+        return null;
+    }
+
+    private static void cleanUpConnection(WriteGraph g, Resource connection) throws DatabaseException {
+        DiagramResource DIA = DiagramResource.getInstance(g);
+        StructuralResource2 STR = StructuralResource2.getInstance(g);
+        ModelingResources MOD = ModelingResources.getInstance(g);
+        
+        Resource diagramConnection = g.getSingleObject(connection, MOD.ConnectionToDiagramConnection);
+        
+        // If connection is degenerated, remove it
+        if(g.getObjects(connection, STR.IsJoinedBy).size() == 0 &&  
+                g.getObjects(connection, STR.Connects).size() <= 1) {
+            g.deny(connection);
+            // Garbage collection removes interior route nodes etc. stuff
+            //removeElement(g, diagramConnection);
+            // platform #4473: Remove connection completely to not leave behind stray connectors.
+            new ConnectionUtil(g).removeConnection(diagramConnection);
+        }
+        // Otherwise just remove degenerated route nodes
+        else {
+            boolean modifiedSomething = true;
+            while(modifiedSomething) { // loop until no modification are made (O(n^2) algorithm)
+                modifiedSomething = false;
+                for(Resource routeNode : g.getObjects(diagramConnection, DIA.HasInteriorRouteNode))
+                    if(g.getObjects(routeNode, DIA.AreConnected).size() <= 1) {
+                        g.deny(routeNode);
+                        modifiedSomething = true;
+                    }
+            }
+        }
+    }
+    
+    private static void removeElement(WriteGraph g, Resource element) throws DatabaseException {
+        OrderedSetUtils.remove(g, OrderedSetUtils.getSingleOwnerList(g, element), element);
+        g.deny(element);
+    }
+
+    private static List<Resource> getConnection(ReadGraph g, List<Resource> flags, List<Resource> connectors) throws DatabaseException {
+        ArrayList<Resource> result = new ArrayList<Resource>(flags.size());
+        for(int i=0;i<flags.size();++i)
+            result.add(getConnection(g, flags.get(i), connectors.get(i)));
+        return result;
+    }
+    
+    private static Resource getConnection(ReadGraph g, Resource flag, Resource connector) throws DatabaseException {
+        StructuralResource2 STR = StructuralResource2.getInstance(g);
+        ModelingResources MOD = ModelingResources.getInstance(g);
+        for(Resource diagramConnection : g.getObjects(connector, STR.Connects)) {
+            if(!flag.equals(diagramConnection)) {
+                return g.getPossibleObject(diagramConnection, MOD.DiagramConnectionToConnection);
+            }
+        }
+        return null;
+    }
+    
+    private static List<Resource> getPossibleRelated(ReadGraph g, List<Resource> subjects, Resource relation) throws DatabaseException {
+        ArrayList<Resource> result = new ArrayList<Resource>(subjects.size());
+        for(int i=0;i<subjects.size();++i)
+            result.add(g.getPossibleObject(subjects.get(i), relation));
+        return result;
+    }
+
+       public static void expandFlagSet(ReadGraph graph, List<Resource> flags) throws DatabaseException {
+               DiagramResource DIA = DiagramResource.getInstance(graph);
+               StructuralResource2 STR = StructuralResource2.getInstance(graph);
+                               
+               THashSet<Resource> connectionSet = new THashSet<Resource>();
+               
+               for(Resource flag : flags) {
+                       for(Resource connector : graph.getObjects(flag, STR.IsConnectedTo))
+                               for(Resource connection : graph.getObjects(connector, STR.Connects))
+                                       if(!connection.equals(flag))
+                                               connectionSet.add(connection);
+               }
+               
+               for(Resource connection : connectionSet.toArray(new Resource[connectionSet.size()])) {
+                       for(Resource connector : graph.getObjects(connection, STR.IsConnectedTo))
+                               for(Statement stat : graph.getStatements(connector, STR.Connects))
+                                       if(!stat.getObject().equals(connection))
+                                               for(Resource connector2 : graph.getObjects(stat.getObject(), graph.getInverse(stat.getPredicate())))
+                                                       if(!connector2.equals(connector))
+                                                               for(Resource connection2 : graph.getObjects(connector2, STR.Connects))
+                                                                       if(graph.isInstanceOf(connection2, DIA.Connection))
+                                                                               connectionSet.add(connection2);
+               }
+               
+               THashSet<Resource> visited = new THashSet<Resource>(flags);
+                                               
+               for(Resource connection : connectionSet) {
+                       visited.add(connection);
+                       for(Resource connector : graph.getObjects(connection, STR.IsConnectedTo))
+                               for(Resource flag : graph.getObjects(connector, STR.Connects))
+                                       if(visited.add(flag) && graph.isInstanceOf(flag, DIA.Flag)
+                                               && graph.hasStatement(flag, DIA.FlagIsJoinedBy))
+                                               flags.add(flag);
+               }                                       
+       }   
+       
+       public static void collectFlagGroupsInComposite(ReadGraph g, Resource composite, ArrayList<ArrayList<Resource>> groups) throws DatabaseException {
+           DiagramResource DIA = DiagramResource.getInstance(g);
+        ModelingResources MOD = ModelingResources.getInstance(g);
+        Layer0 L0 = Layer0.getInstance(g);
+        
+        Resource diagram = g.getPossibleObject(composite, MOD.CompositeToDiagram);
+        if(diagram == null)
+            return;
+        
+        THashSet<Resource> flags = new THashSet<Resource>();
+        for(Resource element : g.getObjects(diagram, L0.ConsistsOf))
+            if(g.isInstanceOf(element, DIA.Flag) && g.hasStatement(element, DIA.FlagIsJoinedBy))
+                flags.add(element);
+        
+        for(Resource flag : flags.toArray(new Resource[flags.size()])) 
+            if(flags.contains(flag)) {
+                ArrayList<Resource> group = new ArrayList<Resource>();
+                group.add(flag);
+                expandFlagSet(g, group);
+                flags.removeAll(group);
+                if(group.size() > 1)
+                    groups.add(group);
+            }
+       }
+       
+       public static void expandCompositeSet(ReadGraph g, THashSet<Resource> composites) throws DatabaseException {
+           for(Resource composite : composites.toArray(new Resource[composites.size()]))
+               expandCompositeSet(g, composite, composites);
+       }
+
+    private static void expandCompositeSet(ReadGraph g, Resource composite,
+            THashSet<Resource> composites) throws DatabaseException {
+        Layer0 L0 = Layer0.getInstance(g);
+        StructuralResource2 STR = StructuralResource2.getInstance(g);
+        for(Resource child : g.getObjects(composite, L0.ConsistsOf))
+            if(g.isInstanceOf(child, STR.Composite))
+                if(composites.add(child))
+                    expandCompositeSet(g, child, composites);
+    }
+    
+    public static String mergeFlags (WriteGraph graph, Resource diagram ) throws DatabaseException {
+       
+       ArrayList<ArrayList<Resource>> groups = new ArrayList<ArrayList<Resource>>();
+       MergeFlags.collectFlagGroupsInComposite(graph, diagram, groups);
+        for(ArrayList<Resource> group : groups) {
+               MergeFlags.merge(graph, group);
+        }
+
+               return "Merged flags in diagram resource: " + diagram.toString() + " and name: " + NameUtils.getSafeName(graph, diagram);
+    }
+    
+    public static void expandFlags(WriteGraph graph, Resource diagram) throws DatabaseException {
+       ArrayList<Resource> groups = new ArrayList<Resource>();
+       ExpandFlags.collectGroupedFlags(graph, diagram, groups);
+       for(Resource group : groups) {
+               ExpandFlags.expandFlag(graph, group);
+       }
+    }    
+}