TokenConsumer::increaseLine()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * This file is part of PhpUnitGen.
5
 *
6
 * (c) 2017-2018 Paul Thébaud <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE.md
9
 * file that was distributed with this source code.
10
 */
11
12
namespace PhpUnitGen\Annotation;
13
14
use PhpUnitGen\Annotation\AnnotationInterface\AnnotationInterface;
15
use PhpUnitGen\Exception\AnnotationParseException;
16
17
/**
18
 * Class TokenConsumer.
19
 *
20
 * @author     Paul Thébaud <[email protected]>.
21
 * @copyright  2017-2018 Paul Thébaud <[email protected]>.
22
 * @license    https://opensource.org/licenses/MIT The MIT license.
23
 * @link       https://github.com/paul-thebaud/phpunit-generator
24
 * @since      Class available since Release 2.0.0.
25
 */
26
class TokenConsumer
27
{
28
    /**
29
     * @var AnnotationFactory $annotationFactory The annotation factory to use.
30
     */
31
    private $annotationFactory;
32
33
    /**
34
     * @var AnnotationInterface[] $parsedAnnotations The parsed annotation list.
35
     */
36
    private $parsedAnnotations;
37
38
    /**
39
     * @var int $currentLine The current line number.
40
     */
41
    private $currentLine;
42
43
    /**
44
     * @var AbstractAnnotation $currentAnnotation The current parsed annotation, null if none.
45
     */
46
    private $currentAnnotation;
47
48
    /**
49
     * @var string|null $currentAnnotationContent The current parsed annotation content, null if none.
50
     */
51
    private $currentAnnotationContent;
52
53
    /**
54
     * @var int|null $openedStringToken The opened string opening token identifier, null if no string opened.
55
     */
56
    private $openedStringToken;
57
58
    /**
59
     * @var bool $currentlyEscaping Tells if last parsed token was an escape token.
60
     */
61
    private $currentlyEscaping;
62
63
    /**
64
     * @var int $openedParenthesis The opened parenthesis number for an annotation content.
65
     */
66
    private $openedParenthesis;
67
68
    /**
69
     * TokenConsumer constructor.
70
     *
71
     * @param AnnotationFactory $annotationFactory The annotation factory to use.
72
     */
73
    public function __construct(AnnotationFactory $annotationFactory)
74
    {
75
        $this->annotationFactory = $annotationFactory;
76
        $this->parsedAnnotations = [];
77
    }
78
79
    /**
80
     * Initialize the documentation parsing process.
81
     */
82
    public function initialize(): void
83
    {
84
        $this->parsedAnnotations = [];
85
        $this->currentLine       = 0;
86
        $this->reset();
87
    }
88
89
    /**
90
     * Reset an annotation parsing.
91
     */
92
    public function reset(): void
93
    {
94
        $this->currentAnnotation        = null;
95
        $this->currentAnnotationContent = null;
96
        $this->openedStringToken        = null;
97
        $this->currentlyEscaping        = false;
98
99
        $this->openedParenthesis = 0;
100
    }
101
102
    /**
103
     * Finalize the documentation parsing process.
104
     *
105
     * @throws AnnotationParseException If an annotation content is not closed.
106
     */
107
    public function finalize(): void
108
    {
109
        if ($this->currentAnnotation !== null) {
110
            if ($this->currentAnnotationContent === null) {
111
                $this->parsedAnnotations[] = $this->currentAnnotation;
112
            } else {
113
                throw new AnnotationParseException(
114
                    'An annotation content is not closed (you probably forget to close a parenthesis or a quote)'
115
                );
116
            }
117
        }
118
    }
119
120
    /**
121
     * Add a token content to the current annotation content.
122
     *
123
     * @param string $value The token value to add.
124
     */
125
    public function addTokenToContent(string $value)
126
    {
127
        if ($this->currentAnnotationContent !== null) {
128
            $this->currentAnnotationContent .= $value;
129
        }
130
    }
131
132
    /**
133
     * Consume an annotation token ("@PhpUnitGen" for example).
134
     *
135
     * @param string $value The annotation token value.
136
     *
137
     * @throws AnnotationParseException If the annotation is unknown.
138
     */
139
    public function consumeAnnotationToken(string $value): void
140
    {
141
        if ($this->currentAnnotation === null) {
142
            // We are not in an annotation, build a new one.
143
            $this->currentAnnotation = $this->annotationFactory
144
                ->invoke($value, $this->currentLine);
145
        } else {
146
            if ($this->currentAnnotationContent === null) {
147
                // It is an annotation without content, save it and create the new one.
148
                $this->parsedAnnotations[] = $this->currentAnnotation;
149
                $this->reset();
150
                $this->currentAnnotation = $this->annotationFactory
151
                    ->invoke($value, $this->currentLine);
152
            } else {
153
                // An annotation content parsing is not finished.
154
                throw new AnnotationParseException(
155
                    'An annotation content is not closed (you probably forget to close a parenthesis or a quote)'
156
                );
157
            }
158
        }
159
    }
160
161
    /**
162
     * Consume an opening parenthesis token.
163
     */
164
    public function consumeOpeningParenthesisToken(): void
165
    {
166
        if ($this->currentAnnotation !== null && $this->openedStringToken === null) {
167
            // We are in an annotation but not in a string, lets do something.
168
            if ($this->currentAnnotationContent === null) {
169
                if ($this->currentAnnotation->getLine() === $this->currentLine) {
170
                    // Begin content parsing only if it is on the same line.
171
                    $this->currentAnnotationContent = '';
172
                    $this->openedParenthesis++;
173
                }
174
            } else {
175
                $this->openedParenthesis++;
176
            }
177
        }
178
    }
179
180
    /**
181
     * Consume an closing parenthesis token.
182
     */
183
    public function consumeClosingParenthesisToken(): void
184
    {
185
        if ($this->currentAnnotationContent !== null && $this->openedStringToken === null) {
186
            // We are in an annotation content and not in a string.
187
            if ($this->openedParenthesis > 0) {
188
                $this->openedParenthesis--;
189
                if ($this->openedParenthesis === 0) {
190
                    // Annotation content is finished.
191
                    $this->currentAnnotation->setStringContent(substr(
192
                        $this->currentAnnotationContent,
193
                        1,
194
                        strlen($this->currentAnnotationContent) - 2
195
                    ));
196
                    $this->parsedAnnotations[] = $this->currentAnnotation;
197
                    $this->reset();
198
                }
199
            }
200
        }
201
    }
202
203
    /**
204
     * Consume a quote token (" or ').
205
     *
206
     * @param int $type The type of the quote token.
207
     */
208
    public function consumeQuoteToken(int $type): void
209
    {
210
        if ($this->currentAnnotationContent !== null) {
211
            if ($this->openedStringToken === null) {
212
                // It is a string opening.
213
                $this->openedStringToken = $type;
214
            } else {
215
                if (! $this->currentlyEscaping && $type === $this->openedStringToken) {
216
                    // We are in a string, the token is not escaped and is the same as the string opening token.
217
                    // Close the string.
218
                    $this->openedStringToken = null;
219
                }
220
            }
221
        }
222
    }
223
224
    /**
225
     * A method that is executed after consuming a token.
226
     *
227
     * @param int $type The token type (an integer from the Lexer class constant).
228
     */
229
    public function afterConsume(int $type): void
230
    {
231
        // Put in escaping mode if it were not escaping and there is a backslash token.
232
        if (! $this->currentlyEscaping && $type === AnnotationLexer::T_BACKSLASH) {
233
            $this->currentlyEscaping = true;
234
        } else {
235
            $this->currentlyEscaping = false;
236
        }
237
    }
238
239
    /**
240
     * @return bool True if a string is currently opened.
241
     */
242
    public function hasOpenedString(): bool
243
    {
244
        return $this->openedStringToken !== null;
245
    }
246
247
    /**
248
     * Increase the line number.
249
     */
250
    public function increaseLine(): void
251
    {
252
        $this->currentLine++;
253
    }
254
255
    /**
256
     * @return AnnotationInterface[] The parsed annotations.
257
     */
258
    public function getParsedAnnotations(): array
259
    {
260
        return $this->parsedAnnotations;
261
    }
262
}
263