Completed
Push — master ( 1bff1a...f9631c )
by Shcherbak
02:38
created

QuerySequence   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 307
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 93.33%

Importance

Changes 8
Bugs 4 Features 2
Metric Value
wmc 39
c 8
b 4
f 2
lcom 1
cbo 11
dl 0
loc 307
ccs 98
cts 105
cp 0.9333
rs 8.2857

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getCollection() 0 3 1
A setPosition() 0 4 1
A getPosition() 0 3 1
A strict() 0 4 1
A possible() 0 4 1
A move() 0 3 1
A moveToToken() 0 20 4
A section() 0 19 3
A search() 0 8 2
A sequence() 0 8 2
C process() 0 28 7
B buildStrategyCondition() 0 21 6
A setValid() 0 7 2
A getToken() 0 10 2
A isValid() 0 3 1
A setSkipWhitespaces() 0 4 1
A checkFromSequence() 0 9 2
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 363
    public function __construct(Collection $collection, $initialPosition = 0) {
45 363
      $this->collection = $collection;
46 363
      $this->position = $initialPosition;
47 363
    }
48
49
50
    /**
51
     * @return Collection
52
     */
53 63
    public function getCollection() {
54 63
      return $this->collection;
55
    }
56
57
58
    /**
59
     * @param int $position
60
     * @return QuerySequence
61
     */
62 315
    public function setPosition($position) {
63 315
      $this->position = $position;
64 315
      return $this;
65
    }
66
67
68
    /**
69
     * @return int
70
     */
71 324
    public function getPosition() {
72 324
      return $this->position;
73
    }
74
75
76
    /**
77
     * Strict validation of condition
78
     *
79
     * @param int|string|Strict $condition
80
     * @return Token
81
     */
82 261
    public function strict($condition) {
83 261
      $query = $this->buildStrategyCondition($condition, Strict::create());
84 252
      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 153
    public function possible($condition) {
95 153
      $query = $this->buildStrategyCondition($condition, Possible::create());
96 141
      return $this->process($query);
97
    }
98
99
100
    /**
101
     * @param string $start
102
     * @param string $end
103
     * @return Collection
104
     */
105 180
    public function section($start, $end) {
106
107 180
      $token = $this->strict($start);
108 180
      if (!$token->isValid()) {
109
        # cant find start position
110 180
        return new Collection();
111
      }
112
113 171
      $this->moveToToken($token);
114
115 171
      $section = new \Funivan\PhpTokenizer\Strategy\Section();
116 171
      $section->setDelimiters($start, $end);
117 171
      $lastToken = $this->process($section);
118 171
      if (!$lastToken->isValid()) {
119
        return new Collection();
120
      }
121
122 171
      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 75
    public function search($condition, $direction = null) {
134 75
      $strategy = Search::create();
135 75
      if ($direction !== null) {
136 6
        $strategy->setDirection($direction);
137 4
      }
138 75
      $query = $this->buildStrategyCondition($condition, $strategy);
139 75
      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 15
    public function move($steps) {
152 15
      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 180
    public function moveToToken(Token $token) {
163
164 180
      if (!$token->isValid()) {
165 57
        $this->setValid(false);
166 57
        return new Token();
167
      }
168
169 177
      $tokenIndex = $token->getIndex();
170
171
172 177
      foreach ($this->collection as $index => $collectionToken) {
173 177
        if ($collectionToken->getIndex() === $tokenIndex) {
174 177
          $this->setPosition($index);
175 177
          return $collectionToken;
176
        }
177 118
      }
178
179 3
      $this->setValid(false);
180 3
      return new Token();
181
    }
182
183
184
    /**
185
     * Array may contain Int, String or any StrategyInterface object
186
     *
187
     * @param array $conditions
188
     * @return Collection
189
     */
190 75
    public function sequence(array $conditions) {
191 75
      $range = new Collection();
192 75
      foreach ($conditions as $value) {
193 75
        $range[] = $this->checkFromSequence($value);
194 50
      }
195
196 75
      return $range;
197
    }
198
199
200
    /**
201
     * @param string|int|StrategyInterface $value
202
     * @return Token
203
     */
204 75
    private function checkFromSequence($value) {
205 75
      if ($value instanceof StrategyInterface) {
206 51
        $query = $value;
207 34
      } else {
208 45
        $query = $this->buildStrategyCondition($value, Strict::create());
209
      }
210
211 75
      return $this->process($query);
212
    }
213
214
215
    /**
216
     * @inheritdoc
217
     */
218 324
    public function process(StrategyInterface $strategy) {
219
220 324
      if ($this->isValid() === false) {
221 225
        return new Token();
222
      }
223
224 324
      $result = $strategy->process($this->collection, $this->getPosition());
225
226 324
      if ($result->isValid() === false) {
227 294
        $this->setValid(false);
228 294
        return new Token();
229
      }
230
231 309
      $position = $result->getNexTokenIndex();
232 309
      $this->setPosition($position);
233
234 309
      $token = $result->getToken();
235 309
      if ($token === null) {
236 168
        $token = new Token();
237 112
      }
238
239 309
      if ($this->skipWhitespaces and isset($this->collection[$position]) and $this->collection[$position]->getType() === T_WHITESPACE) {
240
        # skip whitespaces in next check
241 39
        $this->setPosition($position + 1);
242 26
      }
243
244 309
      return $token;
245
    }
246
247
248
    /**
249
     *
250
     * @param StrategyInterface|string|int $value
251
     * @param QueryStrategy $defaultStrategy
252
     * @return QueryStrategy
253
     */
254 309
    private function buildStrategyCondition($value, QueryStrategy $defaultStrategy) {
255
256 309
      if (is_object($value) and get_class($value) === get_class($defaultStrategy)) {
257 21
        return $value;
258
      }
259
260 297
      $query = $defaultStrategy;
261
262 297
      if (is_string($value) or $value === null) {
263 264
        $query->valueIs($value);
264 264
        return $query;
265
      }
266
267 192
      if (is_int($value)) {
268 171
        $query->typeIs($value);
269 171
        return $query;
270
      }
271
272
273 21
      throw new InvalidArgumentException('Invalid token condition. Expect string or int or StrategyInterface');
274
    }
275
276
277
    /**
278
     * @param boolean $valid
279
     * @return $this
280
     */
281 303
    public function setValid($valid) {
282 303
      if (!is_bool($valid)) {
283 3
        throw new InvalidArgumentException('Invalid flag. Expect boolean. Given:' . gettype($valid));
284
      }
285 300
      $this->valid = $valid;
286 300
      return $this;
287
    }
288
289
290
    /**
291
     * @return Token
292
     */
293
    public function getToken() {
294
      $position = $this->getPosition();
295
      $token = $this->getCollection()->offsetGet($position);
296
297
      if ($token !== null) {
298
        return $token;
299
      }
300
301
      return new Token();
302
    }
303
304
305
    /**
306
     * Indicate state of all conditions
307
     *
308
     * @return bool
309
     */
310 330
    public function isValid() {
311 330
      return ($this->valid === true);
312
    }
313
314
315
    /**
316
     * @param boolean $skipWhitespaces
317
     * @return $this
318
     */
319 42
    public function setSkipWhitespaces($skipWhitespaces) {
320 42
      $this->skipWhitespaces = $skipWhitespaces;
321 42
      return $this;
322
    }
323
324
  }