Passed
Push — new-api ( 5677f6...2a03a6 )
by Sebastian
04:12
created

Text::getParent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
/*
4
 * citeproc-php
5
 *
6
 * @link        http://github.com/seboettg/citeproc-php for the source repository
7
 * @copyright   Copyright (c) 2016 Sebastian Böttger.
8
 * @license     https://opensource.org/licenses/MIT
9
 */
10
11
namespace Seboettg\CiteProc\Rendering\Text;
12
13
use Seboettg\CiteProc\CiteProc;
14
use Seboettg\CiteProc\Config\RenderingMode;
15
use Seboettg\CiteProc\Exception\CiteProcException;
16
use Seboettg\CiteProc\Config\RenderingState;
17
use Seboettg\CiteProc\Locale\Locale;
18
use Seboettg\CiteProc\Rendering\HasParent;
19
use Seboettg\CiteProc\Rendering\Observer\RenderingObserver;
20
use Seboettg\CiteProc\Rendering\Observer\RenderingObserverTrait;
21
use Seboettg\CiteProc\Rendering\Rendering;
22
use Seboettg\CiteProc\Style\Options\GlobalOptions;
23
use Seboettg\CiteProc\Styles\ConsecutivePunctuationCharacterTrait;
24
use Seboettg\CiteProc\Styles\StylesRenderer;
25
use Seboettg\CiteProc\Terms\Locator;
26
use Seboettg\CiteProc\Util\CiteProcHelper;
27
use Seboettg\CiteProc\Util\NumberHelper;
28
use Seboettg\CiteProc\Util\PageHelper;
29
use Seboettg\CiteProc\Util\StringHelper;
30
use Seboettg\Collection\ArrayList\ArrayListInterface;
31
use SimpleXMLElement;
32
use stdClass;
33
use function Seboettg\CiteProc\getCurrentById;
34
use function Seboettg\CiteProc\ucfirst;
35
36
class Text implements HasParent, Rendering, RenderingObserver
37
{
38
    use ConsecutivePunctuationCharacterTrait;
39
    use RenderingObserverTrait;
40
41
    /** @var RenderType|null */
42
    private $renderType;
43
44
    /** @var string */
45
    private $renderObject;
46
47
    /** @var string */
48
    private $form;
49
50
    /** @var Locale|null */
51
    private $locale;
52
53
    /** @var ArrayListInterface */
54
    private $macros;
55
56
    /** @var StylesRenderer */
57
    private $stylesRenderer;
58
59
    /** @var GlobalOptions */
60
    private $globalOptions;
61
62
    private $parent;
63
64 120
    public static function factory(SimpleXMLElement $node)
65
    {
66 120
        $renderObject = "";
67 120
        $renderType = $form = null;
68 120
        foreach ($node->attributes() as $attribute) {
69 120
            $name = $attribute->getName();
70
            switch ($name) {
71 120
                case RenderType::TERM:
72 118
                case RenderType::MACRO:
73 115
                case RenderType::VARIABLE:
74 99
                case RenderType::VALUE:
75 120
                    $renderType = new RenderType($name);
76 120
                    $renderObject = (string) $attribute;
77 120
                    break;
78 78
                case "form":
79 42
                    $form = (string) $attribute;
80 120
                    break;
81
            }
82
        }
83 120
        $context = CiteProc::getContext();
84 120
        $locale = $context->getLocale();
85 120
        $macros = $context->getMacros();
86 120
        $globalOptions = $context->getGlobalOptions();
87 120
        $stylesRenderer = StylesRenderer::factory($node);
88 120
        $text = new self(
89 120
            $renderType,
90 120
            $renderObject,
91 120
            $form,
92 120
            $locale,
93 120
            $macros,
94 120
            $globalOptions,
95 120
            $stylesRenderer
96
        );
97 120
        $context->addObserver($text);
98 120
        return $text;
99
    }
100
101
    /**
102
     * Text constructor.
103
     * @param RenderType|null $renderType
104
     * @param string $renderObject
105
     * @param string|null $form
106
     * @param Locale|null $locale
107
     * @param ArrayListInterface $macros
108
     * @param ?GlobalOptions $globalOptions
109
     * @param StylesRenderer $stylesRenderer
110
     */
111 120
    public function __construct(
112
        ?RenderType $renderType,
113
        string $renderObject,
114
        ?string $form,
115
        ?Locale $locale,
116
        ArrayListInterface $macros,
117
        ?GlobalOptions $globalOptions,
118
        StylesRenderer $stylesRenderer
119
    ) {
120 120
        $this->renderType = $renderType;
121 120
        $this->renderObject = $renderObject;
122 120
        $this->form = ($form ?? "long");
123 120
        $this->locale = $locale;
124 120
        $this->macros = $macros;
125 120
        $this->globalOptions = $globalOptions;
126 120
        $this->stylesRenderer = $stylesRenderer;
127
128 120
        $this->initObserver();
129 120
    }
130
131
    /**
132
     * @param  stdClass $data
133
     * @param  int|null $citationNumber
134
     * @return string
135
     */
136 104
    public function render($data, $citationNumber = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$citationNumber" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$citationNumber"; expected 0 but found 1
Loading history...
137
    {
138 104
        $lang = (isset($data->language) && $data->language != 'en') ? $data->language : 'en';
139 104
        $this->stylesRenderer->getTextCase()->setLanguage($lang);
140 104
        $renderedText = "";
141 104
        switch ((string)$this->renderType) {
142 104
            case RenderType::VALUE:
143 26
                $renderedText = $this->stylesRenderer->renderTextCase($this->renderObject);
144 26
                break;
145 97
            case RenderType::VARIABLE:
146 87
                if ($this->renderObject === "locator" && $this->mode->equals(RenderingMode::CITATION())) {
147 8
                    $renderedText = $this->renderLocator($data, $citationNumber);
148 87
                } elseif ($this->renderObject === "citation-number") {
149 16
                    $renderedText = $this->renderCitationNumber($data, $citationNumber);
150 16
                    break;
151 83
                } elseif (in_array($this->renderObject, ["page", "chapter-number", "folio"])) {
152 35
                    $renderedText = !empty($data->{$this->renderObject}) ?
153 35
                        $this->renderPage($data->{$this->renderObject}) : '';
154
                } else {
155 79
                    $renderedText = $this->renderVariable($data);
156
                }
157 85
                if ($this->state->equals(RenderingState::SUBSTITUTION())) {
158 7
                    unset($data->{$this->renderObject});
159
                }
160 85
                $renderedText = $this->applyAdditionalMarkupFunction($data, $renderedText);
161 85
                break;
162 70
            case RenderType::MACRO:
163 63
                $renderedText = $this->renderMacro($data);
164 63
                break;
165 27
            case RenderType::TERM:
166 27
                $term = $this->locale
167 27
                    ->filter("terms", $this->renderObject, $this->form)
0 ignored issues
show
Bug introduced by
The method filter() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

167
                    ->/** @scrutinizer ignore-call */ filter("terms", $this->renderObject, $this->form)

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
168 27
                    ->single;
169 27
                $renderedText = !empty($term) ? $this->stylesRenderer->renderTextCase($term) : "";
170
        }
171 104
        if (!empty($renderedText)) {
172 104
            $renderedText = $this->formatRenderedText($renderedText);
173
        }
174 104
        return $renderedText;
175
    }
176
177
    /**
178
     * @return string
179
     */
180 68
    public function getSource()
181
    {
182 68
        return $this->renderType;
183
    }
184
185
    /**
186
     * @return string
187
     */
188 68
    public function getVariable()
189
    {
190 68
        return $this->renderObject;
191
    }
192
193 24
    private function renderPage($page)
194
    {
195 24
        if (preg_match(NumberHelper::PATTERN_COMMA_AMPERSAND_RANGE, $page)) {
196 22
            $page = $this->normalizeDateRange($page);
197 22
            $ranges = preg_split("/[-–]/", trim($page));
198 22
            if (count($ranges) > 1) {
0 ignored issues
show
Bug introduced by
It seems like $ranges can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

198
            if (count(/** @scrutinizer ignore-type */ $ranges) > 1) {
Loading history...
199 21
                if (!empty($this->globalOptions)
200 21
                    && !empty($this->globalOptions->getPageRangeFormat())
201
                ) {
202 9
                    return PageHelper::processPageRangeFormats(
203 9
                        $ranges,
0 ignored issues
show
Bug introduced by
It seems like $ranges can also be of type false; however, parameter $ranges of Seboettg\CiteProc\Util\P...ocessPageRangeFormats() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

203
                        /** @scrutinizer ignore-type */ $ranges,
Loading history...
204 9
                        $this->globalOptions->getPageRangeFormat()
205
                    );
206
                }
207 12
                list($from, $to) = $ranges;
208 12
                return $from . "–" . $to;
209
            }
210
        }
211 4
        return $page;
212
    }
213
214 8
    private function renderLocator($data, $citationNumber)
215
    {
216 8
        $citationItem = getCurrentById($this->citationItems, $data->id);
217 8
        if (!empty($citationItem->label)) {
218 4
            $locatorData = new stdClass();
219 4
            $propertyName = Locator::mapLocatorLabelToRenderVariable($citationItem->label);
220 4
            $locatorData->{$propertyName} = trim($citationItem->locator);
221 4
            $renderTypeValueTemp = $this->renderObject;
222 4
            $this->renderObject = $propertyName;
223 4
            $result = $this->render($locatorData, $citationNumber);
224 4
            $this->renderObject = $renderTypeValueTemp;
225 4
            return $result;
226
        }
227 4
        return isset($citationItem->locator) ? trim($citationItem->locator) : '';
228
    }
229
230 22
    private function normalizeDateRange($page)
231
    {
232 22
        if (preg_match("/^(\d+)\s?--?\s?(\d+)$/", trim($page), $matches)) {
233 21
            return $matches[1]."-".$matches[2];
234
        }
235 1
        return $page;
236
    }
237
238
    /**
239
     * @param  $data
240
     * @param  $renderedText
241
     * @return mixed
242
     */
243 87
    private function applyAdditionalMarkupFunction($data, $renderedText)
244
    {
245 87
        return CiteProcHelper::applyAdditionMarkupFunction($data, $this->renderObject, $renderedText);
246
    }
247
248
    /**
249
     * @param  $data
250
     * @return string
251
     */
252 79
    private function renderVariable($data)
253
    {
254
        // check if there is an attribute with prefix short or long e.g. shortTitle or longAbstract
255
        // test case group_ShortOutputOnly.json
256 79
        $value = "";
257 79
        if (in_array($this->form, ["short", "long"])) {
258 79
            $attrWithPrefix = $this->form . ucfirst($this->renderObject);
259 79
            $attrWithSuffix = sprintf("%s-%s", $this->renderObject, $this->form);
260 79
            if (isset($data->{$attrWithPrefix}) && !empty($data->{$attrWithPrefix})) {
261 1
                $value = $data->{$attrWithPrefix};
262
            } else {
263 78
                if (isset($data->{$attrWithSuffix}) && !empty($data->{$attrWithSuffix})) {
264 3
                    $value = $data->{$attrWithSuffix};
265
                } else {
266 78
                    if (isset($data->{$this->renderObject})) {
267 79
                        $value = $data->{$this->renderObject};
268
                    }
269
                }
270
            }
271
        } else {
272
            if (!empty($data->{$this->renderObject})) {
273
                $value = $data->{$this->renderObject};
274
            }
275
        }
276 79
        return $this->stylesRenderer->renderTextCase(
277 79
            StringHelper::clearApostrophes(
278 79
                htmlspecialchars((string)$value, ENT_HTML5)
279
            )
280
        );
281
    }
282
283
    /**
284
     * @param  $renderedText
285
     * @return string
286
     */
287 104
    private function formatRenderedText($renderedText)
288
    {
289 104
        $text = $this->stylesRenderer->renderFormatting((string)$renderedText);
290 104
        $res = $this->stylesRenderer->renderAffixes($text);
291 104
        if (!empty($res)) {
292 104
            $res = $this->removeConsecutiveChars($res);
293
        }
294 104
        $res = $this->stylesRenderer->renderQuotes($res);
295 104
        return $this->stylesRenderer->renderDisplay($res);
296
    }
297
298
    /**
299
     * @param  $data
300
     * @param  $citationNumber
301
     * @return int|mixed
302
     */
303 16
    private function renderCitationNumber($data, $citationNumber)
304
    {
305 16
        $renderedText = $citationNumber + 1;
306 16
        $renderedText = $this->applyAdditionalMarkupFunction($data, $renderedText);
307 16
        return $renderedText;
308
    }
309
310
    /**
311
     * @param  $data
312
     * @return string
313
     */
314 63
    private function renderMacro($data)
315
    {
316 63
        $macro = $this->macros->get($this->renderObject);
317 63
        if (is_null($macro)) {
318
            try {
319 1
                throw new CiteProcException("Macro \"".$this->renderObject."\" does not exist.");
320 1
            } catch (CiteProcException $e) {
321 1
                $renderedText = "";
322
            }
323
        } else {
324 63
            $renderedText = $macro->render($data);
325
        }
326 63
        return $renderedText;
327
    }
328
329
    public function getParent()
330
    {
331
        return $this->parent;
332
    }
333
334 80
    public function setParent($parent)
335
    {
336 80
        $this->parent = $parent;
337 80
    }
338
}
339