Passed
Push — master ( b9bc63...f1e2bc )
by Michiel
06:23
created

PathConvert::main()   F

Complexity

Conditions 15
Paths 584

Size

Total Lines 97
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 33.0643

Importance

Changes 0
Metric Value
eloc 55
dl 0
loc 97
ccs 29
cts 51
cp 0.5686
rs 2.3277
c 0
b 0
f 0
cc 15
nc 584
nop 0
crap 33.0643

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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