Total Complexity | 44 |
Total Lines | 381 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like PathConvert 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.
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 PathConvert, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
42 | class PathConvert extends Task |
||
43 | { |
||
44 | /** |
||
45 | * Set if we're running on windows. |
||
46 | */ |
||
47 | public $onWindows = false; |
||
48 | |||
49 | public $from; |
||
50 | public $to; |
||
51 | // Members |
||
52 | /** |
||
53 | * Path to be converted. |
||
54 | */ |
||
55 | private $path; |
||
56 | /** |
||
57 | * Reference to path/fileset to convert. |
||
58 | * |
||
59 | * @var Reference |
||
60 | */ |
||
61 | private $refid; |
||
62 | /** |
||
63 | * The target OS type. |
||
64 | */ |
||
65 | private $targetOS; |
||
66 | /** |
||
67 | * Set when targetOS is set to windows. |
||
68 | */ |
||
69 | private $targetWindows = false; |
||
70 | /** |
||
71 | * Set if we should create a new property even if the result is empty. |
||
72 | */ |
||
73 | private $setonempty = true; |
||
74 | /** |
||
75 | * The property to receive the conversion. |
||
76 | */ |
||
77 | private $property; |
||
78 | /** |
||
79 | * Path prefix map. |
||
80 | * |
||
81 | * @var MapEntry[] |
||
82 | */ |
||
83 | private $prefixMap = []; |
||
84 | /** |
||
85 | * User override on path sep char. |
||
86 | */ |
||
87 | private $pathSep; |
||
88 | /** |
||
89 | * User override on directory sep char. |
||
90 | */ |
||
91 | private $dirSep; |
||
92 | private $mapper; |
||
93 | private $preserveDuplicates = false; |
||
94 | |||
95 | /** |
||
96 | * constructor. |
||
97 | */ |
||
98 | public function __construct() |
||
99 | { |
||
100 | parent::__construct(); |
||
101 | $this->onWindows = 0 === strncasecmp(PHP_OS, 'WIN', 3); |
||
102 | } |
||
103 | |||
104 | /** |
||
105 | * Create a nested PATH element. |
||
106 | */ |
||
107 | public function createPath() |
||
108 | { |
||
109 | if ($this->isReference()) { |
||
110 | throw $this->noChildrenAllowed(); |
||
111 | } |
||
112 | |||
113 | if (null === $this->path) { |
||
114 | $this->path = new Path($this->getProject()); |
||
115 | } |
||
116 | |||
117 | return $this->path->createPath(); |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * Create a nested MAP element. |
||
122 | * |
||
123 | * @return MapEntry a Map to configure |
||
124 | */ |
||
125 | public function createMap() |
||
126 | { |
||
127 | $entry = new MapEntry($this); |
||
128 | |||
129 | $this->prefixMap[] = $entry; |
||
130 | |||
131 | return $entry; |
||
132 | } |
||
133 | |||
134 | /** |
||
135 | * Set targetos to a platform to one of |
||
136 | * "windows", "unix", "netware", or "os/2"; required unless |
||
137 | * unless pathsep and/or dirsep are specified. |
||
138 | * |
||
139 | * @param mixed $target |
||
140 | */ |
||
141 | public function setTargetos($target) |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Set setonempty. |
||
149 | * |
||
150 | * If false, don't set the new property if the result is the empty string. |
||
151 | * |
||
152 | * @param bool $setonempty true or false |
||
153 | */ |
||
154 | public function setSetonempty($setonempty) |
||
155 | { |
||
156 | $this->setonempty = $setonempty; |
||
157 | } |
||
158 | |||
159 | /** |
||
160 | * The property into which the converted path will be placed. |
||
161 | * |
||
162 | * @param mixed $p |
||
163 | */ |
||
164 | public function setProperty($p) |
||
165 | { |
||
166 | $this->property = $p; |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * Adds a reference to a Path, FileSet, DirSet, or FileList defined |
||
171 | * elsewhere. |
||
172 | * |
||
173 | * @throws BuildException |
||
174 | */ |
||
175 | public function setRefid(Reference $r) |
||
176 | { |
||
177 | if (null !== $this->path) { |
||
178 | throw $this->noChildrenAllowed(); |
||
179 | } |
||
180 | |||
181 | $this->refid = $r; |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * Set the default path separator string; |
||
186 | * defaults to current JVM. |
||
187 | * |
||
188 | * @param string $sep path separator string |
||
189 | */ |
||
190 | public function setPathSep($sep) |
||
191 | { |
||
192 | $this->pathSep = $sep; |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * Set the default directory separator string. |
||
197 | * |
||
198 | * @param string $sep directory separator string |
||
199 | */ |
||
200 | public function setDirSep($sep) |
||
201 | { |
||
202 | $this->dirSep = $sep; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * Has the refid attribute of this element been set? |
||
207 | * |
||
208 | * @return true if refid is valid |
||
209 | */ |
||
210 | public function isReference() |
||
211 | { |
||
212 | return null !== $this->refid; |
||
|
|||
213 | } |
||
214 | |||
215 | /** |
||
216 | * Do the execution. |
||
217 | * |
||
218 | * @throws BuildException if something is invalid |
||
219 | */ |
||
220 | public function main() |
||
221 | { |
||
222 | $savedPath = $this->path; |
||
223 | $savedPathSep = $this->pathSep; // may be altered in validateSetup |
||
224 | $savedDirSep = $this->dirSep; // may be altered in validateSetup |
||
225 | |||
226 | try { |
||
227 | // If we are a reference, create a Path from the reference |
||
228 | if ($this->isReference()) { |
||
229 | $this->path = new Path($this->getProject()); |
||
230 | $this->path = $this->path->createPath(); |
||
231 | |||
232 | $obj = $this->refid->getReferencedObject($this->getProject()); |
||
233 | |||
234 | if ($obj instanceof Path) { |
||
235 | $this->path->setRefid($this->refid); |
||
236 | } elseif ($obj instanceof FileSet) { |
||
237 | $fs = $obj; |
||
238 | |||
239 | $this->path->addFileset($fs); |
||
240 | } elseif ($obj instanceof DirSet) { |
||
241 | $ds = $obj; |
||
242 | |||
243 | $this->path->addDirset($ds); |
||
244 | } elseif ($obj instanceof FileList) { |
||
245 | $fl = $obj; |
||
246 | |||
247 | $this->path->addFilelist($fl); |
||
248 | } else { |
||
249 | throw new BuildException( |
||
250 | "'refid' does not refer to a " |
||
251 | . 'path, fileset, dirset, or ' |
||
252 | . 'filelist.' |
||
253 | ); |
||
254 | } |
||
255 | } |
||
256 | |||
257 | $this->validateSetup(); // validate our setup |
||
258 | |||
259 | // Currently, we deal with only two path formats: Unix and Windows |
||
260 | // And Unix is everything that is not Windows |
||
261 | // (with the exception for NetWare and OS/2 below) |
||
262 | |||
263 | // for NetWare and OS/2, piggy-back on Windows, since here and |
||
264 | // in the apply code, the same assumptions can be made as with |
||
265 | // windows - that \\ is an OK separator, and do comparisons |
||
266 | // case-insensitive. |
||
267 | $fromDirSep = $this->onWindows ? '\\' : '/'; |
||
268 | |||
269 | $rslt = ''; |
||
270 | |||
271 | // Get the list of path components in canonical form |
||
272 | $elems = $this->path->listPaths($this->isPreserveDuplicates()); |
||
273 | |||
274 | $mapperImpl = null === $this->mapper ? new IdentityMapper() : $this->mapper->getImplementation(); |
||
275 | foreach ($elems as &$elem) { |
||
276 | $mapped = $mapperImpl->main($elem); |
||
277 | for ($m = 0; null !== $mapped && $m < count($mapped); ++$m) { |
||
278 | $elem = $mapped[$m]; |
||
279 | } |
||
280 | } |
||
281 | unset($elem); |
||
282 | foreach ($elems as $key => $elem) { |
||
283 | $elem = $this->mapElement($elem); // Apply the path prefix map |
||
284 | |||
285 | // Now convert the path and file separator characters from the |
||
286 | // current os to the target os. |
||
287 | |||
288 | if (0 !== $key) { |
||
289 | $rslt .= $this->pathSep; |
||
290 | } |
||
291 | |||
292 | $rslt .= str_replace($fromDirSep, $this->dirSep, $elem); |
||
293 | } |
||
294 | |||
295 | // Place the result into the specified property, |
||
296 | // unless setonempty == false |
||
297 | $value = $rslt; |
||
298 | if ($this->setonempty) { |
||
299 | $this->log( |
||
300 | 'Set property ' . $this->property . ' = ' . $value, |
||
301 | Project::MSG_VERBOSE |
||
302 | ); |
||
303 | $this->getProject()->setNewProperty($this->property, $value); |
||
304 | } else { |
||
305 | if ('' !== $rslt) { |
||
306 | $this->log( |
||
307 | 'Set property ' . $this->property . ' = ' . $value, |
||
308 | Project::MSG_VERBOSE |
||
309 | ); |
||
310 | $this->getProject()->setNewProperty($this->property, $value); |
||
311 | } |
||
312 | } |
||
313 | } finally { |
||
314 | $this->path = $savedPath; |
||
315 | $this->dirSep = $savedDirSep; |
||
316 | $this->pathSep = $savedPathSep; |
||
317 | } |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * @throws BuildException |
||
322 | * @throws IOException |
||
323 | */ |
||
324 | public function createMapper() |
||
325 | { |
||
326 | if (null !== $this->mapper) { |
||
327 | throw new BuildException('Cannot define more than one mapper', $this->getLocation()); |
||
328 | } |
||
329 | $this->mapper = new Mapper($this->project); |
||
330 | |||
331 | return $this->mapper; |
||
332 | } |
||
333 | |||
334 | /** |
||
335 | * Get the preserveDuplicates. |
||
336 | */ |
||
337 | public function isPreserveDuplicates(): bool |
||
338 | { |
||
339 | return $this->preserveDuplicates; |
||
340 | } |
||
341 | |||
342 | public function setPreserveDuplicates(bool $preserveDuplicates): void |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * Apply the configured map to a path element. The map is used to convert |
||
349 | * between Windows drive letters and Unix paths. If no map is configured, |
||
350 | * then the input string is returned unchanged. |
||
351 | * |
||
352 | * @param string $elem The path element to apply the map to |
||
353 | * |
||
354 | * @return string Updated element |
||
355 | */ |
||
356 | private function mapElement($elem) |
||
379 | } |
||
380 | |||
381 | /** |
||
382 | * Validate that all our parameters have been properly initialized. |
||
383 | * |
||
384 | * @throws BuildException if something is not setup properly |
||
385 | */ |
||
386 | private function validateSetup() |
||
387 | { |
||
388 | if (null === $this->path) { |
||
389 | throw new BuildException('You must specify a path to convert'); |
||
390 | } |
||
391 | |||
392 | // Determine the separator strings. The dirsep and pathsep attributes |
||
393 | // override the targetOS settings. |
||
394 | $dsep = FileUtils::getSeparator(); |
||
395 | $psep = FileUtils::getPathSeparator(); |
||
396 | |||
397 | if (null !== $this->targetOS) { |
||
398 | $psep = $this->targetWindows ? ';' : ':'; |
||
399 | $dsep = $this->targetWindows ? '\\' : '/'; |
||
400 | } |
||
401 | |||
402 | if (null !== $this->pathSep) {// override with pathsep= |
||
403 | $psep = $this->pathSep; |
||
404 | } |
||
405 | |||
406 | if (null !== $this->dirSep) {// override with dirsep= |
||
407 | $dsep = $this->dirSep; |
||
408 | } |
||
409 | |||
410 | $this->pathSep = $psep; |
||
411 | $this->dirSep = $dsep; |
||
412 | } |
||
413 | |||
414 | /** |
||
415 | * Creates an exception that indicates that this XML element must not have |
||
416 | * child elements if the refid attribute is set. |
||
417 | */ |
||
418 | private function noChildrenAllowed() |
||
423 | ); |
||
424 | } |
||
425 | } |
||
426 |