* @package generis */ class common_configuration_ComponentCollection { // --- ASSOCIATIONS --- // --- ATTRIBUTES --- /** * The components that have to be checked. * * @access private * @var array */ private $components = []; /** * An array of arrays. The arrays contained in this field are associative * with the following keys: 'component' is the component on which other * have dependencies. 'depends' is an array containing components that have * dependency on 'component'. * * @access private * @var array */ private $dependencies = []; /** * Short description of attribute checkedComponents * * @access private * @var array */ private $checkedComponents = []; /** * Short description of attribute reports * * @access public * @var array */ public $reports = []; /** * Short description of attribute silentComponents * * @access private * @var array */ private $silentComponents = []; /** * Short description of attribute rootComponent * * @access private * @var Component */ private $rootComponent = null; // --- OPERATIONS --- /** * Short description of method addComponent * * @access public * @author Jerome Bogaerts, * @param Component component * @return void */ public function addComponent(common_configuration_Component $component) { $components = $this->getComponents(); // Search for a similar... foreach ($components as $c) { if ($c === $component) { // Already stored. return; } } // Not stored yet. $components[] = $component; $this->setComponents($components); } /** * Short description of method addDependency * * @access public * @author Jerome Bogaerts, * @param Component component * @param Component dependency * @return void */ public function addDependency(common_configuration_Component $component, common_configuration_Component $dependency) { $dependencies = $this->getDependencies(); $found = false; foreach ($dependencies as $dep) { if ($dependency === $dep['component'] && $component === $dep['isDependencyOf']) { $found = true; break; } } if (false == $found) { $dependencies[] = ['component' => $dependency, 'isDependencyOf' => $component]; $this->setDependencies($dependencies); } } /** * Short description of method reset * * @access public * @author Jerome Bogaerts, * @return void */ public function reset() { $this->setComponents([]); $this->setDependencies([]); $this->setCheckedComponents([]); $this->setSilentComponents([]); } /** * Returns an array of Reports. * * @access public * @author Jerome Bogaerts, * @return array */ public function check() { $returnValue = []; // Reset what should be reset for another check on the same instance. $this->setCheckedComponents([]); $this->setReports([]); $components = $this->getComponents(); $dependencies = $this->getDependencies(); $traversed = []; // Any node that has no incoming edge and is not // the root mock should be bound to it. foreach ($components as $c) { $found = false; foreach ($this->getDependencies() as $d) { if ($c === $d['isDependencyOf']) { // Incoming edge(s). $found = true; break; } } // No incoming edge. if ($found === false && $c !== $this->getRootComponent()) { $this->addDependency($c, $this->getRootComponent()); } } if (count($components) > 0) { if (true == $this->isAcyclic()) { // We go for a Depth First Search in the graph. $stack = []; $node = $components[0]; // Do something with my node. $status = $this->checkComponent($node); array_push($traversed, $node); // mark the node as 'traversed'. if ($status == common_configuration_Report::VALID) { $stack = self::pushTransitionsOnStack($stack, $this->getTransitions($node)); // put all transitions from the node to stack. while (count($stack) > 0) { $transition = array_pop($stack); $node = $transition['isDependencyOf']; // If not already traversed, do something with my node. if (false == in_array($node, $traversed)) { // Do something with my node. $status = $this->checkComponent($node); array_push($traversed, $node); if ($status == common_configuration_Report::VALID) { $stack = self::pushTransitionsOnStack($stack, $this->getTransitions($node)); } } } } $returnValue = $this->getReports(); } else { throw new common_configuration_CyclicDependencyException("The dependency graph is cyclic. Please review your dependencies."); } } return (array) $returnValue; } /** * Short description of method isAcyclic * * @access private * @author Jerome Bogaerts, * @return boolean */ private function isAcyclic() { $returnValue = (bool) false; // To detect if the dependency graph is acyclic or not, // we first perform a usual Topological Sorting algorithm. // If at the end of the algorith, we still have edges, // the graph is cyclic ! $l = []; // Empty list where elements are sorted. $q = []; // Set of nodes with no incoming edges. $components = $this->getComponents(); $dependencies = $this->getDependencies(); // used as a copy ! // Set q with nodes with no incoming edges. foreach ($components as $c) { $incomingEdges = false; foreach ($dependencies as $d) { if ($c === $d['isDependencyOf']) { // $c has incoming edges thus we reject it. $incomingEdges = true; break; } } if ($incomingEdges == false) { array_push($q, $c); } } while (count($q) > 0) { $n = array_pop($q); array_push($l, $n); foreach ($components as $m) { // edge from n to m ? foreach ($dependencies as $k => $dep) { if ($dep['component'] === $n && $dep['isDependencyOf'] === $m) { unset($dependencies[$k]); // other incoming edges for m ? foreach ($dependencies as $dep) { if ($dep['isDependencyOf'] === $m) { break 2; } } // no incoming edges from m ! array_push($q, $m); } } } } $returnValue = count($dependencies) == 0; return (bool) $returnValue; } /** * Short description of method getTransitions * * @access private * @author Jerome Bogaerts, * @param Component component * @return array */ private function getTransitions(common_configuration_Component $component) { $returnValue = []; $dependencies = $this->dependencies; foreach ($dependencies as $d) { if ($d['component'] === $component) { array_push($returnValue, $d); } } return (array) $returnValue; } /** * Short description of method getCheckedComponents * * @access public * @author Jerome Bogaerts, * @return array */ public function getCheckedComponents() { $returnValue = []; // Sort the checked components to make them ordered in the same // way the related components where added. $components = $this->getComponents(); $checkedComponents = []; foreach ($components as $c) { foreach ($this->checkedComponents as $cC) { if ($cC === $c) { array_push($checkedComponents, $cC); } } } $returnValue = $checkedComponents; return (array) $returnValue; } /** * Short description of method getUncheckedComponents * * @access public * @author Jerome Bogaerts, * @return array */ public function getUncheckedComponents() { $returnValue = []; $rootMock = $this->getRootComponent(); foreach ($this->getComponents() as $c) { if (false === in_array($c, $this->getCheckedComponents()) && $c !== $rootMock) { array_push($returnValue, $c); } } // Sort the checked components to make them ordered in the same // way the related components where added. $components = $this->getComponents(); $uncheckedComponents = []; foreach ($components as $c) { foreach ($returnValue as $uC) { if ($uC === $c) { array_push($uncheckedComponents, $uC); } } } $returnValue = $uncheckedComponents; return (array) $returnValue; } /** * Short description of method pushTransitionsOnStack * * @access public * @author Jerome Bogaerts, * @param array stack * @param array transitions * @return array */ public static function pushTransitionsOnStack($stack, $transitions) { $returnValue = []; foreach ($transitions as $t) { array_push($stack, $t); } $returnValue = $stack; return (array) $returnValue; } /** * Short description of method setComponents * * @access public * @author Jerome Bogaerts, * @param array components * @return void */ public function setComponents($components) { $this->components = $components; } /** * Short description of method getComponents * * @access public * @author Jerome Bogaerts, * @return array */ public function getComponents() { $returnValue = []; $returnValue = $this->components; return (array) $returnValue; } /** * Short description of method setCheckedComponents * * @access private * @author Jerome Bogaerts, * @param array checkedComponents * @return void */ private function setCheckedComponents($checkedComponents) { $this->checkedComponents = $checkedComponents; } /** * Short description of method setDependencies * * @access private * @author Jerome Bogaerts, * @param array dependencies * @return void */ private function setDependencies($dependencies) { $this->dependencies = $dependencies; } /** * Short description of method getDependencies * * @access private * @author Jerome Bogaerts, * @return array */ private function getDependencies() { $returnValue = []; $returnValue = $this->dependencies; return (array) $returnValue; } /** * Short description of method setReports * * @access private * @author Jerome Bogaerts, * @param array reports * @return mixed */ private function setReports($reports) { $this->reports = $reports; } /** * Short description of method getReports * * @access public * @author Jerome Bogaerts, * @return array */ public function getReports() { $returnValue = []; if (count($this->reports) == 0) { return $returnValue; } else { // Sort the reports to make them ordered in the same // order the related components where added. $components = $this->getComponents(); $reports = []; foreach ($components as $c) { foreach ($this->reports as $r) { if ($r->getComponent() === $c) { array_push($reports, $r); } } } } $returnValue = $reports; return (array) $returnValue; } /** * Short description of method addReport * * @access private * @author Jerome Bogaerts, * @param Report report * @return void */ private function addReport(common_configuration_Report $report) { array_push($this->reports, $report); } /** * Short description of method componentChecked * * @access private * @author Jerome Bogaerts, * @param Component component * @return void */ private function componentChecked(common_configuration_Component $component) { if ($component !== $this->getRootComponent()) { array_push($this->checkedComponents, $component); } } /** * Short description of method checkComponent * * @access private * @author Jerome Bogaerts, * @param Component component * @return int */ private function checkComponent(common_configuration_Component $component) { $returnValue = (int) 0; $report = $component->check(); // Check the node. $this->componentChecked($component); // Mark the node as 'checked'. // Store the report if not silenced. if (false == $this->isSilent($component)) { $this->addReport($report); // Store the report. } $returnValue = $report->getStatus(); return (int) $returnValue; } /** * Short description of method getSilentComponents * * @access public * @author Jerome Bogaerts, * @return array */ public function getSilentComponents() { $returnValue = []; $returnValue = $this->silentComponents; return (array) $returnValue; } /** * Short description of method setSilentComponents * * @access private * @author Jerome Bogaerts, * @param array silentComponents * @return void */ private function setSilentComponents($silentComponents) { $this->silentComponents = $silentComponents; } /** * Short description of method silent * * @access public * @author Jerome Bogaerts, * @param Component component * @return void */ public function silent(common_configuration_Component $component) { $silentComponents = $this->getSilentComponents(); foreach ($silentComponents as $silent) { if ($silent === $component) { return; } } $silentComponents[] = $component; $this->setSilentComponents($silentComponents); } /** * Short description of method noisy * * @access public * @author Jerome Bogaerts, * @param Component component * @return void */ public function noisy(common_configuration_Component $component) { $silentComponents = $this->getSilentComponents(); foreach ($silentComponents as $k => $silent) { if ($silent === $component) { unset($silentComponents[$k]); } } $this->setSilentComponents($silentComponents); } /** * Short description of method isSilent * * @access private * @author Jerome Bogaerts, * @param Component component * @return boolean */ private function isSilent(common_configuration_Component $component) { $returnValue = (bool) false; $returnValue = in_array($component, $this->getSilentComponents()); return (bool) $returnValue; } /** * Short description of method isNoisy * * @access private * @author Jerome Bogaerts, * @param Component component * @return boolean */ private function isNoisy(common_configuration_Component $component) { $returnValue = (bool) false; $returnValue = !in_array($component, $this->getSilentComponents()); return (bool) $returnValue; } /** * Short description of method __construct * * @access public * @author Jerome Bogaerts, * @return mixed */ public function __construct() { // A mock root check on which any added component has a dependence. The goal // of this is to make sure that components will not stay alone with no // incoming edges in the dependency graph, making them unreachable. $rootStatus = common_configuration_Report::VALID; $root = new common_configuration_Mock($rootStatus, 'tao.dependencies.root'); $this->setRootComponent($root); } /** * Short description of method setRootComponent * * @access private * @author Jerome Bogaerts, * @param Component component * @return void */ private function setRootComponent(common_configuration_Component $component) { $this->rootComponent = $component; $components = $this->getComponents(); $components[] = $component; $this->setComponents($components); $this->silent($component); } /** * Short description of method getRootComponent * * @access private * @author Jerome Bogaerts, * @return common_configuration_Component */ private function getRootComponent() { $returnValue = null; $returnValue = $this->rootComponent; return $returnValue; } }