ModifiedSelector::configure()   F
last analyzed

Complexity

Conditions 19
Paths 1117

Size

Total Lines 96
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 39
CRAP Score 23.7029

Importance

Changes 0
Metric Value
cc 19
eloc 55
nc 1117
nop 0
dl 0
loc 96
ccs 39
cts 51
cp 0.7647
crap 23.7029
rs 0.3499
c 0
b 0
f 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\Type\Selector;
22
23
use Phing\Exception\BuildException;
24
use Phing\IntrospectionHelper;
25
use Phing\Io\File;
26
use Phing\Listener\BuildEvent;
27
use Phing\Listener\BuildListener;
28
use Phing\Phing;
29
use Phing\Project;
30
use Phing\Type\Parameter;
31
use Phing\Type\Selector\Modified\Algorithm;
32
use Phing\Type\Selector\Modified\Cache;
33
use Phing\Type\Selector\Modified\Comparator;
34
use Phing\Type\Selector\Modified\EqualComparator;
35
use Phing\Type\Selector\Modified\HashfileAlgorithm;
36
use Phing\Type\Selector\Modified\LastModifiedAlgorithm;
37
use Phing\Type\Selector\Modified\PropertiesCache;
38
use Phing\Util\StringHelper;
39
use Throwable;
40
41
/**
42
 * @author  Siad Ardroumli <[email protected]>
43
 */
44
class ModifiedSelector extends BaseExtendSelector implements BuildListener
45
{
46
    private const CACHE_PREFIX = 'cache.';
47
    private const ALGORITHM_PREFIX = 'algorithm.';
48
    private const COMPARATOR_PREFIX = 'comparator.';
49
50
    /** Cache name for later instantiation. */
51
    private $cacheName;
52
53
    /** User specified classname for Cache. */
54
    private $cacheClass;
55
56
    /** Algorithm name for later instantiation. */
57
    private $algoName;
58
59
    /** User specified classname for Algorithm. */
60
    private $algorithmClass;
61
62
    /** Comparator name for later instantiation. */
63
    private $compName;
64
65
    /** User specified classname for Comparator. */
66
    private $comparatorClass;
67
68
    private $update = true;
69
    private $selectDirectories = true;
70
    /** @var Cache */
71
    private $cache;
72
    /** @var Algorithm */
73
    private $algorithm;
74
    /** @var Comparator */
75
    private $comparator;
76
    private $delayUpdate = true;
77
    private $modified = 0;
78
    private $isConfigured = false;
79
    /** @var File */
80
    private $cachefile;
81
    /** @var Parameter[] */
82
    private $configParameter = [];
83
    /** @var Parameter[] */
84
    private $specialParameter = [];
85
86 2
    public function __toString(): string
87
    {
88 2
        $this->configure();
89
90 2
        return sprintf(
91 2
            '{modifiedselector update=%s seldirs=%s cache=%s algorithm=%s comparator=%s}',
92 2
            true === $this->update ? 'true' : 'false',
93 2
            true === $this->selectDirectories ? 'true' : 'false',
94 2
            $this->cache,
0 ignored issues
show
Bug introduced by
$this->cache of type Phing\Type\Selector\Modified\Cache is incompatible with the type double|integer|string expected by parameter $values of sprintf(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

94
            /** @scrutinizer ignore-type */ $this->cache,
Loading history...
95 2
            $this->algorithm,
0 ignored issues
show
Bug introduced by
$this->algorithm of type Phing\Type\Selector\Modified\Algorithm is incompatible with the type double|integer|string expected by parameter $values of sprintf(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

95
            /** @scrutinizer ignore-type */ $this->algorithm,
Loading history...
96 2
            $this->comparator
97 2
        );
98
    }
99
100
    /**
101
     * Get the cache type to use.
102
     *
103
     * @return Cache the enumerated cache type
104
     */
105
    public function getCache(): Cache
106
    {
107
        return $this->cache;
108
    }
109
110
    /**
111
     * Set the cache type to use.
112
     *
113
     * @param string $name an enumerated cache type
114
     */
115 3
    public function setCache(string $name): void
116
    {
117 3
        $this->cacheName = $name;
118
    }
119
120 4
    public function verifySettings()
121
    {
122 4
        $this->configure();
123 4
        if (null === $this->cache) {
124 1
            $this->setError('Cache must be set.');
125 3
        } elseif (null === $this->algorithm) {
126 1
            $this->setError('Algorithm must be set.');
127 2
        } elseif (!$this->cache->isValid()) {
128
            $this->setError('Cache must be proper configured.');
129 2
        } elseif (!$this->algorithm->isValid()) {
130
            $this->setError('Algorithm must be proper configured.');
131
        }
132
    }
133
134 4
    public function configure(): void
135
    {
136 4
        if ($this->isConfigured) {
137 2
            return;
138
        }
139 4
        $this->isConfigured = true;
140
141 4
        $p = $this->getProject();
142 4
        $filename = 'cache.properties';
143 4
        if (null !== $p) {
144
            // normal use inside Phing
145 2
            $this->cachefile = new File($p->getBasedir(), $filename);
146
147
            // set self as a BuildListener to delay cachefile saves
148 2
            $this->getProject()->addBuildListener($this);
149
        } else {
150
            // no reference to project - e.g. during normal JUnit tests
151 2
            $this->cachefile = new File($filename);
152 2
            $this->setDelayUpdate(false);
153
        }
154 4
        $defaultCache = new PropertiesCache($this->cachefile);
155 4
        $defaultAlgorithm = new HashfileAlgorithm();
156 4
        $defaultComparator = new EqualComparator();
157
158
        //
159
        // -----  Set the main attributes, pattern '*'  -----
160
        //
161 4
        foreach ($this->configParameter as $parameter) {
162 2
            if (strpos($parameter->getName(), '.') > 0) {
163
                // this is a *.* parameter for later use
164 2
                $this->specialParameter[] = $parameter;
165
            } else {
166 1
                $this->useParameter($parameter);
167
            }
168
        }
169 4
        $this->configParameter = [];
170
171
        // specify the algorithm classname
172 4
        if (null !== $this->algoName) {
173
            // use Algorithm defined via name
174 3
            if ('hashfile' === $this->algoName) {
175 1
                $this->algorithm = new HashfileAlgorithm();
176 2
            } elseif ('lastmodified' === $this->algoName) {
177 3
                $this->algorithm = new LastModifiedAlgorithm();
178
            }
179 1
        } elseif (null !== $this->algorithmClass) {
180
            // use Algorithm specified by classname
181
            $clz = Phing::import($this->algorithmClass);
182
            $this->algorithm = new $clz();
183
            if (!$this->algorithm instanceof Algorithm) {
184
                throw new BuildException($this->algorithmClass . ' is not an Algorithm.');
185
            }
186
        } else {
187
            // nothing specified - use default
188 1
            $this->algorithm = $defaultAlgorithm;
189
        }
190
191
        // specify the cache classname
192 4
        if (null !== $this->cacheName) {
193
            // use Cache defined via name
194 3
            if ('propertyfile' === $this->cacheName) {
195 3
                $this->cache = new PropertiesCache();
196
            }
197 1
        } elseif (null !== $this->cacheClass) {
198
            // use Cache specified by classname
199
            $clz = Phing::import($this->cacheClass);
200
            $this->cache = new $clz();
201
            if (!$this->cache instanceof Cache) {
202
                throw new BuildException($this->cacheClass . ' is not a Cache.');
203
            }
204
        } else {
205
            // nothing specified - use default
206 1
            $this->cache = $defaultCache;
207
        }
208
209
        // specify the comparator classname
210 4
        if (null !== $this->compName) {
211
            // use Algorithm defined via name
212 2
            if ('equal' === $this->compName) {
213 2
                $this->comparator = new EqualComparator();
214
            }
215 2
        } elseif (null !== $this->comparatorClass) {
216
            // use Comparator specified by classname
217
            $localComparator = Phing::import($this->comparatorClass);
218
            if (!$localComparator instanceof Comparator) {
0 ignored issues
show
introduced by
$localComparator is never a sub-type of Phing\Type\Selector\Modified\Comparator.
Loading history...
219
                throw new BuildException($this->comparatorClass . ' is not a Comparator.');
220
            }
221
            $this->comparator = $localComparator;
222
        } else {
223
            // nothing specified - use default
224 2
            $this->comparator = $defaultComparator;
225
        }
226 4
        foreach ($this->specialParameter as $special) {
227 2
            $this->useParameter($special);
228
        }
229 4
        $this->specialParameter = [];
230
    }
231
232
    /**
233
     * Support for nested <code>&lt;param name="" value=""/&gt;</code> tags.
234
     * Parameter named <i>cache</i>, <i>algorithm</i>,
235
     * <i>comparator</i> or <i>update</i> are mapped to
236
     * the respective set-Method.
237
     * Parameter which names starts with <i>cache.</i> or
238
     * <i>algorithm.</i> or <i>comparator.</i> are tried
239
     * to set on the appropriate object via its set-methods.
240
     * Other parameters are invalid and an BuildException will
241
     * be thrown.
242
     *
243
     * @param Parameter $parameter Key and value as parameter object
244
     */
245 2
    public function useParameter(Parameter $parameter): void
246
    {
247 2
        $key = $parameter->getName();
248 2
        $value = $parameter->getValue();
249 2
        if ('cache' === $key) {
250 1
            $this->setCache($value);
251 2
        } elseif ('algorithm' === $key) {
252 1
            $this->setAlgorithm($value);
253 2
        } elseif ('comparator' === $key) {
254 1
            $this->setComparator($value);
255 2
        } elseif ('update' === $key) {
256 1
            $this->setUpdate(StringHelper::booleanValue($value));
257 2
        } elseif ('delayupdate' === $key) {
258
            $this->setDelayUpdate(StringHelper::booleanValue($value));
259 2
        } elseif ('seldirs' === $key) {
260 1
            $this->setSeldirs(StringHelper::booleanValue($value));
261 2
        } elseif (StringHelper::startsWith(self::CACHE_PREFIX, $key)) {
262 2
            $name = StringHelper::substring($key, strlen(self::CACHE_PREFIX));
263 2
            $this->tryToSetAParameter($this->cache, $name, $value);
264 2
        } elseif (StringHelper::startsWith(self::ALGORITHM_PREFIX, $key)) {
265 2
            $name = StringHelper::substring($key, strlen(self::ALGORITHM_PREFIX));
266 2
            $this->tryToSetAParameter($this->algorithm, $name, $value);
267
        } elseif (StringHelper::startsWith(self::COMPARATOR_PREFIX, $key)) {
268
            $name = StringHelper::substring($key, strlen(self::COMPARATOR_PREFIX));
269
            $this->tryToSetAParameter($this->comparator, $name, $value);
270
        } else {
271
            $this->setError('Invalid parameter ' . $key);
272
        }
273
    }
274
275
    /**
276
     * Support for <i>update</i> attribute.
277
     *
278
     * @param bool $update new value
279
     */
280 2
    public function setUpdate(bool $update): void
281
    {
282 2
        $this->update = $update;
283
    }
284
285
    /**
286
     * Support for <i>seldirs</i> attribute.
287
     *
288
     * @param bool $seldirs new value
289
     */
290 2
    public function setSeldirs(bool $seldirs): void
291
    {
292 2
        $this->selectDirectories = $seldirs;
293
    }
294
295
    /**
296
     * Support for nested &lt;param&gt; tags.
297
     *
298
     * @param Parameter $parameter the parameter object
299
     */
300 2
    public function addParam(Parameter $parameter)
301
    {
302 2
        $this->configParameter[] = $parameter;
303
    }
304
305
    /**
306
     * Defined in org.apache.tools.ant.types.Parameterizable.
307
     * Overwrite implementation in superclass because only special
308
     * parameters are valid.
309
     *
310
     * @see #addParam(String,Object)
311
     *
312
     * @param array $parameters the parameters to set
313
     */
314
    public function setParameters(array $parameters): void
315
    {
316
        parent::setParameters($parameters);
317
        foreach ($parameters as $param) {
318
            $this->configParameter[] = $param;
319
        }
320
    }
321
322
    public function buildStarted(BuildEvent $event)
323
    {
324
        // do nothing
325
    }
326
327
    public function buildFinished(BuildEvent $event)
328
    {
329
        if ($this->getDelayUpdate()) {
330
            $this->saveCache();
331
        }
332
    }
333
334
    /**
335
     * Getter for the delay update.
336
     *
337
     * @return bool true if we should delay for performance
338
     */
339 2
    public function getDelayUpdate(): bool
340
    {
341 2
        return $this->delayUpdate;
342
    }
343
344 2
    public function setDelayUpdate(bool $false): void
345
    {
346 2
        $this->delayUpdate = $false;
347
    }
348
349
    /**
350
     * Getter for the modified count.
351
     *
352
     * @return int $modified count
353
     */
354 2
    public function getModified(): int
355
    {
356 2
        return $this->modified;
357
    }
358
359
    /**
360
     * Setter for the modified count.
361
     *
362
     * @param int $modified count
363
     */
364 2
    public function setModified(int $modified): void
365
    {
366 2
        $this->modified = $modified;
367
    }
368
369
    /**
370
     * Setter for algorithmClass.
371
     */
372
    public function setAlgorithmClass(string $classname): void
373
    {
374
        $this->algorithmClass = $classname;
375
    }
376
377
    /**
378
     * Setter for comparatorClass.
379
     */
380
    public function setComparatorClass(string $classname): void
381
    {
382
        $this->comparatorClass = $classname;
383
    }
384
385
    /**
386
     * Setter for cacheClass.
387
     */
388
    public function setCacheClass(string $classname): void
389
    {
390
        $this->cacheClass = $classname;
391
    }
392
393 2
    public function targetStarted(BuildEvent $event)
394
    {
395
        // do nothing
396 2
    }
397
398 2
    public function targetFinished(BuildEvent $event)
399
    {
400 2
        if ($this->getDelayUpdate()) {
401 2
            $this->saveCache();
402
        }
403
    }
404
405 2
    public function taskStarted(BuildEvent $event)
406
    {
407
        // do nothing
408 2
    }
409
410 2
    public function taskFinished(BuildEvent $event)
411
    {
412 2
        if ($this->getDelayUpdate()) {
413 2
            $this->saveCache();
414
        }
415
    }
416
417 2
    public function messageLogged(BuildEvent $event)
418
    {
419
        // do nothing
420 2
    }
421
422
    /**
423
     * @param string $filename
424
     *
425
     * @return null|bool
426
     */
427 2
    public function isSelected(File $basedir, $filename, File $file)
428
    {
429 2
        $this->validate();
430
431
        try {
432 2
            $f = new File($basedir, $filename);
433
434
            // You can not compute a value for a directory
435 2
            if ($f->isDirectory()) {
436 2
                return $this->selectDirectories;
437
            }
438
        } catch (Throwable $t) {
439
            throw new BuildException($t);
440
        }
441
442
        // Get the values and do the comparison
443 2
        $cachedValue = (string) $this->cache->get($f->getAbsolutePath());
444 2
        $newValue = $this->algorithm->getValue($f);
445
446 2
        $rv = 0 !== $this->comparator->compare($cachedValue, $newValue);
447
448
        // Maybe update the cache
449 2
        if ($this->update && $rv) {
450 2
            $this->cache->put($f->getAbsolutePath(), $newValue);
451 2
            $this->setModified($this->getModified() + 1);
452 2
            if (!$this->getDelayUpdate()) {
453
                $this->saveCache();
454
            }
455
        }
456
457 2
        return $rv;
458
    }
459
460 3
    public function setAlgorithm(string $an): void
461
    {
462 3
        $this->algoName = $an;
463
    }
464
465 2
    public function setComparator($value): void
466
    {
467 2
        $this->compName = $value;
468
    }
469
470
    /**
471
     * Try to set a value on an object using reflection.
472
     * Helper method for easier access to IntrospectionHelper.setAttribute().
473
     *
474
     * @param object $obj   the object on which the attribute should be set
475
     * @param string $name  the attributename
476
     * @param string $value the new value
477
     */
478 2
    protected function tryToSetAParameter(object $obj, string $name, string $value): void
479
    {
480 2
        $prj = $this->getProject() ?? new Project();
481 2
        $iHelper = IntrospectionHelper::getHelper(get_class($obj));
482
483
        try {
484 2
            $iHelper->setAttribute($prj, $obj, $name, $value);
485 1
        } catch (BuildException $e) {
486
            // no-op
487
        }
488
    }
489
490
    /**
491
     * save the cache file.
492
     */
493 2
    protected function saveCache(): void
494
    {
495 2
        if ($this->getModified() > 0) {
496 2
            $this->cache->save();
497 2
            $this->setModified(0);
498
        }
499
    }
500
}
501