1<?php
2
3/*
4 * This file is part of the league/commonmark package.
5 *
6 * (c) Colin O'Dell <colinodell@gmail.com>
7 *
8 * Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
9 *  - (c) Atlassian Pty Ltd
10 *
11 * For the full copyright and license information, please view the LICENSE
12 * file that was distributed with this source code.
13 */
14
15namespace League\CommonMark\Delimiter\Processor;
16
17use League\CommonMark\Delimiter\DelimiterInterface;
18use League\CommonMark\Inline\Element\AbstractStringContainer;
19
20/**
21 * An implementation of DelimiterProcessorInterface that dispatches all calls to two or more other DelimiterProcessors
22 * depending on the length of the delimiter run. All child DelimiterProcessors must have different minimum
23 * lengths. A given delimiter run is dispatched to the child with the largest acceptable minimum length. If no
24 * child is applicable, the one with the largest minimum length is chosen.
25 *
26 * @internal
27 */
28final class StaggeredDelimiterProcessor implements DelimiterProcessorInterface
29{
30    /** @var string */
31    private $delimiterChar;
32
33    /** @var int */
34    private $minLength = 0;
35
36    /** @var array<int, DelimiterProcessorInterface>|DelimiterProcessorInterface[] */
37    private $processors = []; // keyed by minLength in reverse order
38
39    public function __construct(string $char, DelimiterProcessorInterface $processor)
40    {
41        $this->delimiterChar = $char;
42        $this->add($processor);
43    }
44
45    public function getOpeningCharacter(): string
46    {
47        return $this->delimiterChar;
48    }
49
50    public function getClosingCharacter(): string
51    {
52        return $this->delimiterChar;
53    }
54
55    public function getMinLength(): int
56    {
57        return $this->minLength;
58    }
59
60    /**
61     * Adds the given processor to this staggered delimiter processor
62     *
63     * @param DelimiterProcessorInterface $processor
64     *
65     * @return void
66     */
67    public function add(DelimiterProcessorInterface $processor)
68    {
69        $len = $processor->getMinLength();
70
71        if (isset($this->processors[$len])) {
72            throw new \InvalidArgumentException(\sprintf('Cannot add two delimiter processors for char "%s" and minimum length %d', $this->delimiterChar, $len));
73        }
74
75        $this->processors[$len] = $processor;
76        \krsort($this->processors);
77
78        $this->minLength = \min($this->minLength, $len);
79    }
80
81    public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int
82    {
83        return $this->findProcessor($opener->getLength())->getDelimiterUse($opener, $closer);
84    }
85
86    public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse)
87    {
88        $this->findProcessor($delimiterUse)->process($opener, $closer, $delimiterUse);
89    }
90
91    private function findProcessor(int $len): DelimiterProcessorInterface
92    {
93        // Find the "longest" processor which can handle this length
94        foreach ($this->processors as $processor) {
95            if ($processor->getMinLength() <= $len) {
96                return $processor;
97            }
98        }
99
100        // Just use the first one in our list
101        /** @var DelimiterProcessorInterface $first */
102        $first = \reset($this->processors);
103
104        return $first;
105    }
106}
107