Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.02% covered (success)
96.02%
169 / 176
85.71% covered (warning)
85.71%
24 / 28
CRAP
0.00% covered (danger)
0.00%
0 / 1
common_configuration_ComponentCollection
96.02% covered (success)
96.02%
169 / 176
85.71% covered (warning)
85.71%
24 / 28
78
0.00% covered (danger)
0.00%
0 / 1
 addComponent
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 addDependency
77.78% covered (warning)
77.78%
7 / 9
0.00% covered (danger)
0.00%
0 / 1
5.27
 reset
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 check
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
1 / 1
12
 isAcyclic
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
12
 getTransitions
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getCheckedComponents
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 getUncheckedComponents
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
7
 pushTransitionsOnStack
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 setComponents
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getComponents
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setCheckedComponents
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setDependencies
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDependencies
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setReports
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getReports
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
5
 addReport
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 componentChecked
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 checkComponent
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 getSilentComponents
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setSilentComponents
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 silent
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 noisy
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 isSilent
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 isNoisy
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setRootComponent
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getRootComponent
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2002-2008 (original work) Public Research Centre Henri Tudor & University of Luxembourg
19 *                         (under the project TAO & TAO2);
20 *               2008-2010 (update and modification) Deutsche Institut für Internationale Pädagogische Forschung
21 *                         (under the project TAO-TRANSFER);
22 *               2009-2012 (update and modification) Public Research Centre Henri Tudor
23 *                         (under the project TAO-SUSTAIN & TAO-DEV);
24 *
25 */
26
27/**
28 * Short description of class common_configuration_ComponentCollection
29 *
30 * @access public
31 * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
32 * @package generis
33
34 */
35class common_configuration_ComponentCollection
36{
37    // --- ASSOCIATIONS ---
38
39
40    // --- ATTRIBUTES ---
41
42    /**
43     * The components that have to be checked.
44     *
45     * @access private
46     * @var array
47     */
48    private $components = [];
49
50    /**
51     * An array of arrays. The arrays contained in this field are associative
52     * with the following keys: 'component' is the component on which other
53     * have dependencies. 'depends' is an array containing components that have
54     * dependency on 'component'.
55     *
56     * @access private
57     * @var array
58     */
59    private $dependencies = [];
60
61    /**
62     * Short description of attribute checkedComponents
63     *
64     * @access private
65     * @var array
66     */
67    private $checkedComponents = [];
68
69    /**
70     * Short description of attribute reports
71     *
72     * @access public
73     * @var array
74     */
75    public $reports = [];
76
77    /**
78     * Short description of attribute silentComponents
79     *
80     * @access private
81     * @var array
82     */
83    private $silentComponents = [];
84
85    /**
86     * Short description of attribute rootComponent
87     *
88     * @access private
89     * @var Component
90     */
91    private $rootComponent = null;
92
93    // --- OPERATIONS ---
94
95    /**
96     * Short description of method addComponent
97     *
98     * @access public
99     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
100     * @param  Component component
101     * @return void
102     */
103    public function addComponent(common_configuration_Component $component)
104    {
105
106        $components = $this->getComponents();
107
108        // Search for a similar...
109        foreach ($components as $c) {
110            if ($c === $component) {
111                // Already stored.
112                return;
113            }
114        }
115
116        // Not stored yet.
117        $components[] = $component;
118        $this->setComponents($components);
119    }
120
121    /**
122     * Short description of method addDependency
123     *
124     * @access public
125     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
126     * @param  Component component
127     * @param  Component dependency
128     * @return void
129     */
130    public function addDependency(common_configuration_Component $component, common_configuration_Component $dependency)
131    {
132
133        $dependencies = $this->getDependencies();
134
135        $found = false;
136        foreach ($dependencies as $dep) {
137            if ($dependency === $dep['component'] && $component === $dep['isDependencyOf']) {
138                $found = true;
139                break;
140            }
141        }
142
143        if (false == $found) {
144            $dependencies[] = ['component' => $dependency, 'isDependencyOf' => $component];
145            $this->setDependencies($dependencies);
146        }
147    }
148
149    /**
150     * Short description of method reset
151     *
152     * @access public
153     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
154     * @return void
155     */
156    public function reset()
157    {
158
159        $this->setComponents([]);
160        $this->setDependencies([]);
161        $this->setCheckedComponents([]);
162        $this->setSilentComponents([]);
163    }
164
165    /**
166     * Returns an array of Reports.
167     *
168     * @access public
169     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
170     * @return array
171     */
172    public function check()
173    {
174        $returnValue = [];
175
176
177        // Reset what should be reset for another check on the same instance.
178        $this->setCheckedComponents([]);
179        $this->setReports([]);
180
181        $components = $this->getComponents();
182        $dependencies = $this->getDependencies();
183        $traversed = [];
184
185        // Any node that has no incoming edge and is not
186        // the root mock should be bound to it.
187        foreach ($components as $c) {
188            $found = false;
189            foreach ($this->getDependencies() as $d) {
190                if ($c === $d['isDependencyOf']) {
191                    // Incoming edge(s).
192                    $found = true;
193                    break;
194                }
195            }
196
197            // No incoming edge.
198            if ($found === false && $c !== $this->getRootComponent()) {
199                $this->addDependency($c, $this->getRootComponent());
200            }
201        }
202
203        if (count($components) > 0) {
204            if (true == $this->isAcyclic()) {
205                // We go for a Depth First Search in the graph.
206                $stack = [];
207                $node = $components[0];
208
209                // Do something with my node.
210                $status = $this->checkComponent($node);
211                array_push($traversed, $node); // mark the node as 'traversed'.
212
213                if ($status == common_configuration_Report::VALID) {
214                    // put all transitions from the node to stack.
215                    $stack = self::pushTransitionsOnStack($stack, $this->getTransitions($node));
216
217                    while (count($stack) > 0) {
218                        $transition = array_pop($stack);
219                        $node = $transition['isDependencyOf'];
220
221                        // If not already traversed, do something with my node.
222                        if (false == in_array($node, $traversed)) {
223                            // Do something with my node.
224                            $status = $this->checkComponent($node);
225                            array_push($traversed, $node);
226
227                            if ($status == common_configuration_Report::VALID) {
228                                $stack = self::pushTransitionsOnStack($stack, $this->getTransitions($node));
229                            }
230                        }
231                    }
232                }
233
234                $returnValue = $this->getReports();
235            } else {
236                throw new common_configuration_CyclicDependencyException(
237                    "The dependency graph is cyclic. Please review your dependencies."
238                );
239            }
240        }
241
242
243
244        return (array) $returnValue;
245    }
246
247    /**
248     * Short description of method isAcyclic
249     *
250     * @access private
251     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
252     * @return boolean
253     */
254    private function isAcyclic()
255    {
256        $returnValue = (bool) false;
257
258
259
260        // To detect if the dependency graph is acyclic or not,
261        // we first perform a usual Topological Sorting algorithm.
262        // If at the end of the algorith, we still have edges,
263        // the graph is cyclic !
264
265
266        $l = []; // Empty list where elements are sorted.
267        $q = []; // Set of nodes with no incoming edges.
268
269        $components = $this->getComponents();
270        $dependencies = $this->getDependencies(); // used as a copy !
271
272        // Set q with nodes with no incoming edges.
273        foreach ($components as $c) {
274            $incomingEdges = false;
275
276            foreach ($dependencies as $d) {
277                if ($c === $d['isDependencyOf']) {
278                    // $c has incoming edges thus we reject it.
279                    $incomingEdges = true;
280                    break;
281                }
282            }
283
284            if ($incomingEdges == false) {
285                array_push($q, $c);
286            }
287        }
288
289        while (count($q) > 0) {
290            $n = array_pop($q);
291            array_push($l, $n);
292
293            foreach ($components as $m) {
294                // edge from n to m ?
295                foreach ($dependencies as $k => $dep) {
296                    if ($dep['component'] === $n && $dep['isDependencyOf'] === $m) {
297                        unset($dependencies[$k]);
298
299                        // other incoming edges for m ?
300                        foreach ($dependencies as $dep) {
301                            if ($dep['isDependencyOf'] === $m) {
302                                break 2;
303                            }
304                        }
305
306                        // no incoming edges from m !
307                        array_push($q, $m);
308                    }
309                }
310            }
311        }
312
313        $returnValue = count($dependencies) == 0;
314
315
316        return (bool) $returnValue;
317    }
318
319    /**
320     * Short description of method getTransitions
321     *
322     * @access private
323     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
324     * @param  Component component
325     * @return array
326     */
327    private function getTransitions(common_configuration_Component $component)
328    {
329        $returnValue = [];
330
331
332        $dependencies = $this->dependencies;
333        foreach ($dependencies as $d) {
334            if ($d['component'] === $component) {
335                array_push($returnValue, $d);
336            }
337        }
338
339
340        return (array) $returnValue;
341    }
342
343    /**
344     * Short description of method getCheckedComponents
345     *
346     * @access public
347     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
348     * @return array
349     */
350    public function getCheckedComponents()
351    {
352        $returnValue = [];
353
354
355        // Sort the checked components to make them ordered in the same
356        // way the related components where added.
357        $components = $this->getComponents();
358        $checkedComponents = [];
359        foreach ($components as $c) {
360            foreach ($this->checkedComponents as $cC) {
361                if ($cC === $c) {
362                    array_push($checkedComponents, $cC);
363                }
364            }
365        }
366
367
368        $returnValue = $checkedComponents;
369
370
371        return (array) $returnValue;
372    }
373
374    /**
375     * Short description of method getUncheckedComponents
376     *
377     * @access public
378     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
379     * @return array
380     */
381    public function getUncheckedComponents()
382    {
383        $returnValue = [];
384
385
386        $rootMock = $this->getRootComponent();
387        foreach ($this->getComponents() as $c) {
388            if (false === in_array($c, $this->getCheckedComponents()) && $c !== $rootMock) {
389                array_push($returnValue, $c);
390            }
391        }
392
393        // Sort the checked components to make them ordered in the same
394        // way the related components where added.
395        $components = $this->getComponents();
396        $uncheckedComponents = [];
397        foreach ($components as $c) {
398            foreach ($returnValue as $uC) {
399                if ($uC === $c) {
400                    array_push($uncheckedComponents, $uC);
401                }
402            }
403        }
404
405        $returnValue = $uncheckedComponents;
406
407
408        return (array) $returnValue;
409    }
410
411    /**
412     * Short description of method pushTransitionsOnStack
413     *
414     * @access public
415     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
416     * @param  array stack
417     * @param  array transitions
418     * @return array
419     */
420    public static function pushTransitionsOnStack($stack, $transitions)
421    {
422        $returnValue = [];
423
424
425        foreach ($transitions as $t) {
426            array_push($stack, $t);
427        }
428
429        $returnValue = $stack;
430
431
432        return (array) $returnValue;
433    }
434
435    /**
436     * Short description of method setComponents
437     *
438     * @access public
439     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
440     * @param  array components
441     * @return void
442     */
443    public function setComponents($components)
444    {
445
446        $this->components = $components;
447    }
448
449    /**
450     * Short description of method getComponents
451     *
452     * @access public
453     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
454     * @return array
455     */
456    public function getComponents()
457    {
458        $returnValue = [];
459
460
461        $returnValue = $this->components;
462
463
464        return (array) $returnValue;
465    }
466
467    /**
468     * Short description of method setCheckedComponents
469     *
470     * @access private
471     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
472     * @param  array checkedComponents
473     * @return void
474     */
475    private function setCheckedComponents($checkedComponents)
476    {
477
478        $this->checkedComponents = $checkedComponents;
479    }
480
481    /**
482     * Short description of method setDependencies
483     *
484     * @access private
485     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
486     * @param  array dependencies
487     * @return void
488     */
489    private function setDependencies($dependencies)
490    {
491
492        $this->dependencies = $dependencies;
493    }
494
495    /**
496     * Short description of method getDependencies
497     *
498     * @access private
499     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
500     * @return array
501     */
502    private function getDependencies()
503    {
504        $returnValue = [];
505
506
507        $returnValue = $this->dependencies;
508
509
510        return (array) $returnValue;
511    }
512
513    /**
514     * Short description of method setReports
515     *
516     * @access private
517     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
518     * @param  array reports
519     * @return mixed
520     */
521    private function setReports($reports)
522    {
523
524        $this->reports = $reports;
525    }
526
527    /**
528     * Short description of method getReports
529     *
530     * @access public
531     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
532     * @return array
533     */
534    public function getReports()
535    {
536        $returnValue = [];
537
538
539        if (count($this->reports) == 0) {
540            return $returnValue;
541        } else {
542            // Sort the reports to make them ordered in the same
543            // order the related components where added.
544            $components = $this->getComponents();
545            $reports = [];
546            foreach ($components as $c) {
547                foreach ($this->reports as $r) {
548                    if ($r->getComponent() === $c) {
549                        array_push($reports, $r);
550                    }
551                }
552            }
553        }
554
555        $returnValue = $reports;
556
557
558        return (array) $returnValue;
559    }
560
561    /**
562     * Short description of method addReport
563     *
564     * @access private
565     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
566     * @param  Report report
567     * @return void
568     */
569    private function addReport(common_configuration_Report $report)
570    {
571
572        array_push($this->reports, $report);
573    }
574
575    /**
576     * Short description of method componentChecked
577     *
578     * @access private
579     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
580     * @param  Component component
581     * @return void
582     */
583    private function componentChecked(common_configuration_Component $component)
584    {
585
586        if ($component !== $this->getRootComponent()) {
587            array_push($this->checkedComponents, $component);
588        }
589    }
590
591    /**
592     * Short description of method checkComponent
593     *
594     * @access private
595     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
596     * @param  Component component
597     * @return int
598     */
599    private function checkComponent(common_configuration_Component $component)
600    {
601        $returnValue = (int) 0;
602
603
604        $report = $component->check(); // Check the node.
605        $this->componentChecked($component); // Mark the node as 'checked'.
606
607        // Store the report if not silenced.
608        if (false == $this->isSilent($component)) {
609            $this->addReport($report); // Store the report.
610        }
611
612        $returnValue = $report->getStatus();
613
614
615        return (int) $returnValue;
616    }
617
618    /**
619     * Short description of method getSilentComponents
620     *
621     * @access public
622     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
623     * @return array
624     */
625    public function getSilentComponents()
626    {
627        $returnValue = [];
628
629
630        $returnValue = $this->silentComponents;
631
632
633        return (array) $returnValue;
634    }
635
636    /**
637     * Short description of method setSilentComponents
638     *
639     * @access private
640     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
641     * @param  array silentComponents
642     * @return void
643     */
644    private function setSilentComponents($silentComponents)
645    {
646
647        $this->silentComponents = $silentComponents;
648    }
649
650    /**
651     * Short description of method silent
652     *
653     * @access public
654     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
655     * @param  Component component
656     * @return void
657     */
658    public function silent(common_configuration_Component $component)
659    {
660
661        $silentComponents = $this->getSilentComponents();
662        foreach ($silentComponents as $silent) {
663            if ($silent === $component) {
664                return;
665            }
666        }
667
668        $silentComponents[] = $component;
669        $this->setSilentComponents($silentComponents);
670    }
671
672    /**
673     * Short description of method noisy
674     *
675     * @access public
676     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
677     * @param  Component component
678     * @return void
679     */
680    public function noisy(common_configuration_Component $component)
681    {
682
683        $silentComponents = $this->getSilentComponents();
684
685        foreach ($silentComponents as $k => $silent) {
686            if ($silent === $component) {
687                unset($silentComponents[$k]);
688            }
689        }
690
691        $this->setSilentComponents($silentComponents);
692    }
693
694    /**
695     * Short description of method isSilent
696     *
697     * @access private
698     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
699     * @param  Component component
700     * @return boolean
701     */
702    private function isSilent(common_configuration_Component $component)
703    {
704        $returnValue = (bool) false;
705
706
707        $returnValue = in_array($component, $this->getSilentComponents());
708
709
710        return (bool) $returnValue;
711    }
712
713    /**
714     * Short description of method isNoisy
715     *
716     * @access private
717     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
718     * @param  Component component
719     * @return boolean
720     */
721    private function isNoisy(common_configuration_Component $component)
722    {
723        $returnValue = (bool) false;
724
725
726        $returnValue = !in_array($component, $this->getSilentComponents());
727
728
729        return (bool) $returnValue;
730    }
731
732    /**
733     * Short description of method __construct
734     *
735     * @access public
736     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
737     * @return mixed
738     */
739    public function __construct()
740    {
741
742
743        // A mock root check on which any added component has a dependence. The goal
744        // of this is to make sure that components will not stay alone with no
745        // incoming edges in the dependency graph, making them unreachable.
746        $rootStatus = common_configuration_Report::VALID;
747        $root = new common_configuration_Mock($rootStatus, 'tao.dependencies.root');
748        $this->setRootComponent($root);
749    }
750
751    /**
752     * Short description of method setRootComponent
753     *
754     * @access private
755     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
756     * @param  Component component
757     * @return void
758     */
759    private function setRootComponent(common_configuration_Component $component)
760    {
761
762        $this->rootComponent = $component;
763        $components = $this->getComponents();
764        $components[] = $component;
765        $this->setComponents($components);
766        $this->silent($component);
767    }
768
769    /**
770     * Short description of method getRootComponent
771     *
772     * @access private
773     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
774     * @return common_configuration_Component
775     */
776    private function getRootComponent()
777    {
778        $returnValue = null;
779
780
781        $returnValue = $this->rootComponent;
782
783
784        return $returnValue;
785    }
786}