Passed
Push — entity-clear ( 05e161...938e18 )
by no
03:01
created

ReferenceList::toArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Wikibase\DataModel;
4
5
use ArrayIterator;
6
use Comparable;
7
use Countable;
8
use InvalidArgumentException;
9
use IteratorAggregate;
10
use Serializable;
11
use Traversable;
12
use Wikibase\DataModel\Internal\MapValueHasher;
13
use Wikibase\DataModel\Snak\Snak;
14
15
/**
16
 * List of Reference objects.
17
 *
18
 * @since 0.1
19
 * Does not implement References anymore since 2.0
20
 * Does not extend SplObjectStorage since 5.0
21
 *
22
 * @licence GNU GPL v2+
23
 * @author Jeroen De Dauw < [email protected] >
24
 * @author H. Snater < [email protected] >
25
 * @author Thiemo Mättig
26
 * @author Bene* < [email protected] >
27
 */
28
class ReferenceList implements Comparable, Countable, IteratorAggregate, Serializable {
29
30
	/**
31
	 * @var Reference[]
32
	 */
33
	private $references = array();
34
35
	/**
36
	 * @param Reference[]|Traversable $references
37
	 *
38
	 * @throws InvalidArgumentException
39
	 */
40
	public function __construct( $references = array() ) {
41
		if ( !is_array( $references ) && !( $references instanceof Traversable ) ) {
42
			throw new InvalidArgumentException( '$references must be an array or an instance of Traversable' );
43
		}
44
45
		foreach ( $references as $reference ) {
46
			if ( !( $reference instanceof Reference ) ) {
47
				throw new InvalidArgumentException( 'Every element in $references must be an instance of Reference' );
48
			}
49
50
			$this->addReference( $reference );
51
		}
52
	}
53
54
	/**
55
	 * Adds the provided reference to the list.
56
	 * Empty references are ignored.
57
	 *
58
	 * @since 0.1
59
	 *
60
	 * @param Reference $reference
61
	 * @param int|null $index
62
	 *
63
	 * @throws InvalidArgumentException
64
	 */
65
	public function addReference( Reference $reference, $index = null ) {
66
		if ( $index !== null && ( !is_int( $index ) || $index < 0 ) ) {
67
			throw new InvalidArgumentException( '$index must be a non-negative integer or null' );
68
		}
69
70
		if ( $reference->isEmpty() ) {
71
			return;
72
		}
73
74
		if ( $index === null || $index >= count( $this->references ) ) {
75
			// Append object to the end of the reference list.
76
			$this->references[] = $reference;
77
		} else {
78
			$this->insertReferenceAtIndex( $reference, $index );
79
		}
80
	}
81
82
	/**
83
	 * @since 1.1
84
	 *
85
	 * @param Snak[]|Snak $snaks
86
	 * @param Snak [$snak2,...]
87
	 *
88
	 * @throws InvalidArgumentException
89
	 */
90
	public function addNewReference( $snaks = array() /*...*/ ) {
91
		if ( $snaks instanceof Snak ) {
92
			$snaks = func_get_args();
93
		}
94
95
		$this->addReference( new Reference( $snaks ) );
96
	}
97
98
	/**
99
	 * @param Reference $reference
100
	 * @param int $index
101
	 */
102
	private function insertReferenceAtIndex( Reference $reference, $index ) {
103
		array_splice( $this->references, $index, 0, array( $reference ) );
104
	}
105
106
	/**
107
	 * Returns if the list contains a reference with the same hash as the provided reference.
108
	 *
109
	 * @since 0.1
110
	 *
111
	 * @param Reference $reference
112
	 *
113
	 * @return boolean
114
	 */
115
	public function hasReference( Reference $reference ) {
116
		return $this->hasReferenceHash( $reference->getHash() );
117
	}
118
119
	/**
120
	 * Returns the index of a reference or false if the reference could not be found.
121
	 *
122
	 * @since 0.5
123
	 *
124
	 * @param Reference $reference
125
	 *
126
	 * @return int|boolean
127
	 */
128
	public function indexOf( Reference $reference ) {
129
		foreach ( $this->references as $index => $ref ) {
130
			if ( $ref === $reference ) {
131
				return $index;
132
			}
133
		}
134
135
		return false;
136
	}
137
138
	/**
139
	 * Removes the reference with the same hash as the provided reference if such a reference exists in the list.
140
	 *
141
	 * @since 0.1
142
	 *
143
	 * @param Reference $reference
144
	 */
145
	public function removeReference( Reference $reference ) {
146
		$this->removeReferenceHash( $reference->getHash() );
147
	}
148
149
	/**
150
	 * Returns if the list contains a reference with the provided hash.
151
	 *
152
	 * @since 0.3
153
	 *
154
	 * @param string $referenceHash
155
	 *
156
	 * @return boolean
157
	 */
158
	public function hasReferenceHash( $referenceHash ) {
159
		return $this->getReference( $referenceHash ) !== null;
160
	}
161
162
	/**
163
	 * Removes the reference with the provided hash if it exists in the list.
164
	 *
165
	 * @since 0.3
166
	 *
167
	 * @param string $referenceHash	`
168
	 */
169
	public function removeReferenceHash( $referenceHash ) {
170
		foreach ( $this->references as $index => $reference ) {
171
			if ( $reference->getHash() === $referenceHash ) {
172
				$this->removeReferenceAtIndex( $index );
173
			}
174
		}
175
	}
176
177
	/**
178
	 * @param int $index
179
	 */
180
	private function removeReferenceAtIndex( $index ) {
181
		array_splice( $this->references, $index, 1 );
182
	}
183
184
	/**
185
	 * Returns the reference with the provided hash, or
186
	 * null if there is no such reference in the list.
187
	 *
188
	 * @since 0.3
189
	 *
190
	 * @param string $referenceHash
191
	 *
192
	 * @return Reference|null
193
	 */
194
	public function getReference( $referenceHash ) {
195
		foreach ( $this->references as $reference ) {
196
			if ( $reference->getHash() === $referenceHash ) {
197
				return $reference;
198
			}
199
		}
200
201
		return null;
202
	}
203
204
	/**
205
	 * @see Serializable::serialize
206
	 *
207
	 * @since 2.1
208
	 *
209
	 * @return string
210
	 */
211
	public function serialize() {
212
		return serialize( $this->references );
213
	}
214
215
	/**
216
	 * @see Serializable::unserialize
217
	 *
218
	 * @since 2.1
219
	 *
220
	 * @param string $data
221
	 */
222
	public function unserialize( $data ) {
223
		$this->__construct( unserialize( $data ) );
224
	}
225
226
	/**
227
	 * @since 4.4
228
	 *
229
	 * @return bool
230
	 */
231
	public function isEmpty() {
232
		return empty( $this->references );
233
	}
234
235
	/**
236
	 * The hash is purely valuer based. Order of the elements in the array is not held into account.
237
	 *
238
	 * @since 0.3
239
	 *
240
	 * @return string
241
	 */
242
	public function getValueHash() {
243
		$hasher = new MapValueHasher();
244
		return $hasher->hash( $this->references );
245
	}
246
247
	/**
248
	 * @see Comparable::equals
249
	 *
250
	 * The comparison is done purely value based, ignoring the order of the elements in the array.
251
	 *
252
	 * @since 0.3
253
	 *
254
	 * @param mixed $target
255
	 *
256
	 * @return bool
257
	 */
258
	public function equals( $target ) {
259
		if ( $this === $target ) {
260
			return true;
261
		}
262
263
		return $target instanceof self
264
		       && $this->getValueHash() === $target->getValueHash();
265
	}
266
267
	/**
268
	 * @see Countable::count
269
	 *
270
	 * @return int
271
	 */
272
	public function count() {
273
		return count( $this->references );
274
	}
275
276
	/**
277
	 * @see IteratorAggregate::getIterator
278
	 *
279
	 * @since 5.0
280
	 *
281
	 * @return Traversable
282
	 */
283
	public function getIterator() {
284
		return new ArrayIterator( $this->references );
285
	}
286
287
}
288