Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
67.57% covered (warning)
67.57%
25 / 37
58.33% covered (warning)
58.33%
7 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
CoordinatesOutput
67.57% covered (warning)
67.57%
25 / 37
58.33% covered (warning)
58.33%
7 / 12
33.65
0.00% covered (danger)
0.00%
0 / 1
 getOrBuildFromParserOutput
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setToParserOutput
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFromParserOutput
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 getCount
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 addPrimary
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 addSecondary
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getPrimary
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasPrimary
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSecondary
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAll
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 jsonSerialize
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 newFromJson
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace GeoData;
4
5use InvalidArgumentException;
6use JsonSerializable;
7use LogicException;
8use MediaWiki\Parser\ParserOutput;
9use Wikimedia\Assert\Assert;
10use Wikimedia\Parsoid\Core\ContentMetadataCollector;
11
12/**
13 * Class that holds output of a parse opertion
14 */
15class CoordinatesOutput implements JsonSerializable {
16    /**
17     * Key used to store this object in the ParserOutput extension data.
18     * Visible for testing only.
19     */
20    public const GEO_DATA_COORDS_OUTPUT = 'GeoDataCoordsOutput';
21
22    public bool $limitExceeded = false;
23    private ?Coord $primary = null;
24    /** @var Coord[] */
25    private array $secondary = [];
26
27    /**
28     * Fetch the current CoordinatesOutput attached to this ParserOutput
29     * or create a new one.
30     *
31     * @note The changes made to the CoordinatesOutput object are not stored
32     * back into the ParserOutput until self::setToParserOutput is called.
33     *
34     * @see setToParserOutput
35     */
36    public static function getOrBuildFromParserOutput(
37        ParserOutput $parserOutput
38    ): self {
39        return self::getFromParserOutput( $parserOutput ) ?? new self();
40    }
41
42    /**
43     * Write the coords to ParserOutput object.
44     */
45    public function setToParserOutput( ContentMetadataCollector $parserOutput ) {
46        $parserOutput->setExtensionData( self::GEO_DATA_COORDS_OUTPUT, $this->jsonSerialize() );
47    }
48
49    /**
50     * Get the CoordinatesOutput attached to this ParserOutput
51     * @param ParserOutput $parserOutput
52     * @return self|null existing CoordinatesOutput or null
53     */
54    public static function getFromParserOutput( ParserOutput $parserOutput ) {
55        $coordsOutput = $parserOutput->getExtensionData( self::GEO_DATA_COORDS_OUTPUT );
56        if ( $coordsOutput !== null ) {
57            if ( is_array( $coordsOutput ) ) {
58                $coordsOutput = self::newFromJson( $coordsOutput );
59            }
60            Assert::invariant( $coordsOutput instanceof self,
61                'ParserOutput extension data ' . self::GEO_DATA_COORDS_OUTPUT .
62                ' must be an instance of CoordinatesOutput' );
63        }
64        return $coordsOutput;
65    }
66
67    public function getCount(): int {
68        return count( $this->secondary ) + ( $this->primary ? 1 : 0 );
69    }
70
71    /**
72     * Sets primary coordinates, throwing an exception if already set
73     *
74     * @param Coord $c
75     * @throws LogicException
76     */
77    public function addPrimary( Coord $c ): void {
78        if ( $this->primary ) {
79            throw new LogicException( 'Primary coordinates already set' );
80        }
81        $this->primary = $c;
82    }
83
84    /**
85     * @param Coord $c
86     * @throws InvalidArgumentException
87     */
88    public function addSecondary( Coord $c ): void {
89        if ( $c->primary ) {
90            throw new InvalidArgumentException( 'Attempt to pass primary coordinates as secondary' );
91        }
92        $this->secondary[] = $c;
93    }
94
95    public function getPrimary(): ?Coord {
96        return $this->primary;
97    }
98
99    /**
100     * @return bool Whether this output has primary coordinates
101     */
102    public function hasPrimary(): bool {
103        return (bool)$this->primary;
104    }
105
106    /**
107     * @return Coord[]
108     */
109    public function getSecondary(): array {
110        return $this->secondary;
111    }
112
113    /**
114     * @return Coord[]
115     */
116    public function getAll(): array {
117        $res = $this->secondary;
118        if ( $this->primary ) {
119            array_unshift( $res, $this->primary );
120        }
121        return $res;
122    }
123
124    public function jsonSerialize(): array {
125        return [
126            'limitExceeded' => $this->limitExceeded,
127            'primary' => $this->primary ? $this->primary->jsonSerialize() : $this->primary,
128            'secondary' => array_map(
129                static fn ( Coord $coord ) => $coord->jsonSerialize(),
130                $this->secondary
131            )
132        ];
133    }
134
135    /**
136     * Instantiate a CoordinatesOutput from $json array created with self::jsonSerialize.
137     *
138     * @internal
139     * @param array $jsonArray
140     * @return static
141     * @see jsonSerialize
142     */
143    public static function newFromJson( array $jsonArray ): self {
144        $coordOutput = new self();
145        $coordOutput->limitExceeded = $jsonArray['limitExceeded'];
146        $coordOutput->primary = $jsonArray['primary'] ? Coord::newFromJson( $jsonArray['primary'] ) : null;
147        $coordOutput->secondary = array_map( Coord::newFromJson( ... ), $jsonArray['secondary'] );
148        return $coordOutput;
149    }
150}