Passed
Push — new-api ( 34a0a9...30b18d )
by Sebastian
04:06
created

Text::render()   C

Complexity

Conditions 15
Paths 112

Size

Total Lines 39
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 15

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
eloc 33
c 1
b 0
f 0
nc 112
nop 2
dl 0
loc 39
ccs 33
cts 33
cp 1
crap 15
rs 5.8166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Observer\RenderingObserver;
19
use Seboettg\CiteProc\Rendering\Observer\RenderingObserverTrait;
20
use Seboettg\CiteProc\Rendering\Rendering;
21
use Seboettg\CiteProc\Style\Options\GlobalOptions;
22
use Seboettg\CiteProc\Styles\ConsecutivePunctuationCharacterTrait;
23
use Seboettg\CiteProc\Styles\StylesRenderer;
24
use Seboettg\CiteProc\Terms\Locator;
25
use Seboettg\CiteProc\Util\CiteProcHelper;
26
use Seboettg\CiteProc\Util\NumberHelper;
27
use Seboettg\CiteProc\Util\PageHelper;
28
use Seboettg\CiteProc\Util\StringHelper;
29
use Seboettg\Collection\ArrayList\ArrayListInterface;
30
use SimpleXMLElement;
31
use stdClass;
32
use function Seboettg\CiteProc\getCurrentById;
33
use function Seboettg\CiteProc\ucfirst;
34
35
class Text implements Rendering, RenderingObserver
36
{
37 117
    public static function factory(SimpleXMLElement $node)
38
    {
39 117
        $renderType = $renderObject = $form = null;
40 117
        foreach ($node->attributes() as $attribute) {
41 117
            $name = $attribute->getName();
42
            switch ($name) {
43 117
                case RenderType::TERM:
44 115
                case RenderType::MACRO:
45 112
                case RenderType::VARIABLE:
46 96
                case RenderType::VALUE:
47 117
                    $renderType = new RenderType($name);
48 117
                    $renderObject = (string) $attribute;
49 117
                    break;
50 75
                case "form":
51 40
                    $form = (string) $attribute;
52 117
                    break;
53
            }
54
        }
55 117
        $context = CiteProc::getContext();
56 117
        $locale = $context->getLocale();
57 117
        $macros = $context->getMacros();
58 117
        $globalOptions = $context->getGlobalOptions();
59 117
        $stylesRenderer = StylesRenderer::factory($node);
60 117
        $text = new self(
61 117
            $renderType,
62 117
            $renderObject,
0 ignored issues
show
Bug introduced by
It seems like $renderObject can also be of type null; however, parameter $renderObject of Seboettg\CiteProc\Render...ext\Text::__construct() does only seem to accept string, 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

62
            /** @scrutinizer ignore-type */ $renderObject,
Loading history...
63 117
            $form,
64 117
            $locale,
65 117
            $macros,
66 117
            $globalOptions,
67 117
            $stylesRenderer
68
        );
69 117
        $context->addObserver($text);
70 117
        return $text;
71
    }
72
73
74
    use ConsecutivePunctuationCharacterTrait;
75
    use RenderingObserverTrait;
76
77
    /** @var RenderType|null */
78
    private $renderType;
79
80
    /** @var string */
81
    private $renderObject;
82
83
    /** @var string */
84
    private $form;
85
86
    /** @var Locale|null */
87
    private $locale;
88
89
    /** @var ArrayListInterface */
90
    private $macros;
91
92
    /** @var StylesRenderer */
93
    private $stylesRenderer;
94
95
    /** @var GlobalOptions */
96
    private $globalOptions;
97
98
    /**
99
     * Text constructor.
100
     * @param RenderType|null $renderType
101
     * @param string $renderObject
102
     * @param string|null $form
103
     * @param Locale|null $locale
104
     * @param ArrayListInterface $macros
105
     * @param ?GlobalOptions $globalOptions
106
     * @param StylesRenderer $stylesRenderer
107
     */
108 117
    public function __construct(
109
        ?RenderType $renderType,
110
        string $renderObject,
111
        ?string $form,
112
        ?Locale $locale,
113
        ArrayListInterface $macros,
114
        ?GlobalOptions $globalOptions,
115
        StylesRenderer $stylesRenderer
116
    ) {
117 117
        $this->renderType = $renderType;
118 117
        $this->renderObject = $renderObject;
119 117
        $this->form = ($form ?? "long");
120 117
        $this->locale = $locale;
121 117
        $this->macros = $macros;
122 117
        $this->globalOptions = $globalOptions;
123 117
        $this->stylesRenderer = $stylesRenderer;
124
125 117
        $this->initObserver();
126 117
    }
127
128
    /**
129
     * @param  stdClass $data
130
     * @param  int|null $citationNumber
131
     * @return string
132
     */
133 101
    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...
134
    {
135 101
        $lang = (isset($data->language) && $data->language != 'en') ? $data->language : 'en';
136 101
        $this->stylesRenderer->getTextCase()->setLanguage($lang);
137 101
        $renderedText = "";
138 101
        switch ((string)$this->renderType) {
139 101
            case RenderType::VALUE:
140 25
                $renderedText = $this->stylesRenderer->renderTextCase($this->renderObject);
141 25
                break;
142 94
            case RenderType::VARIABLE:
143 84
                if ($this->renderObject === "locator" && $this->mode->equals(RenderingMode::CITATION())) {
144 8
                    $renderedText = $this->renderLocator($data, $citationNumber);
145 84
                } elseif ($this->renderObject === "citation-number") {
146 14
                    $renderedText = $this->renderCitationNumber($data, $citationNumber);
147 14
                    break;
148 80
                } elseif (in_array($this->renderObject, ["page", "chapter-number", "folio"])) {
149 32
                    $renderedText = !empty($data->{$this->renderObject}) ?
150 32
                        $this->renderPage($data->{$this->renderObject}) : '';
151
                } else {
152 76
                    $renderedText = $this->renderVariable($data);
153
                }
154 82
                if ($this->state->equals(RenderingState::SUBSTITUTION())) {
155 7
                    unset($data->{$this->renderObject});
156
                }
157 82
                $renderedText = $this->applyAdditionalMarkupFunction($data, $renderedText);
158 82
                break;
159 68
            case RenderType::MACRO:
160 61
                $renderedText = $this->renderMacro($data);
161 61
                break;
162 27
            case RenderType::TERM:
163 27
                $term = $this->locale
164 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

164
                    ->/** @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...
165 27
                    ->single;
166 27
                $renderedText = !empty($term) ? $this->stylesRenderer->renderTextCase($term) : "";
167
        }
168 101
        if (!empty($renderedText)) {
169 101
            $renderedText = $this->formatRenderedText($renderedText);
170
        }
171 101
        return $renderedText;
172
    }
173
174
    /**
175
     * @return string
176
     */
177 65
    public function getSource()
178
    {
179 65
        return $this->renderType;
180
    }
181
182
    /**
183
     * @return string
184
     */
185 65
    public function getVariable()
186
    {
187 65
        return $this->renderObject;
188
    }
189
190 21
    private function renderPage($page)
191
    {
192 21
        if (preg_match(NumberHelper::PATTERN_COMMA_AMPERSAND_RANGE, $page)) {
193 19
            $page = $this->normalizeDateRange($page);
194 19
            $ranges = preg_split("/[-–]/", trim($page));
195 19
            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

195
            if (count(/** @scrutinizer ignore-type */ $ranges) > 1) {
Loading history...
196 18
                if (!empty($this->globalOptions)
197 18
                    && !empty($this->globalOptions->getPageRangeFormat())
198
                ) {
199 8
                    return PageHelper::processPageRangeFormats(
200 8
                        $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

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