Completed
Push — master ( 94bc98...2d9023 )
by Martin
09:18
created

TableParser   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 29
lcom 1
cbo 10
dl 0
loc 124
ccs 0
cts 90
cp 0
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
C parse() 0 63 10
B parseColumns() 0 17 10
B parseRow() 0 24 7
A parseCaption() 0 10 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This is part of the webuni/commonmark-table-extension package.
7
 *
8
 * (c) Martin Hasoň <[email protected]>
9
 * (c) Webuni s.r.o. <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Webuni\CommonMark\TableExtension;
16
17
use League\CommonMark\Block\Element\Paragraph;
18
use League\CommonMark\Block\Parser\AbstractBlockParser;
19
use League\CommonMark\ContextInterface;
20
use League\CommonMark\Cursor;
21
use League\CommonMark\Util\RegexHelper;
22
23
class TableParser extends AbstractBlockParser
24
{
25
    const REGEXP_DEFINITION = '/(?: *(:?) *-+ *(:?) *)+(?=\||$)/';
26
    const REGEXP_CELLS = '/(?:`[^`]*`|\\\\\||\\\\|[^|`\\\\]+)+(?=\||$)/';
27
    const REGEXP_CAPTION = '/^\[(.+?)\](?:\[(.+)\])?\s*$/';
28
29
    public function parse(ContextInterface $context, Cursor $cursor): bool
30
    {
31
        $container = $context->getContainer();
32
33
        if (!$container instanceof Paragraph) {
34
            return false;
35
        }
36
37
        $lines = $container->getStrings();
38
        if (count($lines) < 1) {
39
            return false;
40
        }
41
42
        $expressionOffset = $cursor->getNextNonSpacePosition();
43
44
        $match = RegexHelper::matchAll(self::REGEXP_DEFINITION, $cursor->getLine(), $expressionOffset);
45
        if (null === $match) {
46
            return false;
47
        }
48
49
        $columns = $this->parseColumns($match);
50
        $head = $this->parseRow(trim((string) array_pop($lines)), $columns, TableCell::TYPE_HEAD);
51
        if (null === $head) {
52
            return false;
53
        }
54
55
        $table = new Table(function (Cursor $cursor, Table $table) use ($columns): bool {
56
            $row = $this->parseRow($cursor->getLine(), $columns);
57
            if (null === $row) {
58
                if (null !== $table->getCaption()) {
59
                    return false;
60
                }
61
62
                if (null !== ($caption = $this->parseCaption($cursor->getLine()))) {
63
                    $table->setCaption($caption);
64
65
                    return true;
66
                }
67
68
                return false;
69
            }
70
71
            $table->getBody()->appendChild($row);
72
73
            return true;
74
        });
75
76
        $table->getHead()->appendChild($head);
77
78
        if (count($lines) >= 1) {
79
            $paragraph = new Paragraph();
80
            foreach ($lines as $line) {
81
                $paragraph->addLine($line);
82
            }
83
84
            $context->replaceContainerBlock($paragraph);
85
            $context->addBlock($table);
86
        } else {
87
            $context->replaceContainerBlock($table);
88
        }
89
90
        return true;
91
    }
92
93
    private function parseColumns(array $match): array
94
    {
95
        $columns = [];
96
        foreach ((array) $match[0] as $i => $column) {
97
            if (isset($match[1][$i]) && $match[1][$i] && isset($match[2][$i]) && $match[2][$i]) {
98
                $columns[] = TableCell::ALIGN_CENTER;
99
            } elseif (isset($match[1][$i]) && $match[1][$i]) {
100
                $columns[] = TableCell::ALIGN_LEFT;
101
            } elseif (isset($match[2][$i]) && $match[2][$i]) {
102
                $columns[] = TableCell::ALIGN_RIGHT;
103
            } else {
104
                $columns[] = '';
105
            }
106
        }
107
108
        return $columns;
109
    }
110
111
    private function parseRow(string $line, array $columns, string $type = TableCell::TYPE_BODY): ?TableRow
112
    {
113
        $cells = RegexHelper::matchAll(self::REGEXP_CELLS, $line);
114
115
        if (null === $cells || $line === $cells[0]) {
116
            return null;
117
        }
118
119
        $i = 0;
120
        $row = new TableRow();
121
        foreach ((array) $cells[0] as $i => $cell) {
122
            if (!isset($columns[$i])) {
123
                return $row;
124
            }
125
126
            $row->appendChild(new TableCell(trim($cell), $type, isset($columns[$i]) ? $columns[$i] : null));
127
        }
128
129
        for ($j = count($columns) - 1; $j > $i; --$j) {
130
            $row->appendChild(new TableCell('', $type, null));
131
        }
132
133
        return $row;
134
    }
135
136
    private function parseCaption(string $line): ?TableCaption
137
    {
138
        $caption = RegexHelper::matchAll(self::REGEXP_CAPTION, $line);
139
140
        if (null === $caption) {
141
            return null;
142
        }
143
144
        return new TableCaption($caption[1], $caption[2]);
145
    }
146
}
147