|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* citeproc-php |
|
4
|
|
|
* |
|
5
|
|
|
* @link http://github.com/seboettg/citeproc-php for the source repository |
|
6
|
|
|
* @copyright Copyright (c) 2016 Sebastian Böttger. |
|
7
|
|
|
* @license https://opensource.org/licenses/MIT |
|
8
|
|
|
*/ |
|
9
|
|
|
|
|
10
|
|
|
namespace Seboettg\CiteProc\Rendering; |
|
11
|
|
|
use Seboettg\CiteProc\CiteProc; |
|
12
|
|
|
use Seboettg\CiteProc\Util; |
|
13
|
|
|
use Seboettg\CiteProc\Styles\AffixesTrait; |
|
14
|
|
|
use Seboettg\CiteProc\Styles\DisplayTrait; |
|
15
|
|
|
use Seboettg\CiteProc\Styles\FormattingTrait; |
|
16
|
|
|
use Seboettg\CiteProc\Styles\TextCaseTrait; |
|
17
|
|
|
|
|
18
|
|
|
|
|
19
|
|
|
/** |
|
20
|
|
|
* Class Number |
|
21
|
|
|
* @package Seboettg\CiteProc\Rendering |
|
22
|
|
|
* |
|
23
|
|
|
* @author Sebastian Böttger <[email protected]> |
|
24
|
|
|
*/ |
|
25
|
|
|
class Number implements RenderingInterface |
|
26
|
|
|
{ |
|
27
|
|
|
|
|
28
|
|
|
const RANGE_DELIMITER_HYPHEN = "-"; |
|
29
|
|
|
|
|
30
|
|
|
const RANGE_DELIMITER_AMPERSAND = "&"; |
|
31
|
|
|
|
|
32
|
|
|
const RANGE_DELIMITER_COMMA = ","; |
|
33
|
|
|
|
|
34
|
|
|
use FormattingTrait, |
|
35
|
|
|
AffixesTrait, |
|
36
|
|
|
TextCaseTrait, |
|
37
|
|
|
DisplayTrait; |
|
38
|
|
|
|
|
39
|
|
|
private $variable; |
|
40
|
|
|
|
|
41
|
|
|
private $form; |
|
42
|
|
|
|
|
43
|
|
|
public function __construct(\SimpleXMLElement $node) |
|
44
|
|
|
{ |
|
45
|
|
|
//<number variable="edition" form="ordinal"/> |
|
46
|
|
|
/** @var \SimpleXMLElement $attribute */ |
|
47
|
|
View Code Duplication |
foreach ($node->attributes() as $attribute) { |
|
|
|
|
|
|
48
|
|
|
switch ($attribute->getName()) { |
|
49
|
|
|
case 'variable': |
|
50
|
|
|
$this->variable = (string) $attribute; |
|
51
|
|
|
break; |
|
52
|
|
|
case 'form': |
|
53
|
|
|
$this->form = (string) $attribute; |
|
54
|
|
|
} |
|
55
|
|
|
} |
|
56
|
|
|
|
|
57
|
|
|
$this->initFormattingAttributes($node); |
|
58
|
|
|
$this->initAffixesAttributes($node); |
|
59
|
|
|
$this->initTextCaseAttributes($node); |
|
60
|
|
|
} |
|
61
|
|
|
|
|
62
|
|
|
/** |
|
63
|
|
|
* @param \stdClass $data |
|
64
|
|
|
* @param int|null $citationNumber |
|
65
|
|
|
* @return string |
|
66
|
|
|
*/ |
|
67
|
|
|
public function render($data, $citationNumber = null) |
|
68
|
|
|
{ |
|
69
|
|
|
$lang = (isset($data->language) && $data->language != 'en') ? $data->language : 'en'; |
|
70
|
|
|
|
|
71
|
|
|
if (empty($this->variable) || empty($data->{$this->variable})) { |
|
72
|
|
|
return ""; |
|
73
|
|
|
} |
|
74
|
|
|
switch ($this->form) { |
|
75
|
|
View Code Duplication |
case 'ordinal': |
|
|
|
|
|
|
76
|
|
|
$var = $data->{$this->variable}; |
|
77
|
|
|
if (preg_match("/\s*(\d+)\s*[\-\-\&,]\s*(\d+)\s*/", $var, $matches)) { |
|
78
|
|
|
$num1 = self::ordinal($matches[1]); |
|
79
|
|
|
$num2 = self::ordinal($matches[3]); |
|
80
|
|
|
$text = $this->buildNumberRangeString($num1, $num2, $matches[2]); |
|
81
|
|
|
} else { |
|
82
|
|
|
$text = self::ordinal($var); |
|
83
|
|
|
} |
|
84
|
|
|
break; |
|
85
|
|
View Code Duplication |
case 'long-ordinal': |
|
|
|
|
|
|
86
|
|
|
$var = $data->{$this->variable}; |
|
87
|
|
|
if (preg_match("/\s*(\d+)\s*[\-\-\&,]\s*(\d+)\s*/", $var, $matches)) { |
|
88
|
|
|
$num1 = self::longOrdinal($matches[1]); |
|
89
|
|
|
$num2 = self::longOrdinal($matches[3]); |
|
90
|
|
|
$text = $this->buildNumberRangeString($num1, $num2, $matches[2]); |
|
91
|
|
|
} else { |
|
92
|
|
|
$text = self::longOrdinal($var); |
|
93
|
|
|
} |
|
94
|
|
|
break; |
|
95
|
|
View Code Duplication |
case 'roman': |
|
|
|
|
|
|
96
|
|
|
$var = $data->{$this->variable}; |
|
97
|
|
|
if (preg_match("/\s*(\d+)\s*[\-\-\&,]\s*(\d+)\s*/", $var, $matches)) { |
|
98
|
|
|
$num1 = Util\Number::dec2roman($matches[1]); |
|
99
|
|
|
$num2 = Util\Number::dec2roman($matches[3]); |
|
100
|
|
|
$text = $this->buildNumberRangeString($num1, $num2, $matches[2]); |
|
101
|
|
|
} else { |
|
102
|
|
|
$text = Util\Number::dec2roman($var); |
|
103
|
|
|
} |
|
104
|
|
|
break; |
|
105
|
|
|
case 'numeric': |
|
106
|
|
|
default: |
|
107
|
|
|
/* |
|
108
|
|
|
During the extraction, numbers separated by a hyphen are stripped of intervening spaces (“2 - 4” |
|
109
|
|
|
becomes “2-4”). Numbers separated by a comma receive one space after the comma (“2,3” and “2 , 3” |
|
110
|
|
|
become “2, 3”), while numbers separated by an ampersand receive one space before and one after the |
|
111
|
|
|
ampersand (“2&3” becomes “2 & 3”). |
|
112
|
|
|
*/ |
|
113
|
|
|
$var = $data->{$this->variable}; |
|
114
|
|
|
if (preg_match("/\s*(\d+)\s*[\-\-\&,]\s*(\d+)\s*/", $var, $matches)) { |
|
115
|
|
|
$text = $this->buildNumberRangeString($matches[1], $matches[3], $matches[2]); |
|
116
|
|
|
} else { |
|
117
|
|
|
$text = Util\Number::dec2roman($var); |
|
118
|
|
|
} |
|
119
|
|
|
break; |
|
120
|
|
|
} |
|
121
|
|
|
return $this->wrapDisplayBlock($this->addAffixes($this->format($this->applyTextCase($text, $lang)))); |
|
122
|
|
|
} |
|
123
|
|
|
|
|
124
|
|
|
public static function ordinal($num) { |
|
125
|
|
|
if (($num / 10) % 10 == 1) { |
|
126
|
|
|
$num .= CiteProc::getContext()->getLocale()->filter('terms', 'ordinal-04')->single; |
|
127
|
|
|
} elseif ($num % 10 == 1) { |
|
128
|
|
|
$num .= CiteProc::getContext()->getLocale()->filter('terms', 'ordinal-01')->single; |
|
129
|
|
|
} elseif ($num % 10 == 2) { |
|
130
|
|
|
$num .= CiteProc::getContext()->getLocale()->filter('terms', 'ordinal-02')->single; |
|
131
|
|
|
} elseif ($num % 10 == 3) { |
|
132
|
|
|
$num .= CiteProc::getContext()->getLocale()->filter('terms', 'ordinal-03')->single; |
|
133
|
|
|
} else { |
|
134
|
|
|
$num .= CiteProc::getContext()->getLocale()->filter('terms', 'ordinal-04')->single; |
|
135
|
|
|
} |
|
136
|
|
|
return $num; |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
|
|
140
|
|
|
public static function longOrdinal($num) { |
|
141
|
|
|
$num = sprintf("%02d", $num); |
|
142
|
|
|
$ret = CiteProc::getContext()->getLocale()->filter('terms', 'long-ordinal-' . $num)->single; |
|
143
|
|
|
if (!$ret) { |
|
144
|
|
|
return self::ordinal($num); |
|
145
|
|
|
} |
|
146
|
|
|
return $ret; |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
|
|
/** |
|
150
|
|
|
* @param string|int $num1 |
|
151
|
|
|
* @param string|int $num2 |
|
152
|
|
|
* @param string $delim |
|
153
|
|
|
* @return string |
|
154
|
|
|
*/ |
|
155
|
|
|
public function buildNumberRangeString($num1, $num2, $delim) { |
|
156
|
|
|
|
|
157
|
|
|
if (self::RANGE_DELIMITER_AMPERSAND === $delim) { |
|
158
|
|
|
$numRange = "$num1 " . self::RANGE_DELIMITER_AMPERSAND . " $num2"; |
|
159
|
|
|
} else if (self::RANGE_DELIMITER_COMMA === $delim) { |
|
160
|
|
|
$numRange = $num1 . htmlentities(self::RANGE_DELIMITER_COMMA) . " $num2"; |
|
161
|
|
|
} else { |
|
162
|
|
|
$numRange = $num1 . self::RANGE_DELIMITER_HYPHEN . $num2; |
|
163
|
|
|
} |
|
164
|
|
|
return $numRange; |
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
|
|
168
|
|
|
|
|
169
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.