SetOperatorNodeSimplifier::getUnionOperands()   A
last analyzed

Complexity

Conditions 4
Paths 5

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4.074

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 17
ccs 10
cts 12
cp 0.8333
rs 9.2
cc 4
eloc 10
nc 5
nop 1
crap 4.074
1
<?php
2
3
namespace PPP\Module\TreeSimplifier;
4
5
use InvalidArgumentException;
6
use PPP\DataModel\AbstractNode;
7
use PPP\DataModel\IntersectionNode;
8
use PPP\DataModel\ResourceListNode;
9
use PPP\DataModel\UnionNode;
10
11
/**
12
 * @licence MIT
13
 * @author Thomas Pellissier Tanon
14
 */
15
class SetOperatorNodeSimplifier implements NodeSimplifier {
16
17
	/**
18
	 * @see NodeSimplifier::isSimplifierFor
19
	 */
20 3
	public function isSimplifierFor(AbstractNode $node) {
21 3
		return $node instanceof IntersectionNode || $node instanceof UnionNode;
22
	}
23
24
	/**
25
	 * @see NodeSimplifier::simplify
26
	 */
27 13
	public function simplify(AbstractNode $node) {
28 13
		if($node instanceof UnionNode) {
29 5
			return $this->simplifyUnionNode($node);
30 8
		} elseif($node instanceof IntersectionNode) {
31 7
			return $this->simplifyIntersectionNode($node);
32
		} else {
33 1
			throw new InvalidArgumentException('SetOperatorNodeSimplifier can only simplify UnionNode and IntersectionNode');
34
		}
35
	}
36
37 8
	private function simplifyUnionNode(UnionNode $node) {
38 8
		$simplifiedOperands = $this->simplifyUnionOperands($this->getUnionOperands($node));
39
40 8
		switch(count($simplifiedOperands)) {
41 8
			case 0:
42 1
				return new ResourceListNode();
43 7
			case 1:
44 4
				return reset($simplifiedOperands);
45 4
			default:
46 4
				return new UnionNode($simplifiedOperands);
47 4
		}
48
	}
49
50 8
	private function getUnionOperands(UnionNode $node) {
51 8
		$operands = array();
52
53 8
		foreach($node->getOperands() as $operand) {
54 7
			if($operand instanceof IntersectionNode) {
55
				$operand = $this->simplifyIntersectionNode($operand);
56
			}
57
58 7
			if($operand instanceof UnionNode) {
59 1
				$operands = array_merge($operands, $this->getUnionOperands($operand));
60 1
			} else {
61 7
				$operands[] = $operand;
62
			}
63 8
		}
64
65 8
		return $operands;
66
	}
67
68 8
	private function simplifyUnionOperands(array $operands) {
69 8
		list($resourceLists, $otherOperands) = $this->filterOperandsByType($operands, 'list');
70
71 8
		if(!empty($resourceLists)) {
72 5
			$otherOperands[] = new ResourceListNode($resourceLists);
73 5
		}
74
75 8
		return $otherOperands;
76
	}
77
78 12
	private function filterOperandsByType(array $operands, $type) {
79 12
		$filteredOperands = array();
80 12
		$otherOperands = array();
81
82
		/** @var AbstractNode $operand */
83 12
		foreach($operands as $operand) {
84 10
			if($operand->getType() === $type) {
85 9
				$filteredOperands[] = $operand;
86 9
			} else {
87 5
				$otherOperands[] = $operand;
88
			}
89 12
		}
90
91 12
		return array($filteredOperands, $otherOperands);
92
	}
93
94 7
	private function simplifyIntersectionNode(IntersectionNode $node) {
95 7
		$node = $this->simplifyLazilyIntersectionNode($node);
96
97 7
		if($node instanceof IntersectionNode) {
98 4
			return $this->moveUnionUnderIntersection($node);
99
		} else {
100 3
			return $node;
101
		}
102
	}
103
104 7
	private function simplifyLazilyIntersectionNode(IntersectionNode $node) {
105 7
		$simplifiedOperands = $this->simplifyIntersectionOperands($this->getIntersectionOperands($node));
106
107 7
		if(count($simplifiedOperands) === 1) {
108 3
			return reset($simplifiedOperands);
109
		}
110
111 4
		return new IntersectionNode($simplifiedOperands);
112
	}
113
114 7
	private function getIntersectionOperands(IntersectionNode $node) {
115 7
		$operands = array();
116
117 7
		foreach($node->getOperands() as $operand) {
118 6
			if($operand instanceof UnionNode) {
119 3
				$operand = $this->simplifyUnionNode($operand);
120 3
			}
121
122 6
			if($operand instanceof IntersectionNode) {
123 1
				$operands = array_merge($operands, $this->getIntersectionOperands($operand));
124 1
			} else {
125 6
				$operands[] = $operand;
126
			}
127 7
		}
128
129 7
		return $operands;
130
	}
131
132 7
	private function simplifyIntersectionOperands(array $operands) {
133 7
		list($resourceLists, $otherOperands) = $this->filterOperandsByType($operands, 'list');
134
135 7
		if(!empty($resourceLists)) {
136 6
			$otherOperands[] = $this->doIntersection($resourceLists);
137 6
		}
138
139 7
		return $otherOperands;
140
	}
141
142 6
	private function doIntersection(array $lists) {
143 6
		$result = reset($lists)->toArray();
144 6
		foreach($lists as $list) {
145 6
			$result = $this->intersect($result, $list);
146 6
		}
147
148 6
		return new ResourceListNode($result);
149
	}
150
151 6
	private function intersect(array $list1, ResourceListNode $list2) {
152 6
		$resources = array();
153
154 6
		foreach($list1 as $resource) {
155 6
			if($list2->hasResource($resource)) {
156 6
				$resources[] = $resource;
157 6
			}
158 6
		}
159
160 6
		return $resources;
161
	}
162
163 4
	private function moveUnionUnderIntersection(IntersectionNode $intersectionNode) {
164 4
		list($unions, $otherOperands) = $this->filterOperandsByType($intersectionNode->getOperands(), 'union');
165
166 4
		$intersectionsOperands = array($otherOperands);
167
168
		/** @var UnionNode $union */
169 4
		foreach($unions as $union) {
170 2
			$newIntersectionsOperands = array();
171 2
			foreach($intersectionsOperands as $intersectionOperands) {
172 2
				foreach($union->getOperands() as $operand) {
173 2
					$newIntersectionsOperands[] = array_merge($intersectionOperands, array($operand));
174 2
				}
175 2
			}
176 2
			$intersectionsOperands = $newIntersectionsOperands;
177 4
		}
178
179 4
		$intersections = array();
180 4
		foreach($intersectionsOperands as $intersectionOperands) {
181 4
			$intersections[] = $this->simplifyLazilyIntersectionNode(new IntersectionNode($intersectionOperands));
182 4
		}
183
184 4
		if(count($intersections) === 1) {
185 2
			return reset($intersections);
186
		}
187
188 2
		return new UnionNode($intersections);
189
	}
190
}
191