Completed
Push — master ( 146f46...ce721c )
by Thomas
03:30
created

GeneratorNavigator   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 212
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 90.4%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 55
c 1
b 0
f 0
lcom 1
cbo 2
dl 0
loc 212
ccs 113
cts 125
cp 0.904
rs 6.8

10 Methods

Rating   Name   Duplication   Size   Complexity  
A setConstantSortFunc() 0 3 1
A setPropertySortFunc() 0 3 1
A setMethodSortFunc() 0 3 1
A setUseStatementSortFunc() 0 3 1
A accept() 0 7 3
D visitStruct() 0 61 15
A getConstantSortFunc() 0 3 2
C getUseSortFunc() 0 39 11
C getMethodSortFunc() 0 24 11
B getPropertySortFunc() 0 20 9

How to fix   Complexity   

Complex Class

Complex classes like GeneratorNavigator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GeneratorNavigator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * Copyright 2011 Johannes M. Schmitt <[email protected]>
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
namespace gossi\codegen\visitor;
19
20
use gossi\codegen\model\GenerateableInterface;
21
use gossi\codegen\model\AbstractPhpStruct;
22
use gossi\codegen\model\PhpFunction;
23
use gossi\codegen\model\PhpInterface;
24
use gossi\codegen\model\PhpTrait;
25
use gossi\codegen\model\PhpClass;
26
use gossi\codegen\model\ConstantsInterface;
27
use gossi\codegen\model\TraitsInterface;
28
29
/**
30
 * The default navigator.
31
 *
32
 * This class is responsible for the default traversal algorithm of the different
33
 * code elements.
34
 *
35
 * Unlike other visitor pattern implementations, this allows to separate the
36
 * traversal logic from the objects that are traversed.
37
 *
38
 * @author Johannes M. Schmitt <[email protected]>
39
 */
40
class GeneratorNavigator {
41
42
	private $constantSortFunc;
43
44
	private $propertySortFunc;
45
46
	private $methodSortFunc;
47
48
	private $useStatementSortFunc;
49
50
	private static $defaultMethodSortFunc;
51
52
	private static $defaultPropertySortFunc;
53
54
	private static $defaultUseStatementSortFunc;
55
56
	/**
57
	 * Sets a custom constant sorting function.
58
	 *
59
	 * @param null|\Closure $func        	
60
	 */
61 2
	public function setConstantSortFunc(\Closure $func = null) {
62 2
		$this->constantSortFunc = $func;
63 2
	}
64
65
	/**
66
	 * Sets a custom property sorting function.
67
	 *
68
	 * @param null|\Closure $func        	
69
	 */
70 2
	public function setPropertySortFunc(\Closure $func = null) {
71 2
		$this->propertySortFunc = $func;
72 2
	}
73
74
	/**
75
	 * Sets a custom method sorting function.
76
	 *
77
	 * @param null|\Closure $func        	
78
	 */
79 2
	public function setMethodSortFunc(\Closure $func = null) {
80 2
		$this->methodSortFunc = $func;
81 2
	}
82
83
	/**
84
	 * Sets a custom method sorting function.
85
	 *
86
	 * @param null|\Closure $func
87
	 */
88
	public function setUseStatementSortFunc(\Closure $func = null) {
89
		$this->useStatementSortFunc = $func;
90
	}
91
92 18
	public function accept(GeneratorVisitorInterface $visitor, GenerateableInterface $model) {
93 18
		if ($model instanceof AbstractPhpStruct) {
94 13
			$this->visitStruct($visitor, $model);
95 18
		} else if ($model instanceof PhpFunction) {
96 5
			$visitor->visitFunction($model);
97 5
		}
98 18
	}
99
100 13
	private function visitStruct(GeneratorVisitorInterface $visitor, AbstractPhpStruct $struct) {
101
		// start struct - sort use statements
102 13
		$useStatements = $struct->getUseStatements();
103 13
		uasort($useStatements, $this->getUseSortFunc());
104 13
		$struct->setUseStatements($useStatements);
105
106 13
		if ($struct instanceof PhpInterface) {
107 1
			$visitor->startVisitingInterface($struct);
108 13
		} else if ($struct instanceof PhpTrait) {
109 1
			$visitor->startVisitingTrait($struct);
110 12
		} else if ($struct instanceof PhpClass) {
111 11
			$visitor->startVisitingClass($struct);
112 11
		}
113
114
		// contents
115 13
		if ($struct instanceof ConstantsInterface) {
116 12
			$constants = $struct->getConstants(true);
117 12
			if (!empty($constants)) {
118 6
				uksort($constants, $this->getConstantSortFunc());
119
120 6
				$visitor->startVisitingStructConstants();
121 6
				foreach ($constants as $constant) {
122 6
					$visitor->visitStructConstant($constant);
123 6
				}
124 6
				$visitor->endVisitingStructConstants();
125 6
			}
126 12
		}
127
128 13
		if ($struct instanceof TraitsInterface) {
129 12
			$properties = $struct->getProperties();
130 12
			if (!empty($properties)) {
131 6
				usort($properties, $this->getPropertySortFunc());
132
133 6
				$visitor->startVisitingProperties();
134 6
				foreach ($properties as $property) {
135 6
					$visitor->visitProperty($property);
136 6
				}
137 6
				$visitor->endVisitingProperties();
138 6
			}
139 12
		}
140
141 13
		$methods = $struct->getMethods();
142 13
		if (!empty($methods)) {
143 8
			usort($methods, $this->getMethodSortFunc());
144
145 8
			$visitor->startVisitingMethods();
146 8
			foreach ($methods as $method) {
147 8
				$visitor->visitMethod($method);
148 8
			}
149 8
			$visitor->endVisitingMethods();
150 8
		}
151
152
		// end struct
153 13
		if ($struct instanceof PhpInterface) {
154 1
			$visitor->endVisitingInterface($struct);
155 13
		} else if ($struct instanceof PhpTrait) {
156 1
			$visitor->endVisitingTrait($struct);
157 12
		} else if ($struct instanceof PhpClass) {
158 11
			$visitor->endVisitingClass($struct);
159 11
		}
160 13
	}
161
162 6
	private function getConstantSortFunc() {
163 6
		return $this->constantSortFunc ?  : 'strcasecmp';
164
	}
165
166 13
	private function getUseSortFunc() {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
167 13
		if (null !== $this->useStatementSortFunc) {
168
			return $this->useStatemenentSortFunc;
0 ignored issues
show
Bug introduced by
The property useStatemenentSortFunc does not seem to exist. Did you mean useStatementSortFunc?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
169
		}
170
171 13
		if (empty(self::$defaultUseStatementSortFunc)) {
172
			self::$defaultUseStatementSortFunc = function ($s1, $s2) {
173
				// find first difference
174 1
				$cmp1 = null;
175 1
				$cmp2 = null;
176 1
				$min = min(strlen($s1), strlen($s2));
177 1
				for ($i = 0; $i < $min; $i++) {
178 1
					if ($s1[$i] != $s2[$i]) {
179 1
						$cmp1 = $s1[$i];
180 1
						$cmp2 = $s2[$i];
181 1
						break;
182
					}
183 1
				}
184
185 1
				if ($cmp1 === null && $cmp2 === null) {
186
					return 0;
187
				}
188
189
				$getAscii = function ($str) {
190 1
					$ord = ord($str);
191 1
					if ($ord >= 65 && $ord <= 90) {
192 1
						$ord += 32;
193 1
					} else if ($ord >= 97 && $ord <= 122) {
194 1
						$ord -= 32;
195 1
					}
196 1
					return $ord;
197 1
				};
198
199 1
				return $getAscii($cmp1) - $getAscii($cmp2);
200 1
			};
201 1
		}
202
203 13
		return self::$defaultUseStatementSortFunc;
204
	}
205
206 8
	private function getMethodSortFunc() {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
207 8
		if (null !== $this->methodSortFunc) {
208 2
			return $this->methodSortFunc;
209
		}
210
211 6
		if (empty(self::$defaultMethodSortFunc)) {
212
			self::$defaultMethodSortFunc = function ($a, $b) {
213 2
				if ($a->isStatic() !== $isStatic = $b->isStatic()) {
214
					return $isStatic ? 1 : -1;
215
				}
216
217 2
				if (($aV = $a->getVisibility()) !== $bV = $b->getVisibility()) {
218
					$aV = 'public' === $aV ? 3 : ('protected' === $aV ? 2 : 1);
219
					$bV = 'public' === $bV ? 3 : ('protected' === $bV ? 2 : 1);
220
221
					return $aV > $bV ? -1 : 1;
222
				}
223
224 2
				return strcasecmp($a->getName(), $b->getName());
225 1
			};
226 1
		}
227
228 6
		return self::$defaultMethodSortFunc;
229
	}
230
231 6
	private function getPropertySortFunc() {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
232 6
		if (null !== $this->propertySortFunc) {
233 2
			return $this->propertySortFunc;
234
		}
235
236 4
		if (empty(self::$defaultPropertySortFunc)) {
237 2
			self::$defaultPropertySortFunc = function ($a, $b) {
238 2
				if (($aV = $a->getVisibility()) !== $bV = $b->getVisibility()) {
239
					$aV = 'public' === $aV ? 3 : ('protected' === $aV ? 2 : 1);
240
					$bV = 'public' === $bV ? 3 : ('protected' === $bV ? 2 : 1);
241
242
					return $aV > $bV ? -1 : 1;
243
				}
244
245 2
				return strcasecmp($a->getName(), $b->getName());
246 1
			};
247 1
		}
248
249 4
		return self::$defaultPropertySortFunc;
250
	}
251
}
252