|
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\Styles\AffixesTrait; |
|
12
|
|
|
use Seboettg\CiteProc\Styles\ConsecutivePunctuationCharacterTrait; |
|
13
|
|
|
use Seboettg\CiteProc\Styles\DelimiterTrait; |
|
14
|
|
|
use Seboettg\CiteProc\Styles\DisplayTrait; |
|
15
|
|
|
use Seboettg\CiteProc\Util\Factory; |
|
16
|
|
|
use Seboettg\Collection\ArrayList; |
|
17
|
|
|
|
|
18
|
|
|
|
|
19
|
|
|
/** |
|
20
|
|
|
* Class Group |
|
21
|
|
|
* @package Seboettg\CiteProc\Rendering |
|
22
|
|
|
* |
|
23
|
|
|
* @author Sebastian Böttger <[email protected]> |
|
24
|
|
|
*/ |
|
25
|
|
|
class Group implements RenderingInterface, HasParent |
|
26
|
|
|
{ |
|
27
|
|
|
use DelimiterTrait, |
|
28
|
|
|
AffixesTrait, |
|
29
|
|
|
DisplayTrait, |
|
30
|
|
|
ConsecutivePunctuationCharacterTrait; |
|
31
|
|
|
|
|
32
|
|
|
const CLASS_PATH = 'Seboettg\CiteProc\Rendering'; |
|
33
|
|
|
|
|
34
|
|
|
private static $suppressableElements = [ |
|
35
|
|
|
self::CLASS_PATH . '\Number', |
|
36
|
|
|
self::CLASS_PATH . '\Group', |
|
37
|
|
|
self::CLASS_PATH . '\Date\Date' |
|
38
|
|
|
]; |
|
39
|
|
|
|
|
40
|
|
|
/** |
|
41
|
|
|
* @var ArrayList |
|
42
|
|
|
*/ |
|
43
|
|
|
private $children; |
|
44
|
|
|
|
|
45
|
|
|
/** |
|
46
|
|
|
* cs:group may carry the delimiter attribute to separate its child elements |
|
47
|
|
|
* @var |
|
48
|
|
|
*/ |
|
49
|
|
|
private $delimiter = ""; |
|
50
|
|
|
|
|
51
|
|
|
private $parent; |
|
52
|
|
|
|
|
53
|
|
View Code Duplication |
public function __construct(\SimpleXMLElement $node, $parent) |
|
|
|
|
|
|
54
|
|
|
{ |
|
55
|
|
|
$this->parent = $parent; |
|
56
|
|
|
$this->children = new ArrayList(); |
|
57
|
|
|
foreach ($node->children() as $child) { |
|
58
|
|
|
$this->children->append(Factory::create($child, $this)); |
|
59
|
|
|
} |
|
60
|
|
|
$this->initDisplayAttributes($node); |
|
61
|
|
|
$this->initAffixesAttributes($node); |
|
62
|
|
|
$this->initDelimiterAttributes($node); |
|
63
|
|
|
} |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* @param $data |
|
67
|
|
|
* @param int|null $citationNumber |
|
68
|
|
|
* @return string |
|
69
|
|
|
*/ |
|
70
|
|
|
public function render($data, $citationNumber = null) |
|
71
|
|
|
{ |
|
72
|
|
|
//$text = ''; |
|
|
|
|
|
|
73
|
|
|
$textParts = array(); |
|
74
|
|
|
$terms = $variables = $haveVariables = $elementCount = 0; |
|
75
|
|
|
foreach ($this->children as $child) { |
|
76
|
|
|
$elementCount++; |
|
77
|
|
|
if (($child instanceof Text) && |
|
78
|
|
|
($child->getSource() == 'term' || |
|
79
|
|
|
$child->getSource() == 'value' )) { |
|
80
|
|
|
$terms++; |
|
81
|
|
|
} |
|
82
|
|
|
if (($child instanceof Label)) { |
|
83
|
|
|
++$terms; |
|
84
|
|
|
} |
|
85
|
|
|
if (method_exists($child, "getSource") && $child->getSource() == 'variable' && |
|
86
|
|
|
!empty($child->getVariable()) && |
|
87
|
|
|
!empty($data->{$child->getVariable()}) |
|
88
|
|
|
) { |
|
89
|
|
|
++$variables; |
|
90
|
|
|
} |
|
91
|
|
|
$text = $child->render($data, $citationNumber); |
|
92
|
|
|
$delimiter = $this->delimiter; |
|
93
|
|
|
if (!empty($text)) { |
|
94
|
|
|
if ($delimiter && ($elementCount < count($this->children))) { |
|
95
|
|
|
//check to see if the delimiter is already the last character of the text string |
|
96
|
|
|
//if so, remove it so we don't have two of them when we paste together the group |
|
97
|
|
|
$stext = strip_tags(trim($text)); |
|
98
|
|
|
if ((strrpos($stext, $delimiter[0]) + 1) == strlen($stext) && strlen($stext) > 1) { |
|
99
|
|
|
$text = str_replace($stext, '----REPLACE----', $text); |
|
100
|
|
|
$stext = substr($stext, 0, -1); |
|
101
|
|
|
$text = str_replace('----REPLACE----', $stext, $text); |
|
102
|
|
|
} |
|
103
|
|
|
} |
|
104
|
|
|
//give the text parts a name |
|
105
|
|
|
if($child instanceof Text) { |
|
106
|
|
|
$textParts[$child->getVariable()] = $text; |
|
107
|
|
|
} else { |
|
108
|
|
|
$textParts[$elementCount] = $text; |
|
109
|
|
|
} |
|
110
|
|
|
|
|
111
|
|
|
|
|
112
|
|
|
if (method_exists($child, "getSource") && $child->getSource() == 'variable' || (method_exists($child, "getVariable") && !empty($child->getVariable()))) { |
|
113
|
|
|
$haveVariables++; |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
if (method_exists($child, "getSource") && $child->getSource() == 'macro') { |
|
117
|
|
|
$haveVariables++; |
|
118
|
|
|
} |
|
119
|
|
|
} |
|
120
|
|
|
} |
|
121
|
|
|
if (empty($textParts)) { |
|
122
|
|
|
return ""; |
|
123
|
|
|
} |
|
124
|
|
|
if ($variables && !$haveVariables) { |
|
125
|
|
|
return ""; // there has to be at least one other none empty value before the term is output |
|
126
|
|
|
} |
|
127
|
|
|
|
|
128
|
|
|
if (count($textParts) == $terms) { |
|
129
|
|
|
return ""; // there has to be at least one other none empty value before the term is output |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
|
|
//$text = implode($delimiter, $textParts); // insert the delimiter if supplied. |
|
|
|
|
|
|
133
|
|
|
$text = implode($this->delimiter, $textParts); |
|
134
|
|
|
if (!empty($text)) { |
|
135
|
|
|
return $this->wrapDisplayBlock($this->addAffixes(($text))); |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
return ""; |
|
139
|
|
|
/* |
|
|
|
|
|
|
140
|
|
|
$arr = new ArrayList(); |
|
141
|
|
|
$i = 0; |
|
142
|
|
|
|
|
143
|
|
|
cs:group implicitly acts as a conditional: cs:group and its child elements are suppressed if a) at least |
|
144
|
|
|
one rendering element in cs:group calls a variable (either directly or via a macro), and b) all variables |
|
145
|
|
|
that are called are empty. This accommodates descriptive cs:text elements. |
|
146
|
|
|
|
|
147
|
|
|
$suppressConditionA = false; |
|
148
|
|
|
/** @var RenderingInterface $child / |
|
149
|
|
|
foreach ($this->children as $child) { |
|
150
|
|
|
|
|
151
|
|
|
$res = $child->render($data, $citationNumber); |
|
152
|
|
|
$this->getChildsAffixesAndDelimiter($child); |
|
153
|
|
|
|
|
154
|
|
|
if ($this->doesRenderingElementCallsVariable($child)) { |
|
155
|
|
|
//$arr[] = $res; |
|
156
|
|
|
$arr->add(get_class($child).$i, $res); |
|
157
|
|
|
$suppressConditionA = true; |
|
158
|
|
|
} else { |
|
159
|
|
|
$arr[$i] = $res; |
|
160
|
|
|
} |
|
161
|
|
|
++$i; |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
|
|
$suppressConditionB = null; |
|
165
|
|
|
|
|
166
|
|
|
if ($suppressConditionA) { |
|
167
|
|
|
foreach ($arr as $key => $value) { |
|
168
|
|
|
if (is_null($suppressConditionB)) { |
|
169
|
|
|
$suppressConditionB = true; |
|
170
|
|
|
} |
|
171
|
|
|
if (!is_numeric($key)) { |
|
172
|
|
|
$suppressConditionB = $suppressConditionB && empty($value); |
|
173
|
|
|
} |
|
174
|
|
|
if (!$suppressConditionB) { |
|
175
|
|
|
break; |
|
176
|
|
|
} |
|
177
|
|
|
} |
|
178
|
|
|
} |
|
179
|
|
|
|
|
180
|
|
|
if ($suppressConditionA && !is_null($suppressConditionB) && $suppressConditionB === true) { |
|
181
|
|
|
return ""; |
|
182
|
|
|
} |
|
183
|
|
|
|
|
184
|
|
|
|
|
185
|
|
|
if (!empty($arr)) { |
|
186
|
|
|
$res = $this->wrapDisplayBlock($this->addAffixes(implode($this->delimiter, $arr->toArray()))); |
|
187
|
|
|
$res = $this->removeConsecutiveChars($res); |
|
188
|
|
|
return $res; |
|
189
|
|
|
} |
|
190
|
|
|
return ""; |
|
191
|
|
|
*/ |
|
192
|
|
|
} |
|
193
|
|
|
|
|
194
|
|
|
|
|
195
|
|
|
private function doesRenderingElementCallsVariable($child) |
|
|
|
|
|
|
196
|
|
|
{ |
|
197
|
|
|
if ($child instanceof Text) { |
|
198
|
|
|
if ($child->rendersVariable()) { |
|
199
|
|
|
return true; |
|
200
|
|
|
} |
|
201
|
|
|
return false; |
|
202
|
|
|
} |
|
203
|
|
|
if (in_array(get_class($child), self::$suppressableElements)) { |
|
204
|
|
|
|
|
205
|
|
|
return true; |
|
206
|
|
|
} |
|
207
|
|
|
return false; |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
/** |
|
211
|
|
|
* @return mixed |
|
212
|
|
|
*/ |
|
213
|
|
|
public function getParent() |
|
214
|
|
|
{ |
|
215
|
|
|
return $this->parent; |
|
216
|
|
|
} |
|
217
|
|
|
} |
|
|
|
|
|
|
218
|
|
|
|
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.