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

TableParser::parseColumns()   B

Complexity

Conditions 10
Paths 5

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 0
cts 16
cp 0
rs 7.6666
c 0
b 0
f 0
cc 10
nc 5
nop 1
crap 110

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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