Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.00% covered (success)
94.00%
47 / 50
81.25% covered (warning)
81.25%
13 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
SenseSet
94.00% covered (success)
94.00%
47 / 50
81.25% covered (warning)
81.25%
13 / 16
28.17
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 toArray
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 toArrayUnordered
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 sortSenses
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 count
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 maxSenseIdNumber
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 add
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 remove
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 put
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getById
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 copy
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __clone
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 isEmpty
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 equals
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 hasSenseWithId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 sameSenses
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
4.07
1<?php
2
3namespace Wikibase\Lexeme\Domain\Model;
4
5use Countable;
6use InvalidArgumentException;
7use Wikibase\Lexeme\Domain\Model\Exceptions\ConflictException;
8
9/**
10 * Set of Senses in which uniqueness of a Sense is controlled by its ID.
11 * Supposed to be used only inside the Lexeme class.
12 *
13 * @license GPL-2.0-or-later
14 */
15class SenseSet implements Countable {
16
17    /**
18     * @var Sense[] indexed by serialization of SenseId
19     */
20    private $senses = [];
21
22    /**
23     * @param Sense[] $senses
24     */
25    public function __construct( array $senses = [] ) {
26        foreach ( $senses as $sense ) {
27            if ( !$sense instanceof Sense ) {
28                throw new InvalidArgumentException( '$senses must be an array of Senses' );
29            }
30
31            $this->add( $sense );
32        }
33    }
34
35    /**
36     * @return Sense[]
37     */
38    public function toArray() {
39        $senses = $this->sortSenses( $this->senses );
40        return array_values( $senses );
41    }
42
43    /**
44     * Return the individual Senses in arbitrary order.
45     *
46     * Only use this method if the order is certainly insignificant,
47     * e.g. because the Senses will be summarized or reduced in some way.
48     * Otherwise, use {@link toArray()}.
49     *
50     * @return Sense[]
51     */
52    public function toArrayUnordered(): array {
53        return array_values( $this->senses );
54    }
55
56    /**
57     * @param Sense[] $senses
58     * @return array sorted array mapping numeric id to the sense
59     */
60    private function sortSenses( array $senses ) {
61        $sortedSenses = [];
62        foreach ( $senses as $sense ) {
63            $senseIdPart = explode( '-', $sense->getId()->getSerialization(), 2 )[1];
64            $senseIdNumber = (int)substr( $senseIdPart, 1 );
65            $sortedSenses[$senseIdNumber] = $sense;
66        }
67        ksort( $sortedSenses );
68
69        return $sortedSenses;
70    }
71
72    /**
73     * @return int
74     */
75    public function count(): int {
76        return count( $this->senses );
77    }
78
79    /**
80     * @return int
81     */
82    public function maxSenseIdNumber() {
83        $max = 0;
84
85        foreach ( $this->senses as $senseId => $sense ) {
86            $senseIdPart = explode( '-', $senseId, 2 )[1];
87            $senseIdNumber = (int)substr( $senseIdPart, 1 );
88            if ( $senseIdNumber > $max ) {
89                $max = $senseIdNumber;
90            }
91        }
92
93        return $max;
94    }
95
96    public function add( Sense $sense ) {
97        $senseId = $sense->getId()->getSerialization();
98        if ( array_key_exists( $senseId, $this->senses ) ) {
99            throw new ConflictException(
100                'At least two senses with the same ID were provided: `' . $senseId . '`'
101            );
102        }
103
104        $this->senses[$senseId] = $sense;
105    }
106
107    public function remove( SenseId $senseId ) {
108        unset( $this->senses[$senseId->getSerialization()] );
109    }
110
111    /**
112     * Replace the sense identified by $sense->getId() with the given one or add it.
113     *
114     * @param Sense $sense
115     */
116    public function put( Sense $sense ) {
117        $this->remove( $sense->getId() );
118        $this->add( $sense );
119    }
120
121    /**
122     * @param SenseId $senseId
123     *
124     * @return Sense|null
125     */
126    public function getById( SenseId $senseId ) {
127        return $this->senses[$senseId->getSerialization()] ?? null;
128    }
129
130    /**
131     * @return self
132     */
133    public function copy() {
134        return clone $this;
135    }
136
137    /**
138     * @see http://php.net/manual/en/language.oop5.cloning.php
139     */
140    public function __clone() {
141        $clonedSenses = [];
142        foreach ( $this->senses as $key => $sense ) {
143            $clonedSenses[$key] = clone $sense;
144        }
145        $this->senses = $clonedSenses;
146    }
147
148    /**
149     * @return bool
150     */
151    public function isEmpty() {
152        return $this->senses === [];
153    }
154
155    public function equals( $other ) {
156        if ( $this === $other ) {
157            return true;
158        }
159
160        if ( !( $other instanceof self ) ) {
161            return false;
162        }
163
164        return $this->sameSenses( $other );
165    }
166
167    public function hasSenseWithId( SenseId $id ): bool {
168        return $this->getById( $id ) !== null;
169    }
170
171    /**
172     * @return bool
173     */
174    private function sameSenses( SenseSet $other ) {
175        if ( $this->count() !== $other->count() ) {
176            return false;
177        }
178
179        foreach ( $this->senses as $sense ) {
180            if ( !$sense->equals( $other->getById( $sense->getId() ) ) ) {
181                return false;
182            }
183        }
184
185        return true;
186    }
187
188}