Completed
Push — master ( 9d5aa6...d200fe )
by Siad
12:49
created

ProjectConfigurator   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 341
Duplicated Lines 0 %

Test Coverage

Coverage 83.33%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 99
c 1
b 0
f 0
dl 0
loc 341
ccs 95
cts 114
cp 0.8333
rs 9.6
wmc 35

17 Methods

Rating   Name   Duplication   Size   Complexity  
A addText() 0 8 3
A isIgnoringProjectTag() 0 3 1
A getBuildFile() 0 3 1
A getBuildFileParent() 0 3 1
A setIgnoreProjectTag() 0 3 1
A delayTaskUntilParseEnd() 0 3 1
A getCurrentProjectName() 0 3 1
A __construct() 0 6 1
A isParsing() 0 3 1
B configure() 0 32 8
A storeChild() 0 4 1
A _parse() 0 19 1
A configureId() 0 4 3
A configureProject() 0 3 1
A setCurrentProjectName() 0 3 1
A addLocationToBuildException() 0 21 5
A parse() 0 40 4
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
 * The datatype handler class.
22
 *
23
 * This class handles the occurrence of registered datatype tags like
24
 * FileSet
25
 *
26
 * @author    Andreas Aderhold <[email protected]>
27
 * @copyright 2001,2002 THYRELL. All rights reserved
28
 * @package   phing.parser
29
 */
30
class ProjectConfigurator
31
{
32
    public const PARSING_CONTEXT_REFERENCE = "phing.parsing.context";
33
34
    /**
35
     * @var Project $project
36
     */
37
    public $project;
38
    public $locator;
39
40
    public $buildFile;
41
    public $buildFileParent;
42
43
    /**
44
     * Synthetic target that will be called at the end to the parse phase
45
     */
46
    private $parseEndTarget;
47
48
    /**
49
     * Name of the current project
50
     */
51
    private $currentProjectName;
52
53
    private $isParsing = true;
54
55
    /**
56
     * Indicates whether the project tag attributes are to be ignored
57
     * when processing a particular build file.
58
     */
59
    private $ignoreProjectTag = false;
60
61
    /**
62
     * Static call to ProjectConfigurator. Use this to configure a
63
     * project. Do not use the new operator.
64
     *
65
     * @param Project $project the Project instance this configurator should use
66
     * @param PhingFile $buildFile the buildfile object the parser should use
67
     *
68
     * @throws \IOException
69
     * @throws \BuildException
70
     * @throws NullPointerException
71
     */
72 793
    public static function configureProject(Project $project, PhingFile $buildFile): void
73
    {
74 793
        (new self($project, $buildFile))->parse();
75 793
    }
76
77
    /**
78
     * Constructs a new ProjectConfigurator object
79
     * This constructor is private. Use a static call to
80
     * <code>configureProject</code> to configure a project.
81
     *
82
     * @param  Project $project the Project instance this configurator should use
83
     * @param  PhingFile $buildFile the buildfile object the parser should use
84
     * @throws IOException
85
     * @throws NullPointerException
86
     */
87 793
    private function __construct(Project $project, PhingFile $buildFile)
88
    {
89 793
        $this->project = $project;
90 793
        $this->buildFile = new PhingFile($buildFile->getAbsolutePath());
91 793
        $this->buildFileParent = new PhingFile($this->buildFile->getParent());
92 793
        $this->parseEndTarget = new Target();
93 793
    }
94
95
    /**
96
     * find out the build file
97
     *
98
     * @return PhingFile the build file to which the xml context belongs
99
     */
100 793
    public function getBuildFile()
101
    {
102 793
        return $this->buildFile;
103
    }
104
105
    /**
106
     * find out the parent build file of this build file
107
     *
108
     * @return PhingFile the parent build file of this build file
109
     */
110
    public function getBuildFileParent()
111
    {
112
        return $this->buildFileParent;
113
    }
114
115
    /**
116
     * find out the current project name
117
     *
118
     * @return string current project name
119
     */
120 92
    public function getCurrentProjectName()
121
    {
122 92
        return $this->currentProjectName;
123
    }
124
125
    /**
126
     * set the name of the current project
127
     *
128
     * @param string $name name of the current project
129
     */
130 793
    public function setCurrentProjectName($name)
131
    {
132 793
        $this->currentProjectName = $name;
133 793
    }
134
135
    /**
136
     * tells whether the project tag is being ignored
137
     *
138
     * @return bool whether the project tag is being ignored
139
     */
140 793
    public function isIgnoringProjectTag()
141
    {
142 793
        return $this->ignoreProjectTag;
143
    }
144
145
    /**
146
     * sets the flag to ignore the project tag
147
     *
148
     * @param bool $flag flag to ignore the project tag
149
     */
150 98
    public function setIgnoreProjectTag($flag)
151
    {
152 98
        $this->ignoreProjectTag = $flag;
153 98
    }
154
155
    /**
156
     * @return bool
157
     */
158
    public function isParsing()
159
    {
160
        return $this->isParsing;
161
    }
162
163
    /**
164
     * Creates the ExpatParser, sets root handler and kick off parsing
165
     * process.
166
     *
167
     * @throws BuildException if there is any kind of exception during
168
     *                        the parsing process
169
     */
170 793
    protected function parse()
171
    {
172
        try {
173
            // get parse context
174 793
            $ctx = $this->project->getReference(self::PARSING_CONTEXT_REFERENCE);
175 793
            if (null == $ctx) {
176
                // make a new context and register it with project
177 793
                $ctx = new PhingXMLContext($this->project);
178 793
                $this->project->addReference(self::PARSING_CONTEXT_REFERENCE, $ctx);
179
            }
180
181
            //record this parse with context
182 793
            $ctx->addImport($this->buildFile);
183
184 793
            if (count($ctx->getImportStack()) > 1) {
185 98
                $currentImplicit = $ctx->getImplicitTarget();
186 98
                $currentTargets = $ctx->getCurrentTargets();
187
188 98
                $newCurrent = new Target();
189 98
                $newCurrent->setProject($this->project);
190 98
                $newCurrent->setName('');
191 98
                $ctx->setCurrentTargets([]);
192 98
                $ctx->setImplicitTarget($newCurrent);
193
194
                // this is an imported file
195
                // modify project tag parse behavior
196 98
                $this->setIgnoreProjectTag(true);
197 98
                $this->_parse($ctx);
198 98
                $newCurrent->main();
199
200 98
                $ctx->setImplicitTarget($currentImplicit);
201 98
                $ctx->setCurrentTargets($currentTargets);
202
            } else {
203 793
                $ctx->setCurrentTargets([]);
204 793
                $this->_parse($ctx);
205 793
                $ctx->getImplicitTarget()->main();
206
            }
207 1
        } catch (Exception $exc) {
208
            //throw new BuildException("Error reading project file", $exc);
209 1
            throw $exc;
210
        }
211 793
    }
212
213
    /**
214
     * @param PhingXMLContext $ctx
215
     * @throws ExpatParseException
216
     */
217 793
    protected function _parse(PhingXMLContext $ctx)
218
    {
219
        // push action onto global stack
220 793
        $ctx->startConfigure($this);
221
222 793
        $reader = new BufferedReader(new FileReader($this->buildFile));
223 793
        $parser = new ExpatParser($reader);
224 793
        $parser->parserSetOption(XML_OPTION_CASE_FOLDING, 0);
225 793
        $parser->setHandler(new RootHandler($parser, $this, $ctx));
226 793
        $this->project->log("parsing buildfile " . $this->buildFile->getName(), Project::MSG_VERBOSE);
227 793
        $parser->parse();
228 793
        $reader->close();
229
230
        // mark parse phase as completed
231 793
        $this->isParsing = false;
232
        // execute delayed tasks
233 793
        $this->parseEndTarget->main();
234
        // pop this action from the global stack
235 793
        $ctx->endConfigure();
236 793
    }
237
238
    /**
239
     * Delay execution of a task until after the current parse phase has
240
     * completed.
241
     *
242
     * @param Task $task Task to execute after parse
243
     */
244
    public function delayTaskUntilParseEnd($task)
245
    {
246
        $this->parseEndTarget->addTask($task);
247
    }
248
249
    /**
250
     * Configures an element and resolves eventually given properties.
251
     *
252
     * @param  mixed $target element to configure
253
     * @param  array $attrs element's attributes
254
     * @param  Project $project project this element belongs to
255
     * @throws BuildException
256
     * @throws Exception
257
     */
258 645
    public static function configure($target, $attrs, Project $project)
259
    {
260 645
        if ($target instanceof TaskAdapter) {
261 225
            $target = $target->getProxy();
262
        }
263
264
        // if the target is an UnknownElement, this means that the tag had not been registered
265
        // when the enclosing element (task, target, etc.) was configured.  It is possible, however,
266
        // that the tag was registered (e.g. using <taskdef>) after the original configuration.
267
        // ... so, try to load it again:
268 645
        if ($target instanceof UnknownElement) {
269
            $tryTarget = $project->createTask($target->getTaskType());
270
            if ($tryTarget) {
0 ignored issues
show
introduced by
$tryTarget is of type Task, thus it always evaluated to true.
Loading history...
271
                $target = $tryTarget;
272
            }
273
        }
274
275 645
        $bean = get_class($target);
276 645
        $ih = IntrospectionHelper::getHelper($bean);
277
278 645
        foreach ($attrs as $key => $value) {
279 635
            if ($key == 'id') {
280 57
                continue;
281
                // throw new BuildException("Id must be set Extermnally");
282
            }
283 635
            $value = $project->replaceProperties($value);
284
            try { // try to set the attribute
285 635
                $ih->setAttribute($project, $target, strtolower($key), $value);
286 7
            } catch (BuildException $be) {
287
                // id attribute must be set externally
288 7
                if ($key !== "id") {
289 7
                    throw $be;
290
                }
291
            }
292
        }
293 644
    }
294
295
    /**
296
     * Configures the #CDATA of an element.
297
     *
298
     * @param Project $project the project this element belongs to
299
     * @param object  the element to configure
300
     * @param string $text the element's #CDATA
301
     */
302 438
    public static function addText($project, $target, $text = null)
303
    {
304 438
        if ($text === null || strlen(trim($text)) === 0) {
305 405
            return;
306
        }
307 60
        $ih = IntrospectionHelper::getHelper(get_class($target));
308 60
        $text = $project->replaceProperties($text);
309 60
        $ih->addText($project, $target, $text);
310 60
    }
311
312
    /**
313
     * Stores a configured child element into its parent object
314
     *
315
     * @param object  the project this element belongs to
316
     * @param object  the parent element
317
     * @param object  the child element
318
     * @param string  the XML tagname
319
     */
320
    public static function storeChild($project, $parent, $child, $tag)
321
    {
322
        $ih = IntrospectionHelper::getHelper(get_class($parent));
323
        $ih->storeElement($project, $parent, $child, $tag);
324
    }
325
326
    /**
327
     * Scan Attributes for the id attribute and maybe add a reference to
328
     * project.
329
     *
330
     * @param object $target the element's object
331
     * @param array $attr the element's attributes
332
     */
333 788
    public function configureId($target, $attr)
334
    {
335 788
        if (isset($attr['id']) && $attr['id'] !== null) {
336 84
            $this->project->addReference($attr['id'], $target);
337
        }
338 788
    }
339
340
    /**
341
     * Add location to build exception.
342
     *
343
     * @param  BuildException $ex the build exception, if the build exception
344
     *                                    does not include
345
     * @param  Location $newLocation the location of the calling task (may be null)
346
     * @return BuildException a new build exception based in the build exception with
347
     *         location set to newLocation. If the original exception
348
     *         did not have a location, just return the build exception
349
     */
350 2
    public static function addLocationToBuildException(BuildException $ex, Location $newLocation)
351
    {
352 2
        if ($ex->getLocation() === null || $ex->getMessage() === null) {
353
            return $ex;
354
        }
355 2
        $errorMessage = sprintf(
356 2
            "The following error occurred while executing this line:%s%s %s%s",
357 2
            PHP_EOL,
358 2
            $ex->getLocation(),
359 2
            $ex->getMessage(),
360 2
            PHP_EOL
361
        );
362 2
        if ($ex instanceof ExitStatusException) {
363
            $exitStatus = $ex->getCode();
364
            if ($newLocation === null) {
365
                return new ExitStatusException($errorMessage, $exitStatus);
366
            }
367
            return new ExitStatusException($errorMessage, $exitStatus, $newLocation);
368
        }
369
370 2
        return new BuildException($errorMessage, $ex, $newLocation);
371
    }
372
}
373