Passed
Push — master ( 77c655...406288 )
by Siad
10:48
created

PathConvert   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 388
Duplicated Lines 0 %

Test Coverage

Coverage 61.6%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 120
dl 0
loc 388
rs 8.8798
c 1
b 0
f 0
ccs 77
cts 125
cp 0.616
wmc 44

17 Methods

Rating   Name   Duplication   Size   Complexity  
A createPath() 0 10 3
A setDirSep() 0 3 1
A createMapper() 0 8 2
A setRefid() 0 7 2
A setSetonempty() 0 3 1
F main() 0 97 15
A mapElement() 0 22 4
A isReference() 0 3 1
A setPathSep() 0 3 1
A setTargetos() 0 4 1
A createMap() 0 7 1
A setProperty() 0 3 1
A __construct() 0 4 1
A noChildrenAllowed() 0 5 1
B validateSetup() 0 26 7
A isPreserveDuplicates() 0 3 1
A setPreserveDuplicates() 0 3 1

How to fix   Complexity   

Complex Class

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
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
/**
21
 * Converts path and classpath information to a specific target OS
22
 * format. The resulting formatted path is placed into the specified property.
23
 *
24
 * @author Siad Ardroumli <[email protected]>
25
 *
26
 * @package phing.tasks.system
27
 */
28
class PathConvert extends Task
29
{
30
    // Members
31
    /**
32
     * Path to be converted
33
     */
34
    private $path = null;
35
    /**
36
     * Reference to path/fileset to convert
37
     *
38
     * @var Reference $refid
39
     */
40
    private $refid = null;
41
    /**
42
     * The target OS type
43
     */
44
    private $targetOS = null;
45
    /**
46
     * Set when targetOS is set to windows
47
     */
48
    private $targetWindows = false;
49
    /**
50
     * Set if we're running on windows
51
     */
52
    public $onWindows = false;
53
    /**
54
     * Set if we should create a new property even if the result is empty
55
     */
56
    private $setonempty = true;
57
    /**
58
     * The property to receive the conversion
59
     */
60
    private $property = null;
61
    /**
62
     * Path prefix map
63
     *
64
     * @var MapEntry[]
65
     */
66
    private $prefixMap = [];
67
    /**
68
     * User override on path sep char
69
     */
70
    private $pathSep = null;
71
    /**
72
     * User override on directory sep char
73
     */
74
    private $dirSep = null;
75
76
    public $from = null;
77
    public $to = null;
78
    private $mapper;
79
    private $preserveDuplicates = false;
80
81
    /**
82
     * constructor
83
     */
84 3
    public function __construct()
85
    {
86 3
        parent::__construct();
87 3
        $this->onWindows = strncasecmp(PHP_OS, 'WIN', 3) === 0;
88 3
    }
89
90
    /**
91
     * Create a nested PATH element
92
     */
93 3
    public function createPath()
94
    {
95 3
        if ($this->isReference()) {
96
            throw $this->noChildrenAllowed();
97
        }
98
99 3
        if ($this->path === null) {
100 3
            $this->path = new Path($this->getProject());
101
        }
102 3
        return $this->path->createPath();
103
    }
104
105
106
    /**
107
     * Create a nested MAP element
108
     *
109
     * @return MapEntry a Map to configure
110
     */
111 2
    public function createMap()
112
    {
113 2
        $entry = new MapEntry($this);
114
115 2
        $this->prefixMap[] = $entry;
116
117 2
        return $entry;
118
    }
119
120
    /**
121
     * Set targetos to a platform to one of
122
     * "windows", "unix", "netware", or "os/2"; required unless
123
     * unless pathsep and/or dirsep are specified.
124
     */
125
    public function setTargetos($target)
126
    {
127
        $this->targetOS = $target;
128
        $this->targetWindows = $this->targetOS !== 'unix';
129
    }
130
131
    /**
132
     * Set setonempty
133
     *
134
     * If false, don't set the new property if the result is the empty string.
135
     *
136
     * @param bool $setonempty true or false
137
     */
138
    public function setSetonempty($setonempty)
139
    {
140
        $this->setonempty = $setonempty;
141
    }
142
143
    /**
144
     * The property into which the converted path will be placed.
145
     */
146 3
    public function setProperty($p)
147
    {
148 3
        $this->property = $p;
149 3
    }
150
151
    /**
152
     * Adds a reference to a Path, FileSet, DirSet, or FileList defined
153
     * elsewhere.
154
     *
155
     * @param Reference $r
156
     *
157
     * @throws BuildException
158
     */
159
    public function setRefid(Reference $r)
160
    {
161
        if ($this->path !== null) {
162
            throw $this->noChildrenAllowed();
163
        }
164
165
        $this->refid = $r;
166
    }
167
168
169
    /**
170
     * Set the default path separator string;
171
     * defaults to current JVM
172
     *
173
     * @param string $sep path separator string
174
     */
175
    public function setPathSep($sep)
176
    {
177
        $this->pathSep = $sep;
178
    }
179
180
181
    /**
182
     * Set the default directory separator string
183
     *
184
     * @param string $sep directory separator string
185
     */
186 3
    public function setDirSep($sep)
187
    {
188 3
        $this->dirSep = $sep;
189 3
    }
190
191
192
    /**
193
     * Has the refid attribute of this element been set?
194
     *
195
     * @return true if refid is valid
196
     */
197 3
    public function isReference()
198
    {
199 3
        return $this->refid !== null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->refid !== null returns the type boolean which is incompatible with the documented return type true.
Loading history...
200
    }
201
202
203
    /**
204
     * Do the execution.
205
     *
206
     * @throws BuildException if something is invalid
207
     */
208 3
    public function main()
209
    {
210 3
        $savedPath = $this->path;
211 3
        $savedPathSep = $this->pathSep;// may be altered in validateSetup
212 3
        $savedDirSep = $this->dirSep;// may be altered in validateSetup
213
214
        try {
215
            // If we are a reference, create a Path from the reference
216 3
            if ($this->isReference()) {
217
                $this->path = new Path($this->getProject());
218
                $this->path = $this->path->createPath();
219
220
                $obj = $this->refid->getReferencedObject($this->getProject());
221
222
                if ($obj instanceof Path) {
223
                    $this->path->setRefid($this->refid);
224
                } elseif ($obj instanceof FileSet) {
225
                    $fs = $obj;
226
227
                    $this->path->addFileset($fs);
228
                } elseif ($obj instanceof DirSet) {
229
                    $ds = $obj;
230
231
                    $this->path->addDirset($ds);
232
                } elseif ($obj instanceof FileList) {
233
                    $fl = $obj;
234
235
                    $this->path->addFilelist($fl);
236
                } else {
237
                    throw new BuildException(
238
                        "'refid' does not refer to a "
239
                        . "path, fileset, dirset, or "
240
                        . "filelist."
241
                    );
242
                }
243
            }
244
245 3
            $this->validateSetup();// validate our setup
246
247
            // Currently, we deal with only two path formats: Unix and Windows
248
            // And Unix is everything that is not Windows
249
            // (with the exception for NetWare and OS/2 below)
250
251
            // for NetWare and OS/2, piggy-back on Windows, since here and
252
            // in the apply code, the same assumptions can be made as with
253
            // windows - that \\ is an OK separator, and do comparisons
254
            // case-insensitive.
255 3
            $fromDirSep = $this->onWindows ? "\\" : "/";
256
257 3
            $rslt = '';
258
259
            // Get the list of path components in canonical form
260 3
            $elems = $this->path->listPaths($this->isPreserveDuplicates());
261
262 3
            $mapperImpl = $this->mapper === null ? new IdentityMapper() : $this->mapper->getImplementation();
263 3
            foreach ($elems as &$elem) {
264 3
                $mapped = $mapperImpl->main($elem);
265 3
                for ($m = 0; $mapped !== null && $m < count($mapped); ++$m) {
266 3
                    $elem = $mapped[$m];
267
                }
268
            }
269 3
            unset($elem);
270 3
            foreach ($elems as $key => $elem) {
271 3
                $elem = $this->mapElement($elem);// Apply the path prefix map
272
273
                // Now convert the path and file separator characters from the
274
                // current os to the target os.
275
276 3
                if ($key !== 0) {
277
                    $rslt .= $this->pathSep;
278
                }
279
280 3
                $rslt .= str_replace($fromDirSep, $this->dirSep, $elem);
281
            }
282
283
            // Place the result into the specified property,
284
            // unless setonempty == false
285 3
            $value = $rslt;
286 3
            if ($this->setonempty) {
287 3
                $this->log(
288 3
                    "Set property " . $this->property . " = " . $value,
289 3
                    Project::MSG_VERBOSE
290
                );
291 3
                $this->getProject()->setNewProperty($this->property, $value);
292
            } else {
293
                if ($rslt !== '') {
294
                    $this->log(
295
                        "Set property " . $this->property . " = " . $value,
296
                        Project::MSG_VERBOSE
297
                    );
298
                    $this->getProject()->setNewProperty($this->property, $value);
299
                }
300
            }
301 3
        } finally {
302 3
            $this->path = $savedPath;
303 3
            $this->dirSep = $savedDirSep;
304 3
            $this->pathSep = $savedPathSep;
305
        }
306 3
    }
307
308
    /**
309
     * Apply the configured map to a path element. The map is used to convert
310
     * between Windows drive letters and Unix paths. If no map is configured,
311
     * then the input string is returned unchanged.
312
     *
313
     * @param  string $elem The path element to apply the map to
314
     * @return String Updated element
315
     */
316 3
    private function mapElement($elem)
317
    {
318 3
        $size = count($this->prefixMap);
319
320 3
        if ($size !== 0) {
321
            // Iterate over the map entries and apply each one.
322
            // Stop when one of the entries actually changes the element.
323
324 2
            foreach ($this->prefixMap as $entry) {
325 2
                $newElem = $entry->apply((string) $elem);
326
327
                // Note I'm using "!=" to see if we got a new object back from
328
                // the apply method.
329
330 2
                if ($newElem !== (string) $elem) {
331 2
                    $elem = $newElem;
332 2
                    break;// We applied one, so we're done
333
                }
334
            }
335
        }
336
337 3
        return $elem;
338
    }
339
340
    /**
341
     * @throws BuildException
342
     * @throws IOException
343
     */
344 1
    public function createMapper()
345
    {
346 1
        if ($this->mapper !== null) {
347
            throw new BuildException("Cannot define more than one mapper", $this->getLocation());
348
        }
349 1
        $this->mapper = new Mapper($this->project);
350
351 1
        return $this->mapper;
352
    }
353
354
    /**
355
     * Validate that all our parameters have been properly initialized.
356
     *
357
     * @throws BuildException if something is not setup properly
358
     */
359 3
    private function validateSetup()
360
    {
361 3
        if ($this->path === null) {
362
            throw new BuildException("You must specify a path to convert");
363
        }
364
365
        // Determine the separator strings.  The dirsep and pathsep attributes
366
        // override the targetOS settings.
367 3
        $dsep = FileUtils::getSeparator();
368 3
        $psep = FileUtils::getPathSeparator();
369
370 3
        if ($this->targetOS !== null) {
371
            $psep = $this->targetWindows ? ";" : ":";
372
            $dsep = $this->targetWindows ? "\\" : "/";
373
        }
374
375 3
        if ($this->pathSep !== null) {// override with pathsep=
376
            $psep = $this->pathSep;
377
        }
378
379 3
        if ($this->dirSep !== null) {// override with dirsep=
380 3
            $dsep = $this->dirSep;
381
        }
382
383 3
        $this->pathSep = $psep;
384 3
        $this->dirSep = $dsep;
385 3
    }
386
387
388
    /**
389
     * Creates an exception that indicates that this XML element must not have
390
     * child elements if the refid attribute is set.
391
     */
392
    private function noChildrenAllowed()
393
    {
394
        return new BuildException(
395
            "You must not specify nested <path> "
396
            . "elements when using the refid attribute."
397
        );
398
    }
399
400
    /**
401
     * Get the preserveDuplicates.
402
     *
403
     * @return boolean
404
     */
405 3
    public function isPreserveDuplicates(): bool
406
    {
407 3
        return $this->preserveDuplicates;
408
    }
409
410
    /**
411
     * @param bool $preserveDuplicates
412
     */
413
    public function setPreserveDuplicates(bool $preserveDuplicates): void
414
    {
415
        $this->preserveDuplicates = $preserveDuplicates;
416
    }
417
}
418