Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.13% covered (success)
91.13%
267 / 293
72.22% covered (warning)
72.22%
13 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
MediaInfoEntityStatementsView
91.13% covered (success)
91.13%
267 / 293
72.22% covered (warning)
72.22%
13 / 18
53.89
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 getHtml
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 getHtmlContainerClass
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStatementFormatValueCache
70.59% covered (warning)
70.59%
12 / 17
0.00% covered (danger)
0.00%
0 / 1
4.41
 getSnakFormatValueCache
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
3
 getValueFormatValueCache
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 getLayoutForProperty
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
1 / 1
3
 createPropertyHeader
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
4
 createFormattedDataValue
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 createStatementDiv
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 innerStatementDiv
60.47% covered (warning)
60.47%
26 / 43
0.00% covered (danger)
0.00%
0 / 1
8.22
 renderSnakList
97.37% covered (success)
97.37%
37 / 38
0.00% covered (danger)
0.00%
0 / 1
6
 formatSnak
83.33% covered (warning)
83.33%
10 / 12
0.00% covered (danger)
0.00%
0 / 1
4.07
 formatValue
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 formatEntityId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 statementsByPropertyId
95.45% covered (success)
95.45%
21 / 22
0.00% covered (danger)
0.00%
0 / 1
5
 getOrderedStatementsByProperty
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
4
 addDefaultStatements
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace Wikibase\MediaInfo\View;
4
5use DataValues\DataValue;
6use DataValues\Serializers\DataValueSerializer;
7use Exception;
8use MediaWiki\Html\Html;
9use MediaWiki\Output\OutputPage;
10use OOUI\HtmlSnippet;
11use OOUI\PanelLayout;
12use OOUI\Tag;
13use ValueFormatters\FormatterOptions;
14use ValueFormatters\ValueFormatter;
15use Wikibase\DataModel\Entity\EntityId;
16use Wikibase\DataModel\Entity\EntityIdValue;
17use Wikibase\DataModel\Entity\NumericPropertyId;
18use Wikibase\DataModel\Serializers\SerializerFactory;
19use Wikibase\DataModel\Snak\PropertyNoValueSnak;
20use Wikibase\DataModel\Snak\PropertySomeValueSnak;
21use Wikibase\DataModel\Snak\PropertyValueSnak;
22use Wikibase\DataModel\Snak\Snak;
23use Wikibase\DataModel\Snak\SnakList;
24use Wikibase\DataModel\Snak\SnakObject;
25use Wikibase\DataModel\Statement\Statement;
26use Wikibase\DataModel\Statement\StatementList;
27use Wikibase\Lib\Formatters\OutputFormatSnakFormatterFactory;
28use Wikibase\Lib\Formatters\OutputFormatValueFormatterFactory;
29use Wikibase\Lib\Formatters\SnakFormatter;
30use Wikibase\Lib\Store\PropertyOrderProvider;
31use Wikibase\MediaInfo\DataModel\MediaInfo;
32use Wikibase\View\LocalizedTextProvider;
33
34/**
35 * Generates HTML to display the statements of a MediaInfo entity
36 *
37 * @license GPL-2.0-or-later
38 */
39class MediaInfoEntityStatementsView {
40
41    private $propertyOrderProvider;
42    private $textProvider;
43    private $defaultPropertyIds;
44    private $snakFormatterFactory;
45    private $valueFormatterFactory;
46    private $serializerFactory;
47    private $languageCode;
48    private $properties;
49
50    public const STATEMENTS_CUSTOM_TAG = 'mediaInfoViewStatements';
51
52    /**
53     * @param PropertyOrderProvider $propertyOrderProvider
54     * @param LocalizedTextProvider $textProvider
55     * @param NumericPropertyId[] $defaultPropertyIds Default values are displayed for these properties if
56     *     we don't have values for them
57     * @param OutputFormatSnakFormatterFactory $snakFormatterFactory
58     * @param OutputFormatValueFormatterFactory $valueFormatterFactory
59     * @param SerializerFactory $serializerFactory
60     * @param string $languageCode
61     * @param string[] $properties Array of property IDs
62     */
63    public function __construct(
64        PropertyOrderProvider $propertyOrderProvider,
65        LocalizedTextProvider $textProvider,
66        array $defaultPropertyIds,
67        OutputFormatSnakFormatterFactory $snakFormatterFactory,
68        OutputFormatValueFormatterFactory $valueFormatterFactory,
69        SerializerFactory $serializerFactory,
70        $languageCode,
71        $properties
72    ) {
73        OutputPage::setupOOUI();
74
75        $this->propertyOrderProvider = $propertyOrderProvider;
76        $this->textProvider = $textProvider;
77        $this->defaultPropertyIds = $defaultPropertyIds;
78        $this->snakFormatterFactory = $snakFormatterFactory;
79        $this->valueFormatterFactory = $valueFormatterFactory;
80        $this->serializerFactory = $serializerFactory;
81        $this->languageCode = $languageCode;
82        $this->properties = $properties;
83    }
84
85    /**
86     * @param MediaInfo $entity
87     * @return string
88     */
89    public function getHtml( MediaInfo $entity ) {
90        $statements = $this->statementsByPropertyId( $entity->getStatements() );
91
92        $html = '';
93        foreach ( $statements as $propertyId => $statementArray ) {
94            $panel = $this->getLayoutForProperty( $propertyId, $statementArray );
95            $html .= $panel->toString();
96        }
97
98        // Wrap the whole thing in a custom tag so we can manipulate its position on the page
99        // later on
100        return Html::rawElement(
101            self::STATEMENTS_CUSTOM_TAG,
102            [],
103            $html
104        );
105    }
106
107    public static function getHtmlContainerClass( $propertyIdString ) {
108        return 'wbmi-entityview-statementsGroup-' . str_replace( ':', '_', $propertyIdString );
109    }
110
111    /**
112     * @param Statement $statement
113     * @return array
114     */
115    private function getStatementFormatValueCache( Statement $statement ) {
116        $results = [];
117
118        $results = array_replace_recursive(
119            $results,
120            $this->getSnakFormatValueCache( $statement->getMainSnak() )
121        );
122
123        foreach ( $statement->getQualifiers() as $qualifier ) {
124            $results = array_replace_recursive(
125                $results,
126                $this->getSnakFormatValueCache( $qualifier )
127            );
128        }
129
130        foreach ( $statement->getReferences() as $reference ) {
131            foreach ( $reference->getSnaks() as $snak ) {
132                $results = array_replace_recursive(
133                    $results,
134                    $this->getSnakFormatValueCache( $snak )
135                );
136            }
137        }
138
139        return $results;
140    }
141
142    /**
143     * @param Snak $snak
144     * @return array
145     */
146    private function getSnakFormatValueCache( Snak $snak ) {
147        $result = [];
148
149        // format property
150        if ( $snak instanceof SnakObject ) {
151            $dataValue = new EntityIdValue( $snak->getPropertyId() );
152            $result = array_replace_recursive(
153                $result,
154                $this->getValueFormatValueCache(
155                    $dataValue,
156                    [
157                        SnakFormatter::FORMAT_HTML,
158                        SnakFormatter::FORMAT_PLAIN,
159                    ]
160                )
161            );
162        }
163
164        // format value
165        if ( $snak instanceof PropertyValueSnak ) {
166            $serializer = new DataValueSerializer();
167            $serialized = $serializer->serialize( $snak->getDataValue() );
168
169            $data = json_encode( $serialized );
170            $property = $snak->getPropertyId()->getSerialization();
171            $html = $this->formatSnak( $snak, SnakFormatter::FORMAT_HTML );
172            $plain = $this->formatSnak( $snak, SnakFormatter::FORMAT_PLAIN );
173
174            $result[$data][SnakFormatter::FORMAT_HTML][$this->languageCode][$property] = $html;
175            $result[$data][SnakFormatter::FORMAT_PLAIN][$this->languageCode][$property] = $plain;
176        }
177
178        return $result;
179    }
180
181    /**
182     * @param DataValue $value
183     * @param string[] $formats
184     * @return array
185     */
186    private function getValueFormatValueCache( DataValue $value, $formats = [ SnakFormatter::FORMAT_PLAIN ] ) {
187        $result = [];
188
189        $serializer = new DataValueSerializer();
190        $serialized = $serializer->serialize( $value );
191
192        $data = json_encode( $serialized );
193        foreach ( $formats as $format ) {
194            $result[$data][$format][$this->languageCode][''] = $this->formatValue( $value, $format );
195        }
196
197        return $result;
198    }
199
200    /**
201     * @param string $propertyIdString
202     * @param Statement[] $statements
203     * @return PanelLayout
204     */
205    private function getLayoutForProperty( $propertyIdString, array $statements ) {
206        $statementSerializer = $this->serializerFactory->newStatementSerializer();
207
208        $serializedStatements = [];
209        $formatValueCache = [];
210
211        $itemsGroupDiv = new Tag( 'div' );
212        $itemsGroupDiv->addClasses( [ 'wbmi-content-items-group' ] );
213        foreach ( $statements as $statement ) {
214            $itemsGroupDiv->appendContent( $this->createStatementDiv( $statement ) );
215            $serializedStatements[] = $statementSerializer->serialize( $statement );
216            $formatValueCache = array_replace_recursive(
217                $formatValueCache,
218                $this->getStatementFormatValueCache( $statement )
219            );
220        }
221
222        // Format main property (e.g. depicts).
223        if ( $statements ) {
224            $formatValueCache = array_replace_recursive(
225                $formatValueCache,
226                $this->getValueFormatValueCache(
227                    // @phan-suppress-next-line PhanPossiblyUndeclaredVariable
228                    new EntityIdValue( $statement->getPropertyId() ),
229                    [ SnakFormatter::FORMAT_HTML ]
230                )
231            );
232        }
233
234        $panelClasses = [
235            'wbmi-entityview-statementsGroup',
236            self::getHtmlContainerClass( $propertyIdString ),
237        ];
238
239        $panel = new PanelLayout( [
240            'classes' => $panelClasses,
241            'id' => $propertyIdString,
242            'scrollable' => false,
243            'padded' => false,
244            'expanded' => false,
245            'framed' => true,
246            'content' => [
247                ( new Tag( 'div' ) )
248                    ->addClasses( [ 'wbmi-statements-widget' ] )
249                    ->appendContent(
250                        $this->createPropertyHeader( $propertyIdString ),
251                        $itemsGroupDiv
252                    ),
253            ]
254        ] );
255        $panel->setAttributes(
256            [
257                'data-property' => $propertyIdString,
258                'data-statements' => json_encode( $serializedStatements ),
259                'data-formatvalue' => json_encode( $formatValueCache ),
260            ]
261        );
262        return $panel;
263    }
264
265    private function createPropertyHeader( $propertyIdString ) {
266        $propertyId = new NumericPropertyId( $propertyIdString );
267
268        $propertyTitle = $this->createFormattedDataValue(
269            new HtmlSnippet( $this->formatEntityId( $propertyId, SnakFormatter::FORMAT_HTML ) )
270        );
271
272        /*
273         * Here's quite an odd way to render a title...
274         * We want to keep this place mostly generic, so that it doesn't
275         * really matter what property we're dealing with. `Depicts` (or
276         * any other) is not different from the next.
277         * However, it looks like we (may) want to have specific titles
278         * (https://phabricator.wikimedia.org/T216757 asks for one for
279         * depicts on commons)
280         * Instead of hardcoding this, let's just see if a message exist
281         * that uses the descriptive name that was used for the property
282         * ID in extension.json: for a property { depicts: P1 }, we'll
283         * see if we can find an image with i18n key
284         * wikibasemediainfo-statements-title-depicts, and if so, display
285         * a title. If not, no title... This allows any wiki to set up
286         * any property/statement group with any title, or none at all.
287         */
288        $name = array_search( $propertyIdString, $this->properties );
289        // possible messages include:
290        // wikibasemediainfo-statements-title-depicts
291        $message = wfMessage( 'wikibasemediainfo-statements-title-' . ( $name ?: '' ) );
292        if ( $name !== false && $message->exists() ) {
293            $title = new Tag( 'h3' );
294            $title->addClasses( [ 'wbmi-statements-title' ] );
295            $title->appendContent( $message->text() );
296
297            $propertyTitle->prependContent( $title );
298        }
299
300        $header = new Tag( 'div' );
301        $header->addClasses( [ 'wbmi-statement-header' ] );
302        $header->appendContent(
303            ( new Tag( 'div' ) )
304                ->addClasses( [ 'wbmi-entity-data' ] )
305                ->appendContent( $propertyTitle )
306        );
307
308        return $header;
309    }
310
311    /**
312     * @param string $formattedValue
313     * @return Tag
314     */
315    private function createFormattedDataValue( $formattedValue ) {
316        // Wrap value in a <bdi> tag (bi-directional isolate) so that things
317        // like coordinate strings (with both numerical and non-numerical
318        // symbols) do not get mangled when flipped to RTL
319        $bdi = new Tag( 'bdi' );
320        $bdi->appendContent( $formattedValue );
321
322        $label = new Tag( 'h4' );
323        $label->addClasses( [ 'wbmi-entity-label' ] );
324        $label->appendContent( $bdi );
325
326        $tag = new Tag( 'div' );
327        $tag->addClasses( [ 'wbmi-entity-title' ] );
328        $tag->appendContent( $label );
329
330        return $tag;
331    }
332
333    private function createStatementDiv( Statement $statement ) {
334        $div = new Tag( 'div' );
335        $div->appendContent( $this->innerStatementDiv( $statement ) );
336        $div->addClasses( [ 'wbmi-item', 'wbmi-item-read' ] );
337        return $div;
338    }
339
340    private function innerStatementDiv( Statement $statement ) {
341        $mainSnak = $statement->getMainSnak();
342
343        $statementDiv = new Tag( 'div' );
344        $statementDiv->addClasses( [ 'wbmi-item-container' ] );
345
346        $guid = $statement->getGuid();
347        if ( $guid !== null ) {
348            $statementDiv->setAttributes( [ 'data-guid' => $guid ] );
349        }
350
351        $mainSnakDiv = new Tag( 'div' );
352        $mainSnakDiv->addClasses( [ 'wbmi-entity-header' ] );
353        $mainSnakDiv->appendContent(
354            ( new Tag( 'div' ) )
355                ->addClasses( [ 'wbmi-entity-data' ] )
356                ->appendContent(
357                    $this->createFormattedDataValue(
358                        new HtmlSnippet( $this->formatSnak( $mainSnak, SnakFormatter::FORMAT_HTML ) )
359                    )
360                )
361        );
362
363        $statementDiv->appendContent( $mainSnakDiv );
364
365        $qualifiers = $statement->getQualifiers();
366        if ( count( $qualifiers ) > 0 ) {
367            $qualifiersContainer = new Tag( 'div' );
368            $qualifiersContainer->addClasses( [ 'wbmi-item-qualifiers' ] );
369            $qualifiersContainer->appendContent( $this->renderSnakList( $qualifiers ) );
370
371            $statementDiv->appendContent( $qualifiersContainer );
372        }
373
374        $referenceList = $statement->getReferences();
375        if ( count( $referenceList ) > 0 ) {
376            $referencesContainer = new Tag( 'div' );
377            $referencesContainer->addClasses( [ 'wbmi-item-references' ] );
378
379            foreach ( $referenceList as $reference ) {
380                $snakList = $reference->getSnaks();
381                if ( count( $snakList ) > 0 ) {
382                    $referenceSnaklist = new Tag( 'div' );
383                    $referenceSnaklist->addClasses( [ 'wbmi-snaklist' ] );
384                    $referencesContainer->appendContent( $referenceSnaklist );
385
386                    $referenceTitle = new Tag( 'h5' );
387                    $referenceTitle->addClasses( [ 'wbmi-snaklist-title' ] );
388                    $referenceTitle->appendContent(
389                        wfMessage( 'wikibasemediainfo-statements-item-reference' )->escaped()
390                    );
391                    $referenceSnaklist->appendContent( $referenceTitle );
392
393                    // @phan-suppress-next-line PhanTypeMismatchArgumentSuperType
394                    $referenceSnaklist->appendContent( $this->renderSnakList( $snakList ) );
395                }
396            }
397
398            $statementDiv->appendContent( $referencesContainer );
399        }
400
401        return $statementDiv;
402    }
403
404    private function renderSnakList( SnakList $snakList ) {
405        $propertyOrder = $this->propertyOrderProvider->getPropertyOrder();
406        if ( $propertyOrder === null ) {
407            $snakList->orderByProperty();
408        } else {
409            $propertyIds = array_flip( $propertyOrder );
410            ksort( $propertyIds );
411            $snakList->orderByProperty(
412                array_values( $propertyIds )
413            );
414        }
415
416        $snakDivs = [];
417
418        /** @var Snak $snak */
419        foreach ( $snakList as $snak ) {
420            $formattedProperty = '';
421            if ( $snak instanceof SnakObject ) {
422                $formattedProperty = $this->formatEntityId( $snak->getPropertyId(), SnakFormatter::FORMAT_HTML );
423            }
424
425            $formattedValue = $this->formatSnak( $snak, SnakFormatter::FORMAT_HTML );
426            $formattedValueTag = new Tag( 'span' );
427            $formattedValueTag->addClasses( [ 'wbmi-snak-value--value' ] );
428            $formattedValueTag->appendContent( new HtmlSnippet( $formattedValue ) );
429
430            $separator = new Tag( 'span' );
431            $separator->addClasses( [ 'wbmi-snak-value-separator' ] );
432            $separator->appendContent( $this->textProvider->get( 'colon-separator' ) );
433
434            $snakValueDiv = new Tag( 'div' );
435            $snakValueDiv->addClasses( [ 'wbmi-snak-value' ] );
436            $snakValueDiv->appendContent(
437                new HtmlSnippet( $formattedProperty ),
438                // if we have both a property & a value, add a separator
439                $formattedProperty && $formattedValue ? $separator : '',
440                $formattedValueTag
441            );
442
443            $snakDiv = new Tag( 'div' );
444            $snakDiv->addClasses( [ 'wbmi-snak' ] );
445            $snakDiv->appendContent( $snakValueDiv );
446
447            $snakDivs[] = $snakDiv;
448        }
449
450        $snakListDiv = new Tag( 'div' );
451        $snakListDiv->addClasses( [ 'wbmi-snaklist-content' ] );
452        $snakListDiv->appendContent( ...$snakDivs );
453
454        $snakListContainer = new Tag( 'div' );
455        $snakListContainer->addClasses( [ 'wbmi-snaklist-container' ] );
456        $snakListContainer->appendContent( $snakListDiv );
457
458        return $snakListContainer;
459    }
460
461    /**
462     * @param Snak $snak
463     * @param string $format
464     * @return string
465     */
466    private function formatSnak( Snak $snak, $format = SnakFormatter::FORMAT_PLAIN ) {
467        if ( $snak instanceof PropertyNoValueSnak ) {
468            return wfMessage( 'wikibasemediainfo-filepage-statement-no-value' )->escaped();
469        }
470
471        if ( $snak instanceof PropertySomeValueSnak ) {
472            return wfMessage( 'wikibasemediainfo-filepage-statement-some-value' )->escaped();
473        }
474
475        $formatter = $this->snakFormatterFactory->getSnakFormatter(
476            $format,
477            new FormatterOptions( [ ValueFormatter::OPT_LANG => $this->languageCode ] )
478        );
479
480        try {
481            $formatted = $formatter->formatSnak( $snak );
482
483            // if there are any links inside the formatted content, make them open in a new window
484            return preg_replace( '/<a/', '<a target="_blank"', $formatted );
485        } catch ( Exception $e ) {
486            return wfMessage( 'wikibasemediainfo-filepage-statement-invalid-value' )->escaped();
487        }
488    }
489
490    /**
491     * @param DataValue $value
492     * @param string $format
493     * @return string
494     */
495    private function formatValue( DataValue $value, $format = SnakFormatter::FORMAT_PLAIN ) {
496        $formatter = $this->valueFormatterFactory->getValueFormatter(
497            $format,
498            new FormatterOptions( [ ValueFormatter::OPT_LANG => $this->languageCode ] )
499        );
500
501        $formatted = $formatter->formatValue( $value );
502
503        // if there are any links inside the formatted content, make them open in a new window
504        return preg_replace( '/<a/', '<a target="_blank"', $formatted );
505    }
506
507    /**
508     * @param EntityId $entityId
509     * @param string $format
510     * @return string
511     */
512    private function formatEntityId( EntityId $entityId, $format = SnakFormatter::FORMAT_PLAIN ) {
513        return $this->formatValue( new EntityIdValue( $entityId ), $format );
514    }
515
516    /**
517     * Gather statements into an array with property ids (ordered by $this->propertyOrderProvider)
518     * as keys, and arrays of statements pertaining to those property ids (ordered by rank) as
519     * values
520     *
521     * @param StatementList $statementList
522     * @return array[]
523     */
524    private function statementsByPropertyId( StatementList $statementList ) {
525        $statementsByProperty = $this->getOrderedStatementsByProperty( $statementList );
526
527        $propertyOrder = $this->propertyOrderProvider->getPropertyOrder();
528
529        if ( !$propertyOrder ) {
530            return $statementsByProperty;
531        }
532
533        $ordered = [];
534        $unordered = [];
535
536        foreach ( $statementsByProperty as $propertyId => $statements ) {
537            if ( isset( $propertyOrder[$propertyId] ) ) {
538                $ordered[$propertyOrder[$propertyId]] = [
539                    'propertyId' => $propertyId,
540                    'statements' => $statements
541                ];
542            } else {
543                $unordered[] = [
544                    'propertyId' => $propertyId,
545                    'statements' => $statements
546                ];
547            }
548        }
549
550        ksort( $ordered );
551        $orderedButNotIndexed = array_merge( $ordered, $unordered );
552        $orderedAndIndexed = [];
553        foreach ( $orderedButNotIndexed  as $statementArray ) {
554            $orderedAndIndexed[ $statementArray[ 'propertyId' ] ] = $statementArray[ 'statements' ];
555        }
556        return $orderedAndIndexed;
557    }
558
559    /**
560     * Returns array with property ids as keys and arrays of statements as elements. The arrays
561     * of statements are ordered by rank.
562     *
563     * @param StatementList $statementList
564     * @return array[]
565     */
566    private function getOrderedStatementsByProperty( StatementList $statementList ) {
567        $statementsByPropertyAndRank = [];
568        foreach ( $statementList as $statement ) {
569            $propertyId = $statement->getPropertyId()->getSerialization();
570            if ( !isset( $statementsByPropertyAndRank[$propertyId] ) ) {
571                $statementsByPropertyAndRank[$propertyId] = [
572                    Statement::RANK_PREFERRED => [],
573                    Statement::RANK_NORMAL => [],
574                    Statement::RANK_DEPRECATED => [],
575                ];
576            }
577            $rank = $statement->getRank();
578            $statementsByPropertyAndRank[$propertyId][$rank][] = $statement;
579        }
580
581        $statementsByProperty = [];
582        foreach ( $statementsByPropertyAndRank as $propertyId => $array ) {
583            $statementsByProperty[$propertyId] = array_merge(
584                $array[Statement::RANK_PREFERRED],
585                $array[Statement::RANK_NORMAL],
586                $array[Statement::RANK_DEPRECATED]
587            );
588        }
589
590        $statementsByProperty = $this->addDefaultStatements( $statementsByProperty );
591
592        return $statementsByProperty;
593    }
594
595    private function addDefaultStatements( $statementsByProperty ) {
596        foreach ( $this->defaultPropertyIds as $propertyId ) {
597            if ( !isset( $statementsByProperty[ $propertyId->getSerialization() ] ) ) {
598                $statementsByProperty[ $propertyId->getSerialization() ] = [];
599            }
600        }
601        return $statementsByProperty;
602    }
603
604}