Completed
Branch master (a6414e)
by Shcherbak
02:17
created

QuerySequence::possible()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
3
  namespace Funivan\PhpTokenizer\QuerySequence;
4
5
  use Funivan\PhpTokenizer\Collection;
6
  use Funivan\PhpTokenizer\Exception\InvalidArgumentException;
7
  use Funivan\PhpTokenizer\Strategy\Move;
8
  use Funivan\PhpTokenizer\Strategy\Possible;
9
  use Funivan\PhpTokenizer\Strategy\QueryStrategy;
10
  use Funivan\PhpTokenizer\Strategy\Search;
11
  use Funivan\PhpTokenizer\Strategy\StrategyInterface;
12
  use Funivan\PhpTokenizer\Strategy\Strict;
13
  use Funivan\PhpTokenizer\Token;
14
15
  /**
16
   * Start from specific position and check token from this position according to strategies
17
   */
18
  class QuerySequence implements QuerySequenceInterface {
19
20
    /**
21
     * @var bool
22
     */
23
    private $valid = true;
24
25
    /**
26
     * @var int
27
     */
28
    private $position = 0;
29
30
    /**
31
     * @var Collection
32
     */
33
    private $collection;
34
35
    /**
36
     * @var
37
     */
38
    private $skipWhitespaces = false;
39
40
41
    /**
42
     * @inheritdoc
43
     */
44 232
    public function __construct(Collection $collection, $initialPosition = 0) {
45 232
      $this->collection = $collection;
46 232
      $this->position = $initialPosition;
47 202
    }
48
49
50
    /**
51
     * @return Collection
52
     */
53 38
    public function getCollection() {
54 38
      return $this->collection;
55
    }
56
57
58
    /**
59
     * @param int $position
60
     * @return QuerySequence
61
     */
62 181
    public function setPosition($position) {
63 181
      $this->position = $position;
64 181
      return $this;
65
    }
66
67
68
    /**
69
     * @return int
70
     */
71 189
    public function getPosition() {
72 189
      return $this->position;
73
    }
74
75
76
    /**
77
     * Strict validation of condition
78
     *
79
     * @param int|string|Strict $condition
80
     * @return Token
81
     */
82 132
    public function strict($condition) {
83 132
      $query = $this->buildStrategyCondition($condition, Strict::create());
84 126
      return $this->process($query);
85
    }
86
87
88
    /**
89
     * Check if token possible valid for our condition
90
     *
91
     * @param int|string|Possible $condition
92
     * @return Token
93
     */
94 92
    public function possible($condition) {
95 92
      $query = $this->buildStrategyCondition($condition, Possible::create());
96 84
      return $this->process($query);
97
    }
98
99
100
    /**
101
     * @param string $start
102
     * @param string $end
103
     * @return Collection
104
     */
105 80
    public function section($start, $end) {
106
107 80
      $token = $this->strict($start);
108 80
      if (!$token->isValid()) {
109
        # cant find start position
110 80
        return new Collection();
111
      }
112
113 74
      $this->moveToToken($token);
114
115 74
      $section = new \Funivan\PhpTokenizer\Strategy\Section();
116 74
      $section->setDelimiters($start, $end);
117 74
      $lastToken = $this->process($section);
118 74
      if (!$lastToken->isValid()) {
119
        return new Collection();
120
      }
121
122 74
      return $this->collection->extractByTokens($token, $lastToken);
123
    }
124
125
126
    /**
127
     * By default we search forward
128
     *
129
     * @param int|string|Search $condition
130
     * @param null $direction
131
     * @return Token
132
     */
133 48
    public function search($condition, $direction = null) {
134 48
      $strategy = Search::create();
135 48
      if ($direction !== null) {
136 2
        $strategy->setDirection($direction);
137 1
      }
138 48
      $query = $this->buildStrategyCondition($condition, $strategy);
139 48
      return $this->process($query);
140
    }
141
142
143
    /**
144
     * Relative move
145
     * +10 move forward 10 tokens
146
     * -5 move backward 5 tokens
147
     *
148
     * @param int $steps
149
     * @return Token
150
     */
151 2
    public function move($steps) {
152 2
      return $this->process(Move::create($steps));
153
    }
154
155
156
    /**
157
     * Move to specific position
158
     *
159
     * @param Token $token
160
     * @return Token|null
161
     */
162 91
    public function moveToToken(Token $token) {
163
164 80
      if (!$token->isValid()) {
165 38
        $this->setValid(false);
166 38
        return new Token();
167
      }
168
169 78
      $tokenIndex = $token->getIndex();
170
171
172 89
      foreach ($this->collection as $index => $collectionToken) {
173 78
        if ($collectionToken->getIndex() == $tokenIndex) {
174 78
          $this->setPosition($index);
175 89
          return $collectionToken;
176
        }
177 39
      }
178
179 2
      $this->setValid(false);
180 2
      return new Token();
181 10
    }
182
183
184
    /**
185
     * Array may contain Int, String or any StrategyInterface object
186
     *
187
     * @param array $conditions
188
     * @return Collection
189
     */
190 50
    public function sequence(array $conditions) {
191 50
      $range = new Collection();
192 50
      foreach ($conditions as $value) {
193 50
        $range[] = $this->checkFromSequence($value);
194 25
      }
195
196 50
      return $range;
197
    }
198
199
200
    /**
201
     * @param string|int|StrategyInterface $value
202
     * @return Token
203
     */
204 50
    private function checkFromSequence($value) {
205 50
      if ($value instanceof StrategyInterface) {
206 34
        $query = $value;
207 17
      } else {
208 30
        $query = $this->buildStrategyCondition($value, Strict::create());
209
      }
210
211 50
      $token = $this->process($query);
212 50
      return $token;
213
    }
214
215
216
    /**
217
     * @inheritdoc
218
     */
219 189
    public function process(StrategyInterface $strategy) {
220
221 174
      if ($this->isValid() === false) {
222 138
        return new Token();
223
      }
224
225 174
      $result = $strategy->process($this->collection, $this->getPosition());
226
227 174
      if ($result->isValid() === false) {
228 154
        $this->setValid(false);
229 154
        return new Token();
230
      }
231
232 164
      $position = $result->getNexTokenIndex();
233 164
      $this->setPosition($position);
234
235 164
      $token = $result->getToken();
236 176
      if ($token === null) {
237 102
        $token = new Token();
238 51
      }
239
240 176
      if ($this->skipWhitespaces and isset($this->collection[$position]) and $this->collection[$position]->getType() === T_WHITESPACE) {
241
        # skip whitespaces in next check
242 26
        $this->setPosition(($position + 1));
243 13
      }
244
245 176
      return $token;
246 15
    }
247
248
249
    /**
250
     *
251
     * @param StrategyInterface|string|int $value
252
     * @param QueryStrategy $defaultStrategy
253
     * @return QueryStrategy
254
     */
255 186
    private function buildStrategyCondition($value, QueryStrategy $defaultStrategy) {
256
257 164
      if (is_object($value) and get_class($value) == get_class($defaultStrategy)) {
258 18
        return $value;
259
      }
260
261 174
      $query = $defaultStrategy;
262
263 156
      if (is_string($value) or $value === null) {
264 134
        $query->valueIs($value);
265 141
        return $query;
266
      }
267
268 118
      if (is_int($value)) {
269 104
        $query->typeIs($value);
270 108
        return $query;
271
      }
272
273
274 14
      throw new InvalidArgumentException("Invalid token condition. Expect string or int or StrategyInterface");
275 22
    }
276
277
278
    /**
279
     * @param boolean $valid
280
     * @return $this
281
     */
282 173
    public function setValid($valid) {
283 160
      if (!is_bool($valid)) {
284 2
        throw new InvalidArgumentException("Invalid flag. Expect boolean. Given:" . gettype($valid));
285
      }
286 171
      $this->valid = $valid;
287 171
      return $this;
288
    }
289
290
291
    /**
292
     * @return Token
293
     */
294
    public function getToken() {
295
      $position = $this->getPosition();
296
      $token = $this->getCollection()->offsetGet($position);
297
298
      if ($token !== null) {
299
        return $token;
300
      }
301
302
      return new Token();
303
    }
304
305
306
    /**
307
     * Indicate state of all conditions
308
     *
309
     * @return bool
310
     */
311 180
    public function isValid() {
312 180
      return ($this->valid === true);
313
    }
314
315
316
    /**
317
     * @param boolean $skipWhitespaces
318
     * @return $this
319
     */
320 29
    public function setSkipWhitespaces($skipWhitespaces) {
321 29
      $this->skipWhitespaces = $skipWhitespaces;
322 29
      return $this;
323
    }
324
325
  }