wikimedia/mediawiki-extensions-Wikibase

View on GitHub
lib/includes/Formatters/QuantityDetailsFormatter.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace Wikibase\Lib\Formatters;

use DataValues\DecimalValue;
use DataValues\QuantityValue;
use DataValues\UnboundedQuantityValue;
use InvalidArgumentException;
use MediaWiki\Html\Html;
use MediaWiki\Message\Message;
use ValueFormatters\DecimalFormatter;
use ValueFormatters\FormatterOptions;
use ValueFormatters\NumberLocalizer;
use ValueFormatters\QuantityFormatter;
use ValueFormatters\ValueFormatter;

/**
 * Formatter for rendering the details of a QuantityValue (most useful for diffs) in HTML.
 *
 * @license GPL-2.0-or-later
 * @author Daniel Kinzler
 * @author Thiemo Kreuz
 */
class QuantityDetailsFormatter implements ValueFormatter {

    /**
     * @var QuantityFormatter
     */
    private $quantityFormatter;

    /**
     * @var QuantityFormatter
     */
    private $numberFormatter;

    /**
     * @var ValueFormatter
     */
    private $vocabularyUriFormatter;

    /**
     * @var FormatterOptions
     */
    private $options;

    /**
     * @param NumberLocalizer|null $numberLocalizer
     * @param ValueFormatter $vocabularyUriFormatter
     * @param FormatterOptions|null $options
     */
    public function __construct(
        ?NumberLocalizer $numberLocalizer,
        ValueFormatter $vocabularyUriFormatter,
        FormatterOptions $options = null
    ) {
        $this->options = $options ?: new FormatterOptions();
        $this->options->defaultOption( ValueFormatter::OPT_LANG, 'en' );

        $decimalFormatter = new DecimalFormatter( $this->options, $numberLocalizer );
        $this->vocabularyUriFormatter = $vocabularyUriFormatter;

        $this->quantityFormatter = new QuantityFormatter(
            $this->options,
            $decimalFormatter,
            $this->vocabularyUriFormatter
        );

        $this->numberFormatter = new QuantityFormatter(
            new FormatterOptions( [
                QuantityFormatter::OPT_SHOW_UNCERTAINTY_MARGIN => false,
                QuantityFormatter::OPT_APPLY_ROUNDING => false,
            ] ),
            $decimalFormatter,
            $this->vocabularyUriFormatter
        );
    }

    /**
     * @see ValueFormatter::format
     *
     * Generates HTML representing the details of a QuantityValue,
     * as an itemized list.
     *
     * @param UnboundedQuantityValue|QuantityValue $value
     *
     * @throws InvalidArgumentException
     * @return string HTML
     */
    public function format( $value ) {
        if ( !( $value instanceof UnboundedQuantityValue ) ) {
            throw new InvalidArgumentException( 'Data value type mismatch. Expected a UnboundedQuantityValue.' );
        }

        $html = '';
        $html .= Html::element( 'b',
            [ 'class' => 'wb-details wb-quantity-details wb-quantity-rendered' ],
            $this->quantityFormatter->format( $value )
        );

        $html .= Html::openElement( 'table',
            [ 'class' => 'wb-details wb-quantity-details' ] );

        $html .= $this->renderLabelValuePair( 'amount',
            $this->formatNumber( $value->getAmount(), $value->getUnit() ) );
        if ( $value instanceof QuantityValue ) {
            $html .= $this->renderLabelValuePair( 'upperBound',
                $this->formatNumber( $value->getUpperBound(), $value->getUnit() ) );
            $html .= $this->renderLabelValuePair( 'lowerBound',
                $this->formatNumber( $value->getLowerBound(), $value->getUnit() ) );
        }
        /**
         * @todo Display URIs to entities in the local repository as clickable labels.
         * @todo Display URIs that start with http:// or https:// as clickable links.
         * @todo Mark "unitless" units somehow, e.g. via CSS or with an appended message.
         * @see WikibaseValueFormatterBuilders::$unitOneUris
         */
        $html .= $this->renderLabelValuePair( 'unit', $this->formatUnit( $value->getUnit() ) );

        $html .= Html::closeElement( 'table' );

        return $html;
    }

    /**
     * @param DecimalValue $number
     * @param string $unit URI
     *
     * @return string HTML
     */
    private function formatNumber( DecimalValue $number, $unit ) {
        return htmlspecialchars( $this->numberFormatter->format(
            new UnboundedQuantityValue( $number, $unit )
        ) );
    }

    /**
     * @param string $unit URI
     *
     * @return string HTML
     */
    private function formatUnit( $unit ) {
        $formattedUnit = $this->vocabularyUriFormatter->format( $unit );

        if ( $formattedUnit === null || $formattedUnit === $unit ) {
            return htmlspecialchars( $unit );
        }

        return Html::element( 'a', [ 'href' => $unit ], $formattedUnit );
    }

    /**
     * @param string $fieldName
     * @param string $valueHtml HTML
     *
     * @return string HTML for the label/value pair
     */
    private function renderLabelValuePair( $fieldName, $valueHtml ) {
        $html = Html::openElement( 'tr' );

        $html .= Html::element( 'th', [ 'class' => 'wb-quantity-' . $fieldName ],
            $this->getFieldLabel( $fieldName )->text() );
        $html .= Html::rawElement( 'td', [ 'class' => 'wb-quantity-' . $fieldName ],
            $valueHtml );

        $html .= Html::closeElement( 'tr' );
        return $html;
    }

    /**
     * @param string $fieldName
     *
     * @return Message
     */
    private function getFieldLabel( $fieldName ) {
        $lang = $this->options->getOption( ValueFormatter::OPT_LANG );

        // Messages:
        // wikibase-quantitydetails-amount
        // wikibase-quantitydetails-upperbound
        // wikibase-quantitydetails-lowerbound
        // wikibase-quantitydetails-unit
        $key = 'wikibase-quantitydetails-' . strtolower( $fieldName );
        $msg = wfMessage( $key )->inLanguage( $lang );

        return $msg;
    }

}