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