Add support for nested requirement sets

This allows now to link requirements by an OR condition as well and to nest
such grouped requirements in other sets of type AND, and vice versa.

refs #8508
This commit is contained in:
Johannes Meyer 2015-02-26 10:49:03 +01:00
parent 04630a20be
commit 8ed4a943f7
1 changed files with 126 additions and 9 deletions

View File

@ -4,6 +4,7 @@
namespace Icinga\Module\Setup;
use ArrayIterator;
use LogicException;
use IteratorAggregate;
/**
@ -11,12 +12,77 @@ use IteratorAggregate;
*/
class Requirements implements IteratorAggregate
{
/**
* Mode AND (all requirements must met)
*/
const MODE_AND = 0;
/**
* Mode OR (at least one requirement must met)
*/
const MODE_OR = 1;
/**
* The mode by with the requirements are evaluated
*
* @var string
*/
protected $mode;
/**
* The registered requirements
*
* @var array
*/
protected $requirements = array();
protected $requirements;
/**
* Whether there is any mandatory requirement part of this set
*
* @var bool
*/
protected $containsMandatoryRequirements;
/**
* Create a new set of requirements
*
* @param int $mode The mode by with to evaluate the requirements
*/
public function __construct($mode = null)
{
$this->requirements = array();
$this->containsMandatoryRequirements = false;
$this->setMode($mode ?: static::MODE_AND);
}
/**
* Set the mode by with to evaluate the requirements
*
* @param int $mode
*
* @return Requirements
*
* @throws LogicException In case the given mode is invalid
*/
public function setMode($mode)
{
if ($mode !== static::MODE_AND && $mode !== static::MODE_OR) {
throw new LogicException(sprintf('Invalid mode %u given.'), $mode);
}
$this->mode = $mode;
return $this;
}
/**
* Return the mode by with the requirements are evaluated
*
* @return int
*/
public function getMode()
{
return $this->mode;
}
/**
* Register a requirement
@ -29,8 +95,8 @@ class Requirements implements IteratorAggregate
{
$merged = false;
foreach ($this as $knownRequirement) {
if ($requirement->equals($knownRequirement)) {
if ($knownRequirement->isOptional() && !$requirement->isOptional()) {
if ($knownRequirement instanceof Requirement && $requirement->equals($knownRequirement)) {
if ($this->getMode() === static::MODE_AND && !$requirement->isOptional()) {
$knownRequirement->setOptional(false);
}
@ -44,6 +110,12 @@ class Requirements implements IteratorAggregate
}
if (! $merged) {
if ($this->getMode() === static::MODE_OR) {
$requirement->setOptional();
} elseif (! $requirement->isOptional()) {
$this->containsMandatoryRequirements = true;
}
$this->requirements[] = $requirement;
}
@ -60,6 +132,16 @@ class Requirements implements IteratorAggregate
return $this->requirements;
}
/**
* Return whether there is any mandatory requirement part of this set
*
* @return bool
*/
public function hasAnyMandatoryRequirement()
{
return $this->containsMandatoryRequirements || $this->getMode() === static::MODE_OR;
}
/**
* Return an iterator of all registered requirements
*
@ -79,26 +161,61 @@ class Requirements implements IteratorAggregate
*/
public function merge(Requirements $requirements)
{
foreach ($requirements as $requirement) {
$this->add($requirement);
if ($this->getMode() === static::MODE_OR && $requirements->getMode() === static::MODE_OR) {
foreach ($requirements as $requirement) {
if ($requirement instanceof static) {
$this->merge($requirement);
} else {
$this->add($requirement);
}
}
} else {
if ($requirements->getMode() === static::MODE_OR) {
$this->containsMandatoryRequirements = true;
}
$this->requirements[] = $requirements;
}
return $this;
}
/**
* Return whether all mandatory requirements are fulfilled
* Return whether all requirements can successfully be evaluated based on the current mode
*
* @return bool
*/
public function fulfilled()
{
$state = false;
foreach ($this as $requirement) {
if (! $requirement->getState() && !$requirement->isOptional()) {
return false;
if ($requirement instanceof static) {
if ($requirement->fulfilled()) {
if ($this->getMode() === static::MODE_OR) {
return true;
}
$state = true;
} elseif ($this->getMode() === static::MODE_AND && $requirement->hasAnyMandatoryRequirement()) {
return false;
}
} else {
if ($requirement->getState()) {
if ($this->getMode() === static::MODE_OR) {
return true;
}
$state = true;
} elseif ($this->getMode() === static::MODE_AND) {
if (! $requirement->isOptional()) {
return false;
}
$state = true; // There may only be optional requirements...
}
}
}
return true;
return $state;
}
}