Failed Conditions
Push — pr-3115 ( f9aa34 )
by Andreas
05:23 queued 02:46
created

Lists::writeCall()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace dokuwiki\Parsing\Handler;
4
5
class Lists implements ReWriterInterface
6
{
7
8
    /** @var CallWriterInterface original call writer */
9
    protected $callWriter;
10
11
    protected $calls = array();
12
    protected $listCalls = array();
13
    protected $listStack = array();
14
15
    protected $initialDepth = 0;
16
17
    const NODE = 1;
18
19
20
    /** @inheritdoc */
21
    public function __construct(CallWriterInterface $CallWriter)
22
    {
23
        $this->callWriter = $CallWriter;
24
    }
25
26
    /** @inheritdoc */
27
    public function writeCall($call)
28
    {
29
        $this->calls[] = $call;
30
    }
31
32
    /**
33
     * @inheritdoc
34
     * Probably not needed but just in case...
35
     */
36
    public function writeCalls($calls)
37
    {
38
        $this->calls = array_merge($this->calls, $calls);
39
    }
40
41
    /** @inheritdoc */
42
    public function finalise()
43
    {
44
        $last_call = end($this->calls);
45
        $this->writeCall(array('list_close',array(), $last_call[2]));
46
47
        $this->process();
48
        $this->callWriter->finalise();
49
        unset($this->callWriter);
50
    }
51
52
    /** @inheritdoc */
53
    public function process()
54
    {
55
56
        foreach ($this->calls as $call) {
57
            switch ($call[0]) {
58
                case 'list_item':
59
                    $this->listOpen($call);
60
                    break;
61
                case 'list_open':
62
                    $this->listStart($call);
63
                    break;
64
                case 'list_close':
65
                    $this->listEnd($call);
66
                    break;
67
                default:
68
                    $this->listContent($call);
69
                    break;
70
            }
71
        }
72
73
        $this->callWriter->writeCalls($this->listCalls);
74
        return $this->callWriter;
75
    }
76
77
    protected function listStart($call)
78
    {
79
        $depth = $this->interpretSyntax($call[1][0], $listType);
80
81
        $this->initialDepth = $depth;
82
        //                   array(list type, current depth, index of current listitem_open)
83
        $this->listStack[] = array($listType, $depth, 1);
84
85
        $this->listCalls[] = array('list'.$listType.'_open',array(),$call[2]);
86
        $this->listCalls[] = array('listitem_open',array(1),$call[2]);
87
        $this->listCalls[] = array('listcontent_open',array(),$call[2]);
88
    }
89
90
91
    protected function listEnd($call)
92
    {
93
        $closeContent = true;
94
95
        while ($list = array_pop($this->listStack)) {
96
            if ($closeContent) {
97
                $this->listCalls[] = array('listcontent_close',array(),$call[2]);
98
                $closeContent = false;
99
            }
100
            $this->listCalls[] = array('listitem_close',array(),$call[2]);
101
            $this->listCalls[] = array('list'.$list[0].'_close', array(), $call[2]);
102
        }
103
    }
104
105
    protected function listOpen($call)
106
    {
107
        $depth = $this->interpretSyntax($call[1][0], $listType);
108
        $end = end($this->listStack);
109
        $key = key($this->listStack);
110
111
        // Not allowed to be shallower than initialDepth
112
        if ($depth < $this->initialDepth) {
113
            $depth = $this->initialDepth;
114
        }
115
116
        if ($depth == $end[1]) {
117
            // Just another item in the list...
118
            if ($listType == $end[0]) {
119
                $this->listCalls[] = array('listcontent_close',array(),$call[2]);
120
                $this->listCalls[] = array('listitem_close',array(),$call[2]);
121
                $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]);
122
                $this->listCalls[] = array('listcontent_open',array(),$call[2]);
123
124
                // new list item, update list stack's index into current listitem_open
125
                $this->listStack[$key][2] = count($this->listCalls) - 2;
126
127
                // Switched list type...
128
            } else {
129
                $this->listCalls[] = array('listcontent_close',array(),$call[2]);
130
                $this->listCalls[] = array('listitem_close',array(),$call[2]);
131
                $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]);
132
                $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
133
                $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
134
                $this->listCalls[] = array('listcontent_open',array(),$call[2]);
135
136
                array_pop($this->listStack);
137
                $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
138
            }
139
        } elseif ($depth > $end[1]) { // Getting deeper...
140
            $this->listCalls[] = array('listcontent_close',array(),$call[2]);
141
            $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
142
            $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
143
            $this->listCalls[] = array('listcontent_open',array(),$call[2]);
144
145
            // set the node/leaf state of this item's parent listitem_open to NODE
146
            $this->listCalls[$this->listStack[$key][2]][1][1] = self::NODE;
147
148
            $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
149
        } else { // Getting shallower ( $depth < $end[1] )
150
            $this->listCalls[] = array('listcontent_close',array(),$call[2]);
151
            $this->listCalls[] = array('listitem_close',array(),$call[2]);
152
            $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]);
153
154
            // Throw away the end - done
155
            array_pop($this->listStack);
156
157
            while (1) {
158
                $end = end($this->listStack);
159
                $key = key($this->listStack);
160
161
                if ($end[1] <= $depth) {
162
                    // Normalize depths
163
                    $depth = $end[1];
164
165
                    $this->listCalls[] = array('listitem_close',array(),$call[2]);
166
167
                    if ($end[0] == $listType) {
168
                        $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]);
169
                        $this->listCalls[] = array('listcontent_open',array(),$call[2]);
170
171
                        // new list item, update list stack's index into current listitem_open
172
                        $this->listStack[$key][2] = count($this->listCalls) - 2;
173
                    } else {
174
                        // Switching list type...
175
                        $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]);
176
                        $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
177
                        $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
178
                        $this->listCalls[] = array('listcontent_open',array(),$call[2]);
179
180
                        array_pop($this->listStack);
181
                        $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
182
                    }
183
184
                    break;
185
186
                    // Haven't dropped down far enough yet.... ( $end[1] > $depth )
187
                } else {
188
                    $this->listCalls[] = array('listitem_close',array(),$call[2]);
189
                    $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]);
190
191
                    array_pop($this->listStack);
192
                }
193
            }
194
        }
195
    }
196
197
    protected function listContent($call)
198
    {
199
        $this->listCalls[] = $call;
200
    }
201
202
    protected function interpretSyntax($match, & $type)
203
    {
204
        if (substr($match, -1) == '*') {
205
            $type = 'u';
206
        } else {
207
            $type = 'o';
208
        }
209
        // Is the +1 needed? It used to be count(explode(...))
210
        // but I don't think the number is seen outside this handler
211
        return substr_count(str_replace("\t", '  ', $match), '  ') + 1;
212
    }
213
}
214