Explorar o código

#1183: grouping threshold is now taken int account for "Depends on..." graphs (i.e. grouping backwards)

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@3875 a333f486-631f-4898-b8df-5754b55c2be0
dflaven %!s(int64=9) %!d(string=hai) anos
pai
achega
5e5841c9e6
Modificáronse 1 ficheiros con 200 adicións e 111 borrados
  1. 200 111
      core/displayablegraph.class.inc.php

+ 200 - 111
core/displayablegraph.class.inc.php

@@ -236,134 +236,213 @@ class DisplayableNode extends GraphNode
 		return is_object($this->GetProperty('object', null)) ? get_class($this->GetProperty('object', null)) : null;
 	}
 	
+	protected function AddToStats($oNode, &$aNodesPerClass)
+	{
+		$sClass = $oNode->GetObjectClass();
+		if (!array_key_exists($sClass, $aNodesPerClass))
+		{
+			$aNodesPerClass[$sClass] = array(
+				'reached' => array(
+					'count' => 0,
+					'nodes' => array(),
+					'icon_url' => $oNode->GetProperty('icon_url'),
+				),
+				'not_reached' => array(
+					'count' => 0,
+					'nodes' => array(),
+					'icon_url' => $oNode->GetProperty('icon_url'),
+				)
+			);
+		}
+		$sKey = $oNode->GetProperty('is_reached') ? 'reached' : 'not_reached';
+		if (!array_key_exists($oNode->GetId(), $aNodesPerClass[$sClass][$sKey]['nodes']))
+		{
+			$aNodesPerClass[$sClass][$sKey]['nodes'][$oNode->GetId()] = $oNode;
+			$aNodesPerClass[$sClass][$sKey]['count'] += $oNode->GetObjectCount();
+		}		
+	}
+	
 	/**
-	 * Group together (as a special kind of nodes) all the similar neighbours of the current node
-	 * @param DisplayableGraph $oGraph
-	 * @param int $iThresholdCount
-	 * @param boolean $bDirectionUp
-	 * @param boolean $bDirectionDown
+	 * Retrieves the list of neighbour nodes, in the given direction: 'up' or 'down'
+	 * @param bool $bDirectionDown
+	 * @return multitype:NULL
 	 */
-	public function GroupSimilarNeighbours(DisplayableGraph $oGraph, $iThresholdCount, $bDirectionUp = false, $bDirectionDown = true)
+	protected function GetNextNodes($bDirectionDown = true)
 	{
-		if ($this->GetProperty('grouped') === true) return;
-		$this->SetProperty('grouped', true);
-			
+		$aNextNodes = array();
 		if ($bDirectionDown)
 		{
-			$aNodesPerClass = array();
 			foreach($this->GetOutgoingEdges() as $oEdge)
 			{
-				$oNode = $oEdge->GetSinkNode();
-				$sClass = $oNode->GetObjectClass();
-				if ($sClass !== null)
+				$aNextNodes[] = $oEdge->GetSinkNode();
+			}
+		}
+		else
+		{
+			foreach($this->GetIncomingEdges() as $oEdge)
+			{
+				$aNextNodes[] = $oEdge->GetSourceNode();
+			}	
+		}
+		return $aNextNodes;
+	}
+	
+	/**
+	 * Replaces the next neighbour node (in the given direction: 'up' or 'down') by the supplied group node
+	 * preserving the connectivity of the graph
+	 * @param DisplayableGraph $oGraph
+	 * @param DisplayableNode $oNextNode
+	 * @param DisplayableGroupNode $oNewNode
+	 * @param bool $bDirectionDown
+	 */
+	protected function ReplaceNextNodeBy(DisplayableGraph $oGraph, DisplayableNode $oNextNode, DisplayableGroupNode $oNewNode, $bDirectionDown = true)
+	{
+		$sClass = $oNewNode->GetProperty('class');
+		if ($bDirectionDown)
+		{
+			foreach($oNextNode->GetIncomingEdges() as $oEdge)
+			{
+				if ($oEdge->GetSourceNode()->GetId() !== $this->GetId())
 				{
-					if (!array_key_exists($sClass, $aNodesPerClass))
+					try
 					{
-						$aNodesPerClass[$sClass] = array(
-							'reached' => array(
-								'count' => 0,
-								'nodes' => array(),
-								'icon_url' => $oNode->GetProperty('icon_url'),
-							),
-							'not_reached' => array(
-								'count' => 0,
-								'nodes' => array(),
-								'icon_url' => $oNode->GetProperty('icon_url'),
-							)
-						);
+						$oNewEdge = new DisplayableEdge($oGraph, $oEdge->GetId().'::'.$sClass, $oEdge->GetSourceNode(), $oNewNode);
 					}
-					$sKey = $oNode->GetProperty('is_reached') ? 'reached' : 'not_reached';
-					if (!array_key_exists($oNode->GetId(), $aNodesPerClass[$sClass][$sKey]['nodes']))
+					catch(Exception $e)
 					{
-						$aNodesPerClass[$sClass][$sKey]['nodes'][$oNode->GetId()] = $oNode;
-						$aNodesPerClass[$sClass][$sKey]['count'] += $oNode->GetObjectCount();
+						// ignore this edge
 					}
 				}
-				else
+			}
+			foreach($oNextNode->GetOutgoingEdges() as $oEdge)
+			{
+				try
+				{
+					$oNewEdge = new DisplayableEdge($oGraph, $oEdge->GetId().'::'.$sClass, $oNewNode, $oEdge->GetSinkNode());
+				}
+				catch(Exception $e)
 				{
-					$oNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
+					// ignore this edge
 				}
 			}
-			foreach($aNodesPerClass as $sClass => $aDefs)
+		}
+		else
+		{
+			foreach($oNextNode->GetOutgoingEdges() as $oEdge)
 			{
-				foreach($aDefs as $sStatus => $aGroupProps)
+				if ($oEdge->GetSinkNode()->GetId() !== $this->GetId())
 				{
-					if (count($aGroupProps['nodes']) >= $iThresholdCount)
+					try
 					{
-						$sNewId = $this->GetId().'::'.$sClass.'/'.(($sStatus == 'reached') ? '_reached': '');
-						$oNewNode = $oGraph->GetNode($sNewId);
-						if ($oNewNode == null)
-						{
-							$oNewNode = new DisplayableGroupNode($oGraph, $sNewId);
-							$oNewNode->SetProperty('label', 'x'.$aGroupProps['count']);
-							$oNewNode->SetProperty('icon_url', $aGroupProps['icon_url']);
-							$oNewNode->SetProperty('class', $sClass);
-							$oNewNode->SetProperty('is_reached', ($sStatus == 'reached'));
-							$oNewNode->SetProperty('count', $aGroupProps['count']);
-						}
-						
-						try
+						$oNewEdge = new DisplayableEdge($oGraph, $oEdge->GetId().'::'.$sClass, $oNewNode, $oEdge->GetSinkNode());
+					}
+					catch(Exception $e)
+					{
+						// ignore this edge
+					}
+				}
+			}
+			foreach($oNextNode->GetIncomingEdges() as $oEdge)
+			{
+				try
+				{
+					$oNewEdge = new DisplayableEdge($oGraph, $oEdge->GetId().'::'.$sClass, $oEdge->GetSourceNode(), $oNewNode);
+				}
+				catch(Exception $e)
+				{
+					// ignore this edge
+				}
+			}
+		}
+		
+		if ($oGraph->GetNode($oNextNode->GetId()))
+		{
+			$oGraph->_RemoveNode($oNextNode);
+			if ($oNextNode instanceof DisplayableGroupNode)
+			{
+				// Copy all the objects of the previous group into the new group
+				foreach($oNextNode->GetObjects() as $oObj)
+				{
+					$oNewNode->AddObject($oObj);
+				}
+			}
+			else
+			{
+				$oNewNode->AddObject($oNextNode->GetProperty('object'));
+			}
+		}			
+	}
+	
+	/**
+	 * Group together (as a special kind of nodes) all the similar neighbours of the current node
+	 * @param DisplayableGraph $oGraph
+	 * @param int $iThresholdCount
+	 * @param boolean $bDirectionUp
+	 * @param boolean $bDirectionDown
+	 */
+	public function GroupSimilarNeighbours(DisplayableGraph $oGraph, $iThresholdCount, $bDirectionUp = false, $bDirectionDown = true)
+	{
+		if ($this->GetProperty('grouped') === true) return;
+		$this->SetProperty('grouped', true);
+		
+		$aNodesPerClass = array();
+		foreach($this->GetNextNodes($bDirectionDown) as $oNode)
+		{
+			$sClass = $oNode->GetObjectClass();
+			if ($sClass !== null)
+			{
+				$this->AddToStats($oNode, $aNodesPerClass);
+			}
+			else
+			{
+				$oNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
+			}
+		}
+		foreach($aNodesPerClass as $sClass => $aDefs)
+		{
+			foreach($aDefs as $sStatus => $aGroupProps)
+			{
+				if (count($aGroupProps['nodes']) >= $iThresholdCount)
+				{
+					$sNewId = $this->GetId().'::'.$sClass.'/'.(($sStatus == 'reached') ? '_reached': '');
+					$oNewNode = $oGraph->GetNode($sNewId);
+					if ($oNewNode == null)
+					{
+						$oNewNode = new DisplayableGroupNode($oGraph, $sNewId);
+						$oNewNode->SetProperty('label', 'x'.$aGroupProps['count']);
+						$oNewNode->SetProperty('icon_url', $aGroupProps['icon_url']);
+						$oNewNode->SetProperty('class', $sClass);
+						$oNewNode->SetProperty('is_reached', ($sStatus == 'reached'));
+						$oNewNode->SetProperty('count', $aGroupProps['count']);
+					}
+					
+					try
+					{
+						if ($bDirectionDown)
 						{
 							$oIncomingEdge = new DisplayableEdge($oGraph, $this->GetId().'-'.$oNewNode->GetId(), $this, $oNewNode);
 						}
-						catch(Exception $e)
+						else
 						{
-							// Ignore this redundant egde
-						}				
-						
-						foreach($aGroupProps['nodes'] as $oNode)
-						{
-							foreach($oNode->GetIncomingEdges() as $oEdge)
-							{
-								if ($oEdge->GetSourceNode()->GetId() !== $this->GetId())
-								{
-									try
-									{
-										$oNewEdge = new DisplayableEdge($oGraph, $oEdge->GetId().'::'.$sClass, $oEdge->GetSourceNode(), $oNewNode);
-									}
-									catch(Exception $e)
-									{
-										// ignore this edge
-									}
-								}
-							}
-							foreach($oNode->GetOutgoingEdges() as $oEdge)
-							{
-								$aOutgoing[] = $oEdge->GetSinkNode();
-								try
-								{
-									$oNewEdge = new DisplayableEdge($oGraph, $oEdge->GetId().'::'.$sClass, $oNewNode, $oEdge->GetSinkNode());
-								}
-								catch(Exception $e)
-								{
-									// ignore this edge
-								}
-							}
-							if ($oGraph->GetNode($oNode->GetId()))
-							{
-								$oGraph->_RemoveNode($oNode);
-								if ($oNode instanceof DisplayableGroupNode)
-								{
-									// Copy all the objects of the previous group into the new group
-									foreach($oNode->GetObjects() as $oObj)
-									{
-										$oNewNode->AddObject($oObj);
-									}
-								}
-								else
-								{
-									$oNewNode->AddObject($oNode->GetProperty('object'));
-								}								
-							}
+							$oOutgoingEdge = new DisplayableEdge($oGraph, $this->GetId().'-'.$oNewNode->GetId(), $oNewNode, $this);
 						}
-						$oNewNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
 					}
-					else
+					catch(Exception $e)
 					{
-						foreach($aGroupProps['nodes'] as $oNode)
-						{
-							$oNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
-						}
+						// Ignore this redundant egde
+					}				
+					
+					foreach($aGroupProps['nodes'] as $oNextNode)
+					{
+						$this->ReplaceNextNodeBy($oGraph, $oNextNode, $oNewNode, $bDirectionDown);
+					}
+					$oNewNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
+				}
+				else
+				{
+					foreach($aGroupProps['nodes'] as $oNode)
+					{
+						$oNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
 					}
 				}
 			}
@@ -536,8 +615,11 @@ class DisplayableRedundancyNode extends DisplayableNode
 									$oNewEdge = new DisplayableEdge($oGraph, '-'.$oEdge->GetId().'::'.$sClass.'/'.$sStatus, $oNewNode, $oEdge->GetSinkNode());
 								}
 							}
-							$oGraph->_RemoveNode($oNode);
-							$oNewNode->AddObject($oNode->GetProperty('object'));
+							if ($oGraph->HasNode($oNode->GetId()))
+							{
+								$oGraph->_RemoveNode($oNode);
+								$oNewNode->AddObject($oNode->GetProperty('object'));
+							}
 						}
 						//$oNewNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
 					}
@@ -627,14 +709,17 @@ class DisplayableGroupNode extends DisplayableNode
 		$this->aObjects = array();
 	}
 	
-	public function AddObject(DBObject $oObj)
+	public function AddObject(DBObject $oObj = null)
 	{
-		$sPrevClass = $this->GetObjectClass();
-		if (($sPrevClass !== null) && (get_class($oObj) !== $sPrevClass))
+		if (is_object($oObj))
 		{
-			throw new Exception("Error: adding an object of class '".get_class($oObj)."' to a group of '$sPrevClass' objects.");
+			$sPrevClass = $this->GetObjectClass();
+			if (($sPrevClass !== null) && (get_class($oObj) !== $sPrevClass))
+			{
+				throw new Exception("Error: adding an object of class '".get_class($oObj)."' to a group of '$sPrevClass' objects.");
+			}
+			$this->aObjects[$oObj->GetKey()] = $oObj;
 		}
-		$this->aObjects[$oObj->GetKey()] = $oObj;
 	}
 	
 	public function GetObjects()
@@ -863,9 +948,13 @@ class DisplayableGraph extends SimpleGraph
 		foreach($oNodesIter as $oNode)
 		{
 			set_time_limit($iLoopTimeLimit);
-			if ($oNode->GetProperty('source'))
+			if ($bDirectionDown && $oNode->GetProperty('source'))
+			{
+				$oNode->GroupSimilarNeighbours($oNewGraph, $iGroupingThreshold, true, $bDirectionDown);
+			}
+			else if (!$bDirectionDown && $oNode->GetProperty('sink'))
 			{
-				$oNode->GroupSimilarNeighbours($oNewGraph, $iGroupingThreshold, true, true);
+				$oNode->GroupSimilarNeighbours($oNewGraph, $iGroupingThreshold, true, $bDirectionDown);
 			}
 		}
 		// Groups numbering