Passed
Push — main ( 95b369...5c384e )
by Michiel
17:35 queued 12s
created

ComponentHelper::initCustomTasks()   A

Complexity

Conditions 4
Paths 11

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.9102

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 18
ccs 8
cts 13
cp 0.6153
rs 9.8333
c 0
b 0
f 0
cc 4
nc 11
nop 0
crap 4.9102
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;
22
23
use Exception;
24
use Phing\Exception\BuildException;
25
use Phing\Io\File;
26
use Phing\Io\IOException;
27
use Phing\Task\System\Condition\Condition;
28
use Phing\Type\DataType;
29
use Phing\Util\Properties;
30
use ReflectionClass;
31
32
/**
33
 * Component creation and configuration.
34
 *
35
 * @author Michiel Rook <[email protected]>
36
 */
37
class ComponentHelper
38
{
39
    public const COMPONENT_HELPER_REFERENCE = 'phing.ComponentHelper';
40
41
    /**
42
     * @var Project
43
     */
44
    private $project;
45
46
    /**
47
     * task definitions for this project.
48
     *
49
     * @var string[]
50
     */
51
    private $taskdefs = [];
52
53
    /**
54
     * type definitions for this project.
55
     *
56
     * @var string[]
57
     */
58
    private $typedefs = [];
59
60
    /**
61
     * ComponentHelper constructor.
62
     */
63 893
    public function __construct(Project $project)
64
    {
65 893
        $this->project = $project;
66
    }
67
68 893
    public static function getComponentHelper(Project $project): ComponentHelper
69
    {
70
        /** @var ComponentHelper $componentHelper */
71 893
        $componentHelper = $project->getReference(self::COMPONENT_HELPER_REFERENCE);
72
73 893
        if (null !== $componentHelper) {
74 892
            return $componentHelper;
75
        }
76
77 893
        $componentHelper = new ComponentHelper($project);
78 893
        $project->addReference(self::COMPONENT_HELPER_REFERENCE, $componentHelper);
79
80 893
        return $componentHelper;
81
    }
82
83
    /**
84
     * Initializes the default tasks and data types.
85
     */
86 890
    public function initDefaultDefinitions(): void
87
    {
88 890
        $this->initDefaultTasks();
89 890
        $this->initDefaultDataTypes();
90 890
        $this->initCustomTasks();
91 890
        $this->initCustomDataTypes();
92
    }
93
94
    /**
95
     * Adds a task definition.
96
     *
97
     * @param string $name      name of tag
98
     * @param string $class     the class path to use
99
     * @param string $classpath the classpat to use
100
     */
101 890
    public function addTaskDefinition($name, $class, $classpath = null): void
102
    {
103 890
        if ('' === $class) {
104
            $this->project->log("Task {$name} has no class defined.", Project::MSG_ERR);
105 890
        } elseif (!isset($this->taskdefs[$name])) {
106 890
            Phing::import($class, $classpath);
107 890
            $this->taskdefs[$name] = $class;
108 890
            $this->project->log("  +Task definition: {$name} ({$class})", Project::MSG_DEBUG);
109
        } else {
110 14
            $this->project->log("Task {$name} ({$class}) already registered, skipping", Project::MSG_VERBOSE);
111
        }
112
    }
113
114
    /**
115
     * Returns the task definitions.
116
     */
117 46
    public function getTaskDefinitions(): array
118
    {
119 46
        return $this->taskdefs;
120
    }
121
122
    /**
123
     * Adds a data type definition.
124
     *
125
     * @param string $typeName  name of the type
126
     * @param string $typeClass the class to use
127
     * @param string $classpath the classpath to use
128
     */
129 890
    public function addDataTypeDefinition($typeName, $typeClass, $classpath = null): void
130
    {
131 890
        if (!isset($this->typedefs[$typeName])) {
132 890
            Phing::import($typeClass, $classpath);
133 890
            $this->typedefs[$typeName] = $typeClass;
134 890
            $this->project->log("  +User datatype: {$typeName} ({$typeClass})", Project::MSG_DEBUG);
135
        } else {
136 33
            $this->project->log("Type {$typeName} ({$typeClass}) already registered, skipping", Project::MSG_VERBOSE);
137
        }
138
    }
139
140 139
    public static function getElementName(Project $p = null, $o = null, $brief = false)
141
    {
142 139
        return null === $p
143 139
            ? self::getUnmappedElementName($o, $brief)
144 139
            : self::getComponentHelper($p)->getElementName(null, $o, $brief);
145
    }
146
147
    /**
148
     * Returns the data type definitions.
149
     */
150 891
    public function getDataTypeDefinitions(): array
151
    {
152 891
        return $this->typedefs;
153
    }
154
155
    /**
156
     * Create a new task instance and return reference to it.
157
     *
158
     * @param string $taskType Task name
159
     *
160
     * @throws BuildException
161
     *
162
     * @return Task A task object
163
     */
164 751
    public function createTask(string $taskType)
165
    {
166
        try {
167 751
            $classname = '';
168 751
            $tasklwr = strtolower($taskType);
169 751
            foreach ($this->taskdefs as $name => $class) {
170 751
                if (strtolower($name) === $tasklwr) {
171 746
                    $classname = $class;
172
173 746
                    break;
174
                }
175
            }
176
177 751
            if ('' === $classname) {
178 83
                return null;
179
            }
180
181 746
            $o = $this->createObject($classname);
182
183 746
            if ($o instanceof Task) {
184 724
                $task = $o;
185
            } else {
186 233
                $this->project->log("  (Using TaskAdapter for: {$taskType})", Project::MSG_DEBUG);
187
                // not a real task, try adapter
188 233
                $taskA = new TaskAdapter();
189 233
                $taskA->setProxy($o);
190 233
                $task = $taskA;
191
            }
192 746
            $task->setProject($this->project);
193 746
            $task->setTaskType($taskType);
194
            // set default value, can be changed by the user
195 746
            $task->setTaskName($taskType);
196 746
            $this->project->log('  +Task: ' . $taskType, Project::MSG_DEBUG);
197
        } catch (Exception $t) {
198
            throw new BuildException('Could not create task of type: ' . $taskType, $t);
199
        }
200
        // everything fine return reference
201 746
        return $task;
202
    }
203
204
    /**
205
     * Creates a new condition and returns the reference to it.
206
     *
207
     * @throws BuildException
208
     *
209
     * @return Condition
210
     */
211 1
    public function createCondition(string $conditionType): ?Condition
212
    {
213
        try {
214 1
            $classname = '';
215 1
            $tasklwr = strtolower($conditionType);
216 1
            foreach ($this->typedefs as $name => $class) {
217 1
                if (strtolower($name) === $tasklwr) {
218 1
                    $classname = $class;
219
220 1
                    break;
221
                }
222
            }
223
224 1
            if ('' === $classname) {
225
                return null;
226
            }
227
228 1
            $o = $this->createObject($classname);
229
230 1
            if ($o instanceof Condition) {
231 1
                return $o;
232
            }
233
234
            throw new BuildException('Not actually a condition');
235
        } catch (Exception $e) {
236
            throw new BuildException('Could not create condition of type: ' . $conditionType, $e);
237
        }
238
    }
239
240
    /**
241
     * Create a datatype instance and return reference to it
242
     * See createTask() for explanation how this works.
243
     *
244
     * @param string $typeName Type name
245
     *
246
     * @throws BuildException Exception
247
     *
248
     * @return object A datatype object
249
     */
250 83
    public function createDataType(string $typeName)
251
    {
252
        try {
253 83
            $cls = '';
254 83
            $typelwr = strtolower($typeName);
255 83
            foreach ($this->typedefs as $name => $class) {
256 83
                if (strtolower($name) === $typelwr) {
257 83
                    $cls = $class;
258
259 83
                    break;
260
                }
261
            }
262
263 83
            if ('' === $cls) {
264
                return null;
265
            }
266
267 83
            if (!class_exists($cls)) {
268
                throw new BuildException(
269
                    "Could not instantiate class {$cls}, even though a class was specified. (Make sure that the specified class file contains a class with the correct name.)"
270
                );
271
            }
272
273 83
            $type = new $cls();
274 83
            $this->project->log("  +Type: {$typeName}", Project::MSG_DEBUG);
275 83
            if ($type instanceof ProjectComponent) {
276 83
                $type->setProject($this->project);
277
            }
278 83
            if (!($type instanceof DataType)) {
279 83
                throw new Exception("{$cls} is not an instance of phing.types.DataType");
280
            }
281
        } catch (Exception $t) {
282
            throw new BuildException("Could not create type: {$typeName}", $t);
283
        }
284
        // everything fine return reference
285 83
        return $type;
286
    }
287
288 36
    public function initSubProject(ComponentHelper $helper): void
289
    {
290 36
        $dataTypes = $helper->getDataTypeDefinitions();
291 36
        foreach ($dataTypes as $name => $class) {
292 36
            $this->addDataTypeDefinition($name, $class);
293
        }
294
    }
295
296
    /**
297
     * @param object|string $c
298
     * @param $brief
299
     *
300
     * @throws \ReflectionException
301
     */
302 139
    private static function getUnmappedElementName($c, $brief): string
303
    {
304 139
        $clazz = new ReflectionClass($c);
305 139
        $name = $clazz->getName();
306
307 139
        if ($brief) {
308 139
            return $clazz->getShortName();
309
        }
310
311
        return $name;
312
    }
313
314 746
    private function createObject(string $classname)
315
    {
316 746
        if ('' === $classname) {
317
            return null;
318
        }
319
320 746
        $cls = Phing::import($classname);
321
322 746
        if (!class_exists($cls)) {
323
            throw new BuildException(
324
                "Could not instantiate class {$cls}, even though a class was specified. (Make sure that the specified class file contains a class with the correct name.)"
325
            );
326
        }
327
328 746
        return new $cls();
329
    }
330
331 890
    private function initDefaultTasks(): void
332
    {
333 890
        $taskdefs = Phing::getResourcePath('etc/default.tasks.properties');
334
335
        try { // try to load taskdefs
336 890
            $props = new Properties();
337 890
            $in = new File((string) $taskdefs);
338
339 890
            if (null === $in) {
340
                throw new BuildException("Can't load default task list");
341
            }
342 890
            $props->load($in);
343
344 890
            $enum = $props->propertyNames();
345 890
            foreach ($enum as $key) {
346 890
                $value = $props->getProperty($key);
347 890
                $this->addTaskDefinition($key, $value);
348
            }
349
        } catch (IOException $ioe) {
350
            throw new BuildException("Can't load default task list");
351
        }
352
    }
353
354 890
    private function initDefaultDataTypes(): void
355
    {
356 890
        $typedefs = Phing::getResourcePath('etc/default.types.properties');
357
358
        try { // try to load typedefs
359 890
            $props = new Properties();
360 890
            $in = new File((string) $typedefs);
361 890
            if (null === $in) {
362
                throw new BuildException("Can't load default datatype list");
363
            }
364 890
            $props->load($in);
365
366 890
            $enum = $props->propertyNames();
367 890
            foreach ($enum as $key) {
368 890
                $value = $props->getProperty($key);
369 890
                $this->addDataTypeDefinition($key, $value);
370
            }
371
        } catch (IOException $ioe) {
372
            throw new BuildException("Can't load default datatype list");
373
        }
374
    }
375
376 890
    private function initCustomTasks(): void
377
    {
378 890
        $taskdefs = Phing::getResourcePath('custom.task.properties');
379
380
        try { // try to load typedefs
381 890
            $props = new Properties();
382 890
            $in = new File((string) $taskdefs);
383 890
            if (!$in->exists()) {
384
                return;
385
            }
386 890
            $props->load($in);
387 890
            $enum = $props->propertyNames();
388 890
            foreach ($enum as $key) {
389
                $value = $props->getProperty($key);
390
                $this->addTaskDefinition($key, $value);
391
            }
392
        } catch (IOException $ioe) {
393
            throw new BuildException("Can't load custom task list");
394
        }
395
    }
396
397 890
    private function initCustomDataTypes(): void
398
    {
399 890
        $typedefs = Phing::getResourcePath('custom.type.properties');
400
401
        try { // try to load typedefs
402 890
            $props = new Properties();
403 890
            $in = new File((string) $typedefs);
404 890
            if (!$in->exists()) {
405
                return;
406
            }
407 890
            $props->load($in);
408 890
            $enum = $props->propertyNames();
409 890
            foreach ($enum as $key) {
410
                $value = $props->getProperty($key);
411
                $this->addDataTypeDefinition($key, $value);
412
            }
413
        } catch (IOException $ioe) {
414
            throw new BuildException("Can't load custom type list");
415
        }
416
    }
417
}
418