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