Completed
Branch 0.8-dev (af4386)
by Kacper
03:08
created

Token::process()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 30
ccs 21
cts 21
cp 1
rs 8.439
cc 6
eloc 18
nc 8
nop 4
crap 6
1
<?php
2
/**
3
 * Highlighter
4
 *
5
 * Copyright (C) 2016, Some right reserved.
6
 *
7
 * @author Kacper "Kadet" Donat <[email protected]>
8
 *
9
 * Contact with author:
10
 * Xmpp: [email protected]
11
 * E-mail: [email protected]
12
 *
13
 * From Kadet with love.
14
 */
15
16
namespace Kadet\Highlighter\Parser\Token;
17
18
use Kadet\Highlighter\Language\Language;
19
use Kadet\Highlighter\Parser\Result;
20
use Kadet\Highlighter\Parser\Rule;
21
use Kadet\Highlighter\Parser\TokenIterator;
22
use Kadet\Highlighter\Parser\Validator\Validator;
23
use Kadet\Highlighter\Utils\ArrayHelper;
24
use Kadet\Highlighter\Utils\Helper;
25
26
class Token
27
{
28
    const NAME  = null;
29
    const START = 0x1;
30
    const END   = 0x2;
31
32
    protected static $_id = 0;
33
34
    public $pos;
35
    public $name;
36
    public $closedBy;
37
    public $index = 1;
38
    public $id;
39
    public $rule;
40
41
    # region >>> cache
42
    /**
43
     * @var static|null|false
44
     */
45
    protected $_end;
46
47
    /**
48
     * @var static|null|false
49
     */
50
    protected $_start;
51
52
    protected $_valid;
53
    protected $_length;
54
    # endregion
55
56
    /**
57
     * Token constructor.
58
     *
59
     * @param array $options
60
     */
61 52
    public function __construct(array $options)
62
    {
63 52
        if (isset($options[0])) {
64 39
            $this->name     = $options[0];
65 39
            $this->closedBy = $this->name;
66 39
        }
67
68 52
        if (isset($options['pos'])) {
69 49
            $this->pos = $options['pos'];
70 49
        }
71
72 52
        if (isset($options['index'])) {
73 6
            $this->index = $options['index'];
74 6
        }
75
76 52
        if (isset($options['start'])) {
77 2
            $this->setStart($options['start']);
78 2
        }
79
80 52
        $this->rule = isset($options['rule']) ? $options['rule'] : new Rule();
81
82 52
        if (isset($options['end'])) {
83 42
            $this->setEnd($options['end']);
84 42
        }
85
86 52
        if (isset($options['closed-by'])) {
87
            $this->closedBy = $options['closed-by'];
88
        }
89
90 52
        $this->id = ++self::$_id;
91 52
    }
92
93 35
    public function isStart()
94
    {
95 35
        return $this->_start === null;
96
    }
97
98 33
    public function isEnd()
99
    {
100 33
        return $this->_end === null;
101
    }
102
103 16
    public function isValid(Language $language, $context = null)
104
    {
105 16
        if ($this->_valid === null) {
106 15
            $this->validate($language, $context);
107 15
        }
108
109 16
        return $this->_valid;
110
    }
111
112 11
    protected function validate(Language $language, $context)
113
    {
114 11
        $this->setValid(
115 11
            $language === $this->rule->language &&
116 11
            $this->rule->validator->validate($context, $this->isEnd() ? [$this->name => Validator::CONTEXT_IN] : [])
117 11
        );
118 11
    }
119
120 15
    public function setValid($valid = true)
121
    {
122 15
        $this->_valid = $valid;
123
124 15
        if ($this->_end) {
125 12
            $this->_end->_valid = $this->_valid;
126 15
        } elseif ($this->_start) {
127 12
            $this->_start->_valid = $this->_valid;
128 12
        }
129 15
    }
130
131
    /**
132
     * @return Token|null|false
133
     */
134 29
    public function getStart()
135
    {
136 29
        return $this->_start;
137
    }
138
139
    /**
140
     * @param Token|null|false $start
141
     */
142 13 View Code Duplication
    public function setStart($start = null)
143
    {
144 13
        $this->_end   = null;
145 13
        $this->_start = $start;
146
147 13
        if ($start instanceof Token) {
148 12
            $this->_start->_length = null;
149 12
            $start->_end = $this;
150 12
        }
151 13
    }
152
153
    /**
154
     * @return Token|null|false
155
     */
156 40
    public function getEnd()
157
    {
158 40
        return $this->_end;
159
    }
160
161
    /**
162
     * @param Token|null|false $end
163
     */
164 42 View Code Duplication
    public function setEnd($end = null)
165
    {
166 42
        $this->_start  = null;
167 42
        $this->_end    = $end;
168 42
        $this->_length = null;
169
170 42
        if ($end instanceof Token) {
171 42
            $end->_start = $this;
172 42
        }
173 42
    }
174
175 2
    public function getLength()
176
    {
177 2
        if ($this->_length === null) {
178 2
            $this->_length = !$this->_end ? 0 : $this->_end->pos - $this->pos;
179 2
        }
180
181 2
        return $this->_length;
182
    }
183
184 11
    public function __get($name)
185
    {
186 11
        return $this->rule->$name;
187
    }
188
189
    /**
190
     * @param array         $context
191
     * @param Language      $language
192
     * @param Result        $result
193
     * @param TokenIterator $tokens
194
     *
195
     * todo: Documentation
196
     *
197
     * @return bool Continue?
198
     */
199 10
    public function process(array &$context, Language $language, Result $result, TokenIterator $tokens) {
200 10
        if(!$this->isValid($language, $context)) {
201 3
            return true;
202
        }
203
204 10
        if($this->isStart()) {
205 10
            $result->append($this);
206 10
            $context[$tokens->key()] = $this->name;
207 10
        } else {
208 9
            if($this->_start) {
209 8
                unset($context[spl_object_hash($this->_start)]);
210 8
            } else {
211 1
                $start = ArrayHelper::find(array_reverse($context), function ($k, $v) {
212 1
                    return $v === $this->closedBy;
213 1
                });
214
215 1
                if ($start !== false) {
216 1
                    $this->setStart($tokens[$start]);
217
218 1
                    unset($context[$start]);
219 1
                }
220
            }
221
222 9
            if (!$this->_start instanceof MetaToken) {
223 9
                $result->append($this);
224 9
            }
225
        }
226
227 10
        return true;
228
    }
229
230 13
    public static function compare(Token $a, Token $b)
231
    {
232 13
        $multiplier = $a->isEnd() ? -1 : 1;
233
234 13
        if (($a->isStart() && $b->isEnd()) || ($a->isEnd() && $b->isStart())) {
235 1
            if ($a->getStart() == $b) {
236 1
                return 1;
237 1
            } elseif ($a->getEnd() == $b) {
238
                return -1;
239
            } else {
240 1
                return $multiplier;
241
            }
242 12
        } elseif (($rule = Helper::cmp($b->rule->priority, $a->rule->priority)) !== 0) {
243 10
            return $multiplier*$rule;
244 5
        } elseif (($rule = Helper::cmp($b->index, $a->index)) !== 0) {
245 1
            return $multiplier*$rule;
246
        } else {
247 4
            return $multiplier*($a->id < $b->id ? -1 : 1);
248
        }
249
    }
250
}
251