Browse Source

now circular containment links are properly dealt with (thank you Yentl for the challenge)

Simon Van Mierlo 8 years ago
parent
commit
eb5581e661
3 changed files with 80 additions and 19 deletions
  1. 18 9
      client/geometry_utils.js
  2. 16 8
      client/mmm_utils.js
  3. 46 2
      mmmk.js

+ 18 - 9
client/geometry_utils.js

@@ -179,9 +179,11 @@ GeometryUtils = function(){
 	 * Resizes the containers of icons (specified as uri array) that have moved within
 	 * them as required and uninsert dragged-out icons.
 	 */
-	this.resizeContainers = function(icons,context,dryRun,disabledDragouts) {
+	this.resizeContainers = function(icons,context,dryRun,disabledDragouts,reqs) {
 		if( icons.length == 0 )
 			return (dryRun ? [] : undefined);
+        if( reqs == undefined )
+            reqs = [];
 	
 		var requests 			  = [],
 			 containers2changes = {},
@@ -245,7 +247,11 @@ GeometryUtils = function(){
 	 				 }
 				 };
 	
-		icons.forEach(
+		icons.filter(
+            function(ic) {
+                return reqs.map(function(_node) {return _node['uri'];}).indexOf(ic + '.cs') < 0;
+            })
+            .forEach(
 			function(it)
 			{
 				if( !(it in __icons) || __isConnectionType(it) )
@@ -276,13 +282,16 @@ GeometryUtils = function(){
 					 'uri':HttpUtils.url(uri+'.cs',__NO_USERNAME+__NO_WID),
 					 'reqData':{'changes':containers2changes[uri]}});
 	
-		requests = 
-			requests.concat(
-				utils.flatten(
-					GeometryUtils.resizeContainers(
-						 utils.keys(containers2changes),
-						 containers2changes,
-						 true)));
+        for (var req_id in requests) {
+            var to_concat = utils.flatten(GeometryUtils.resizeContainers(
+                                                         utils.keys(containers2changes),
+                                                         containers2changes,
+                                                         true,
+                                                         false,
+                                                         requests)
+                                         )
+            requests = requests.concat(to_concat);
+        }
 	
 		if( dryRun )
 			return requests;	

+ 16 - 8
client/mmm_utils.js

@@ -194,9 +194,11 @@ function __createEdge(segments,style,edgeId,linkuri)
     for (var id in __icons) {
         __icons[id]['ordernr'] = undefined;
     }
-    function getOrderNr(id) {
+    function getOrderNr(id,visited) {
         var icon = __icons[id];
+        if (visited.indexOf(icon) > 0) return
         if (icon['ordernr']) return;
+        visited.push(icon);
         if (__isConnectionType(id)) {
             // I like my edges as I like my women: always on top
             icon['ordernr'] = 9999;
@@ -204,7 +206,7 @@ function __createEdge(segments,style,edgeId,linkuri)
             for (var edgeId in icon['edgesIn']) {
                 var associationid = __edges[icon['edgesIn'][edgeId]]['start']
                 if (__isContainmentConnectionType(associationid)) {
-                    getOrderNr(__edges[__icons[associationid]['edgesIn'][0]]['start']);
+                    getOrderNr(__edges[__icons[associationid]['edgesIn'][0]]['start'], visited);
                     icon['ordernr'] = __icons[__edges[__icons[associationid]['edgesIn'][0]]['start']]['ordernr'] + 1
                 }
             }
@@ -214,7 +216,7 @@ function __createEdge(segments,style,edgeId,linkuri)
         }
     }
     for (var id in __icons) {
-        getOrderNr(id);
+        getOrderNr(id, []);
     }
     
     Object.keys(__icons).concat().sort(function(a, b) {return __icons[a]['ordernr'] - __icons[b]['ordernr']}).forEach(function(el) {
@@ -426,10 +428,14 @@ function __legalConnections(uri1,uri2,ctype)
 	3. return 'setified' concatenation of results from steps 1 and 2 */
 function __getIconsInContainer(container)
 {
-	function getExplicitContents(container)
+	function getExplicitContents(container, contents)
 	{
 		if( __isConnectionType(container) )
 			return [];
+        
+        if( contents.indexOf(container) > -1 ) {
+            return []
+        }
 	
 		var contents = 
 			utils.flatten(__icons[container]['edgesOut'].map(
@@ -446,12 +452,14 @@ function __getIconsInContainer(container)
 								}).concat([linkuri]);
 				}));
 
-		return utils.toSet(
-					contents.concat(
-						utils.flatten(contents.map(getExplicitContents))));
+        for (var ct_idx in contents) {
+            var to_concat = utils.flatten(getExplicitContents(contents[ct_idx], contents));
+            contents.concat(to_concat);
+        }
+        return utils.toSet(contents);
 	}
 
-	var explicitContents = getExplicitContents(container),
+	var explicitContents = getExplicitContents(container, []),
 		 implicitContents = [];
 
 	for( var uri in __icons )

+ 46 - 2
mmmk.js

@@ -874,7 +874,9 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 		{
 			var inCounts 	= {},
 				 outCounts	= {},
-				 model 		= (model == undefined ? this.model : model);
+				 model 		= (model == undefined ? this.model : model),
+                 outContainments = {},
+                 containmentTargets = {};
 
 			if( model.nodes == undefined ||
 				 model.edges == undefined ||
@@ -886,7 +888,9 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 			{
 				var edge 	 = model.edges[i],
 					 srcType  = this.__getType(model.nodes[edge['src']]['$type']),
-					 destType = this.__getType(model.nodes[edge['dest']]['$type']);
+					 destType = this.__getType(model.nodes[edge['dest']]['$type']),
+                     srcMetamodel = this.__getMetamodel(model.nodes[edge['src']]['$type']),
+                     destMetamodel = this.__getMetamodel(model.nodes[edge['dest']]['$type']);
 				
 				if( inCounts[edge['dest']] == undefined )
 					inCounts[edge['dest']] = {};
@@ -899,8 +903,23 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 				if( outCounts[edge['src']][destType] == undefined )
 					outCounts[edge['src']][destType] = 0;
 				outCounts[edge['src']][destType]++;
+                
+                if ( outContainments[edge['src']] == undefined ) {
+                    outContainments[edge['src']] = [];
+                }
+                if (destType in this.metamodels[destMetamodel]['connectorTypes'] && this.metamodels[destMetamodel]['connectorTypes'][destType] == 'containment') {
+                    outContainments[edge['src']].push(edge['dest']);
+                }
+                
+                if ( containmentTargets[edge['src']] == undefined ) {
+                    containmentTargets[edge['src']] = [];
+                }
+                if (srcType in this.metamodels[srcMetamodel]['connectorTypes'] && this.metamodels[srcMetamodel]['connectorTypes'][srcType] == 'containment') {
+                    containmentTargets[edge['src']].push(edge['dest']);
+                }
 			}
 
+            var checked_for_loops = []
 			for( var id in model.nodes )
 			{
 				var metamodel = this.__getMetamodel(model.nodes[id]['$type']),
@@ -917,6 +936,31 @@ with AToMPM.  If not, see <http://www.gnu.org/licenses/>.
 								cardinality['min'] > (inCounts[id] == undefined || inCounts[id][tc] == undefined ? 0 : inCounts[id][tc]) )
 						return {'$err':'insufficient incoming connections of type '+tc+' for '+model.nodes[id]['$type']+'/'+id};
 				}
+                
+                if (checked_for_loops.indexOf(id) < 0 && !(type in this.metamodels[metamodel]['connectorTypes'])) {
+                    var visited = [],
+                        tv = [id];
+                    function dfs(to_visit) {
+                        var curr = to_visit.pop();
+                        if( curr == undefined )
+                            return undefined; // no more to check
+                        else if( visited.indexOf(curr) > -1 )
+                            return {'$err':'containment loop found for ' + model.nodes[id]['$type']+'/'+id}; // error: loop found!
+                        else {
+                            visited.push(curr);
+                            // find all (containment) associations linked to the object, and add their targets to the to_visit list.
+                            for ( var oc_idx in outContainments[curr] ) {
+                                to_visit = to_visit.concat(containmentTargets[outContainments[curr][oc_idx]]);
+                            }
+                            return dfs( to_visit );
+                        }
+                    }
+                    var res = dfs(tv);
+                    if (res != undefined) {
+                        return res;
+                    }
+                    checked_for_loops= checked_for_loops.concat(visited);
+                }
 			}
 			
 			for( var metamodel in this.metamodels )