diff --git a/core/displayablegraph.class.inc.php b/core/displayablegraph.class.inc.php index 7ba48188c..cafebaecc 100644 --- a/core/displayablegraph.class.inc.php +++ b/core/displayablegraph.class.inc.php @@ -226,6 +226,16 @@ class DisplayableNode extends GraphNode } } + public function GetObjectCount() + { + return 1; + } + + public function GetObjectClass() + { + return is_object($this->GetProperty('object', null)) ? get_class($this->GetProperty('object', null)) : null; + } + /** * Group together (as a special kind of nodes) all the similar neighbours of the current node * @param DisplayableGraph $oGraph @@ -244,11 +254,10 @@ class DisplayableNode extends GraphNode foreach($this->GetOutgoingEdges() as $oEdge) { $oNode = $oEdge->GetSinkNode(); - - if ($oNode->GetProperty('class') !== null) + $sClass = $oNode->GetObjectClass(); + if ($sClass !== null) { - $sClass = $oNode->GetProperty('class'); - if (($sClass!== null) && (!array_key_exists($sClass, $aNodesPerClass))) + if (!array_key_exists($sClass, $aNodesPerClass)) { $aNodesPerClass[$sClass] = array( 'reached' => array( @@ -267,7 +276,7 @@ class DisplayableNode extends GraphNode if (!array_key_exists($oNode->GetId(), $aNodesPerClass[$sClass][$sKey]['nodes'])) { $aNodesPerClass[$sClass][$sKey]['nodes'][$oNode->GetId()] = $oNode; - $aNodesPerClass[$sClass][$sKey]['count'] += (int)$oNode->GetProperty('count', 1); + $aNodesPerClass[$sClass][$sKey]['count'] += $oNode->GetObjectCount(); } } else @@ -275,14 +284,13 @@ class DisplayableNode extends GraphNode $oNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown); } } - foreach($aNodesPerClass as $sClass => $aDefs) { foreach($aDefs as $sStatus => $aGroupProps) { if (count($aGroupProps['nodes']) >= $iThresholdCount) { - $sNewId = $this->GetId().'::'.(($sStatus == 'reached') ? '_reached': ''); + $sNewId = $this->GetId().'::'.$sClass.'/'.(($sStatus == 'reached') ? '_reached': ''); $oNewNode = $oGraph->GetNode($sNewId); if ($oNewNode == null) { @@ -293,10 +301,6 @@ class DisplayableNode extends GraphNode $oNewNode->SetProperty('is_reached', ($sStatus == 'reached')); $oNewNode->SetProperty('count', $aGroupProps['count']); } - else - { - $oNewNode->SetProperty('count', $oNewNode->GetProperty('count')+$aGroupProps['count']); - } try { @@ -338,7 +342,18 @@ class DisplayableNode extends GraphNode if ($oGraph->GetNode($oNode->GetId())) { $oGraph->_RemoveNode($oNode); - $oNewNode->AddObject($oNode->GetProperty('object')); + 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')); + } } } $oNewNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown); @@ -385,6 +400,30 @@ class DisplayableNode extends GraphNode $sHtml .= ''; return $sHtml; } + + /** + * Get the description of the node in "dot" language + * Used to generate the positions in the graph, but we'd better use fake label + * just to retain the space used by the node, without compromising the parsing + * of the result which may occur when using the real labels (with possible weird characters in the middle) + */ + public function GetDotAttributes($bNoLabel = false) + { + $sDot = ''; + if ($bNoLabel) + { + // simulate a fake label with the approximate same size as the true label + $sLabel = str_repeat('x',strlen($this->GetProperty('label', $this->GetId()))); + $sDot = 'label="'.$sLabel.'"'; + } + else + { + // actual label + $sLabel = addslashes($this->GetProperty('label', $this->GetId())); + $sDot = 'label="'.$sLabel.'"'; + } + return $sDot; + } } class DisplayableRedundancyNode extends DisplayableNode @@ -455,9 +494,9 @@ class DisplayableRedundancyNode extends DisplayableNode { $oNode = $oEdge->GetSourceNode(); - if (($oNode->GetProperty('class') !== null) && (!$oNode->GetProperty('is_reached'))) + if (($oNode->GetObjectClass() !== null) && (!$oNode->GetProperty('is_reached'))) { - $sClass = $oNode->GetProperty('class'); + $sClass = $oNode->GetObjectClass(); if (!array_key_exists($sClass, $aNodesPerClass)) { $aNodesPerClass[$sClass] = array('reached' => array(), 'not_reached' => array()); @@ -524,6 +563,17 @@ class DisplayableRedundancyNode extends DisplayableNode $sHtml .= ''; return $sHtml; } + + + public function GetObjectCount() + { + return 0; + } + + public function GetObjectClass() + { + return null; + } } class DisplayableEdge extends GraphEdge @@ -579,6 +629,11 @@ class DisplayableGroupNode extends DisplayableNode public function AddObject(DBObject $oObj) { + $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; } @@ -653,9 +708,24 @@ class DisplayableGroupNode extends DisplayableNode { $sHtml = ''; $iGroupIdx = $this->GetProperty('group_index'); - $sHtml .= Dict::Format('UI:RelationGroupNumber_N', (1+$iGroupIdx)); + $sHtml .= ''.Dict::Format('UI:RelationGroupNumber_N', (1+$iGroupIdx)).""; + $sHtml .= '
| '.MetaModel::GetName($this->GetObjectClass()).' '; + $sHtml .= Dict::Format('UI_CountOfObjectsShort', $this->GetObjectCount()).' | ';
+ $sHtml .= '