Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.38% covered (success)
95.38%
62 / 65
72.73% covered (warning)
72.73%
8 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
PageNumber
95.38% covered (success)
95.38%
62 / 65
72.73% covered (warning)
72.73%
8 / 11
30
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getFormattedPageNumber
95.24% covered (success)
95.24%
20 / 21
0.00% covered (danger)
0.00%
0 / 1
9
 getRawPageNumber
95.24% covered (success)
95.24%
20 / 21
0.00% covered (danger)
0.00%
0 / 1
9
 isEmpty
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDisplayMode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isNumeric
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isRecto
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 formatRectoVerso
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 rawRectoVerso
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getDisplayModes
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 formatICU
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace ProofreadPage\Pagination;
4
5use MediaWiki\Language\Language;
6use MediaWiki\Parser\Sanitizer;
7use NumberFormatter;
8use ProofreadPage\Pagination\CustomNumberFormatters\BengaliCurrencyFormat;
9
10/**
11 * @license GPL-2.0-or-later
12 */
13class PageNumber {
14
15    public const DISPLAY_NORMAL = 'normal';
16    public const DISPLAY_HIGHROMAN = 'highroman';
17    public const DISPLAY_ROMAN = 'roman';
18    public const DISPLAY_FOLIO = 'folio';
19    public const DISPLAY_FOLIOHIGHROMAN = 'foliohighroman';
20    public const DISPLAY_FOLIOROMAN = 'folioroman';
21    public const DISPLAY_BENGALI_CURRENCY = 'prpbengalicurrency';
22    public const DISPLAY_EMPTY = 'empty';
23
24    /**
25     * Mapping between the ProofreadPage number formats ids and the one from ICU.
26     * The full list of ICU number formats is available here:
27     * https://github.com/unicode-org/cldr/blob/master/common/supplemental/numberingSystems.xml
28     */
29    private const DISPLAY_FROM_ICU = [
30        'beng' => 'beng',
31        'deva' => 'deva',
32        'highroman' => 'roman',
33        'roman' => 'romanlow',
34        'tamldec' => 'tamldec',
35        'guru' => 'guru',
36        'gujr' => 'gujr',
37        'telu' => 'telu',
38        'knda' => 'knda',
39        'mlym' => 'mlym',
40        'orya' => 'orya',
41        'thai' => 'thai',
42        'latn' => 'latn'
43    ];
44
45    /** @var string */
46    private $number;
47
48    /** @var string */
49    private $displayMode;
50
51    /** @var bool */
52    private $isEmpty;
53
54    /** @var bool */
55    private $isRecto;
56
57    /**
58     * @param string $number the page number
59     * @param string $displayMode the display mode (one of the DISPLAY_* constant)
60     * @param bool $isEmpty
61     * @param bool $isRecto true if recto, false if verso (for folio modes only)
62     */
63    public function __construct(
64        string $number,
65        string $displayMode = self::DISPLAY_NORMAL,
66        bool $isEmpty = false,
67        bool $isRecto = true
68    ) {
69        $this->number = $number;
70        $this->displayMode = $displayMode;
71        $this->isEmpty = $isEmpty;
72        $this->isRecto = $isRecto;
73    }
74
75    /**
76     * Returns the formatted page number as it should be displayed
77     *
78     * @param Language $language the language used for formatting
79     * @return string
80     */
81    public function getFormattedPageNumber( Language $language ): string {
82        if ( !is_numeric( $this->number ) ) {
83            return $this->number;
84        }
85
86        $number = (int)$this->number;
87        switch ( $this->displayMode ) {
88            case self::DISPLAY_NORMAL:
89                return Sanitizer::escapeHtmlAllowEntities( $language->formatNumNoSeparators( $number ) );
90            case self::DISPLAY_FOLIO:
91                return Sanitizer::escapeHtmlAllowEntities( $language->formatNumNoSeparators( $number ) ) .
92                    $this->formatRectoVerso();
93            case self::DISPLAY_FOLIOHIGHROMAN:
94                return self::formatICU( $language, 'roman', $number ) .
95                    $this->formatRectoVerso();
96            case self::DISPLAY_FOLIOROMAN:
97                return self::formatICU( $language, 'romanlow', $number ) .
98                    $this->formatRectoVerso();
99            case self::DISPLAY_BENGALI_CURRENCY:
100                $formatter = new BengaliCurrencyFormat();
101                return $formatter->formatNumber( $language, $number );
102            default:
103                if ( array_key_exists( $this->displayMode, self::DISPLAY_FROM_ICU ) ) {
104                    return self::formatICU( $language, self::DISPLAY_FROM_ICU[$this->displayMode], $number );
105                } else {
106                    return $this->number;
107                }
108        }
109    }
110
111    /**
112     * Returns the raw page number, without any formatting
113     *
114     * @param Language $language
115     * @return string
116     */
117    public function getRawPageNumber( Language $language ): string {
118        if ( !is_numeric( $this->number ) ) {
119            return $this->number;
120        }
121
122        $number = (int)$this->number;
123        switch ( $this->displayMode ) {
124            case self::DISPLAY_NORMAL:
125                return Sanitizer::escapeHtmlAllowEntities( $language->formatNumNoSeparators( $number ) );
126            case self::DISPLAY_FOLIO:
127                return Sanitizer::escapeHtmlAllowEntities( $language->formatNumNoSeparators( $number ) ) .
128                    $this->rawRectoVerso();
129            case self::DISPLAY_FOLIOHIGHROMAN:
130                return self::formatICU( $language, 'roman', $number ) .
131                    $this->rawRectoVerso();
132            case self::DISPLAY_FOLIOROMAN:
133                return self::formatICU( $language, 'romanlow', $number ) .
134                    $this->rawRectoVerso();
135            case self::DISPLAY_BENGALI_CURRENCY:
136                $formatter = new BengaliCurrencyFormat();
137                return $formatter->formatNumber( $language, $number );
138            default:
139                if ( array_key_exists( $this->displayMode, self::DISPLAY_FROM_ICU ) ) {
140                    return self::formatICU( $language, self::DISPLAY_FROM_ICU[$this->displayMode], $number );
141                } else {
142                    return $this->number;
143                }
144        }
145    }
146
147    /**
148     * @return bool
149     */
150    public function isEmpty(): bool {
151        return $this->isEmpty;
152    }
153
154    /**
155     * @return string
156     */
157    public function getDisplayMode(): string {
158        return $this->displayMode;
159    }
160
161    /**
162     * @return bool
163     */
164    public function isNumeric(): bool {
165        return is_numeric( $this->number );
166    }
167
168    /**
169     * @return bool
170     */
171    public function isRecto(): bool {
172        return $this->isRecto;
173    }
174
175    /**
176     * @return string
177     */
178    private function formatRectoVerso(): string {
179        return $this->isRecto ? '<sup>r</sup>' : '<sup>v</sup>';
180    }
181
182    /**
183     * @return string
184     */
185    private function rawRectoVerso(): string {
186        return $this->isRecto ? 'r' : 'v';
187    }
188
189    /**
190     * @return string[]
191     */
192    public static function getDisplayModes(): array {
193        $modes = array_keys( self::DISPLAY_FROM_ICU );
194        $modes[] = self::DISPLAY_NORMAL;
195        $modes[] = self::DISPLAY_FOLIO;
196        $modes[] = self::DISPLAY_FOLIOHIGHROMAN;
197        $modes[] = self::DISPLAY_FOLIOROMAN;
198        $modes[] = self::DISPLAY_BENGALI_CURRENCY;
199        return $modes;
200    }
201
202    /**
203     * Formats a number in $language using the name numbering system using the ICU data
204     *
205     * @param Language $language
206     * @param string|null $name
207     * @param int $number
208     * @return string|false
209     */
210    private static function formatICU( Language $language, ?string $name, int $number ) {
211        $locale = $language->getCode();
212        if ( $name !== null ) {
213            $locale .= '-u-nu-' . $name;
214        }
215        $formatter = new NumberFormatter( $locale, NumberFormatter::DEFAULT_STYLE );
216        $formatter->setSymbol( NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '' );
217        return $formatter->format( $number );
218    }
219}