PathConvert::main()   F
last analyzed

Complexity

Conditions 15
Paths 584

Size

Total Lines 97
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 55
dl 0
loc 97
rs 2.3277
c 0
b 0
f 0
cc 15
nc 584
nop 0

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
/**
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the LGPL. For more information please see
18
 * <http://phing.info>.
19
 */
20
21
namespace Phing\Task\System;
22
23
use Phing\Exception\BuildException;
24
use Phing\Io\FileUtils;
25
use Phing\Io\IOException;
26
use Phing\Mapper\IdentityMapper;
27
use Phing\Project;
28
use Phing\Task;
29
use Phing\Type\DirSet;
30
use Phing\Type\FileList;
31
use Phing\Type\FileSet;
32
use Phing\Type\Mapper;
33
use Phing\Type\Path;
34
use Phing\Type\Reference;
35
36
/**
37
 * Converts path and classpath information to a specific target OS
38
 * format. The resulting formatted path is placed into the specified property.
39
 *
40
 * @author Siad Ardroumli <[email protected]>
41
 */
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)
142
    {
143
        $this->targetOS = $target;
144
        $this->targetWindows = 'unix' !== $this->targetOS;
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;
0 ignored issues
show
Bug Best Practice introduced by
The expression return null !== $this->refid returns the type boolean which is incompatible with the documented return type true.
Loading history...
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
343
    {
344
        $this->preserveDuplicates = $preserveDuplicates;
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)
357
    {
358
        $size = count($this->prefixMap);
359
360
        if (0 !== $size) {
361
            // Iterate over the map entries and apply each one.
362
            // Stop when one of the entries actually changes the element.
363
364
            foreach ($this->prefixMap as $entry) {
365
                $newElem = $entry->apply((string) $elem);
366
367
                // Note I'm using "!=" to see if we got a new object back from
368
                // the apply method.
369
370
                if ($newElem !== (string) $elem) {
371
                    $elem = $newElem;
372
373
                    break; // We applied one, so we're done
374
                }
375
            }
376
        }
377
378
        return $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()
419
    {
420
        return new BuildException(
421
            'You must not specify nested <path> '
422
            . 'elements when using the refid attribute.'
423
        );
424
    }
425
}
426