Skip to content

Commit debf0de

Browse files
committed
Add StatementList
1 parent 4dfacaa commit debf0de

2 files changed

Lines changed: 397 additions & 0 deletions

File tree

src/Statement/StatementList.php

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
3+
namespace Wikibase\DataModel\Statement;
4+
5+
use InvalidArgumentException;
6+
use Traversable;
7+
use Wikibase\DataModel\Claim\Claims;
8+
use Wikibase\DataModel\Claim\Statement;
9+
use Wikibase\DataModel\Entity\PropertyId;
10+
use Wikibase\DataModel\Reference;
11+
use Wikibase\DataModel\ReferenceList;
12+
use Wikibase\DataModel\References;
13+
use Wikibase\DataModel\Snak\Snak;
14+
use Wikibase\DataModel\Snak\SnakList;
15+
use Wikibase\DataModel\Snak\Snaks;
16+
17+
/**
18+
* Ordered, non-unique, collection of Statement objects.
19+
* Provides various filter operations though does not do any indexing by default.
20+
*
21+
* @since 1.0
22+
*
23+
* @licence GNU GPL v2+
24+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
25+
*/
26+
class StatementList implements \IteratorAggregate {
27+
28+
/**
29+
* @var Statement[]
30+
*/
31+
private $statements = array();
32+
33+
/**
34+
* @param Statement[]|Traversable $statements
35+
*
36+
* @throws InvalidArgumentException
37+
*/
38+
public function __construct( $statements = array() ) {
39+
$this->addStatements( $statements );
40+
}
41+
42+
/**
43+
* Returns the best statements per property.
44+
* The best statements are those with the highest rank for a particular property.
45+
* Deprecated ranks are never included.
46+
*
47+
* @return self
48+
*/
49+
public function getBestStatementPerProperty() {
50+
$statementList = new self();
51+
52+
foreach ( $this->getPropertyIds() as $propertyId ) {
53+
$claims = new Claims( $this->statements );
54+
$statementList->addStatements( $claims->getClaimsForProperty( $propertyId )->getBestClaims() );
55+
}
56+
57+
return $statementList;
58+
}
59+
60+
private function addStatements( $statements ) {
61+
$this->assertAreStatements( $statements );
62+
63+
foreach ( $statements as $statement ) {
64+
$this->statements[] = $statement;
65+
}
66+
}
67+
68+
private function assertAreStatements( $statements ) {
69+
if ( !is_array( $statements ) && !( $statements instanceof Traversable ) ) {
70+
throw new InvalidArgumentException( '$statements should be an array or a Traversable' );
71+
}
72+
73+
foreach ( $statements as $statement ) {
74+
if ( !( $statement instanceof Statement ) ) {
75+
throw new InvalidArgumentException( 'All elements need to be of type Statement' );
76+
}
77+
}
78+
}
79+
80+
/**
81+
* Returns the property ids used by the statements.
82+
* The keys of the returned array hold the serializations of the property ids.
83+
*
84+
* @return PropertyId[]
85+
*/
86+
public function getPropertyIds() {
87+
$propertyIds = array();
88+
89+
foreach ( $this->statements as $statement ) {
90+
$propertyIds[$statement->getPropertyId()->getSerialization()] = $statement->getPropertyId();
91+
}
92+
93+
return $propertyIds;
94+
}
95+
96+
public function addStatement( Statement $statement ) {
97+
$this->statements[] = $statement;
98+
}
99+
100+
/**
101+
* @param Snak $mainSnak
102+
* @param Snak[]|Snaks|null $qualifiers
103+
* @param Reference[]|References|null $references
104+
* @param string|null $guid
105+
*/
106+
public function addNewStatement( Snak $mainSnak, $qualifiers = null, $references = null, $guid = null ) {
107+
$qualifiers = is_array( $qualifiers ) ? new SnakList( $qualifiers ) : $qualifiers;
108+
$references = is_array( $references ) ? new ReferenceList( $references ) : $references;
109+
110+
$statement = new Statement( $mainSnak, $qualifiers, $references );
111+
$statement->setGuid( $guid );
112+
113+
$this->addStatement( $statement );
114+
}
115+
116+
/**
117+
* Statements that have a main snak already in the list are filtered out.
118+
* The last occurrences are retained.
119+
*
120+
* @return self
121+
*/
122+
public function getWithUniqueMainSnaks() {
123+
$statements = array();
124+
125+
foreach ( $this->statements as $statement ) {
126+
$statements[$statement->getMainSnak()->getHash()] = $statement;
127+
}
128+
129+
return new self( $statements );
130+
}
131+
132+
/**
133+
* @return Traversable
134+
*/
135+
public function getIterator() {
136+
return new \ArrayIterator( $this->statements );
137+
}
138+
139+
/**
140+
* @return Statement[]
141+
*/
142+
public function toArray() {
143+
return $this->statements;
144+
}
145+
146+
}
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
<?php
2+
3+
namespace Wikibase\Test;
4+
5+
use DataValues\StringValue;
6+
use Wikibase\DataModel\Claim\Claim;
7+
use Wikibase\DataModel\Claim\Claims;
8+
use Wikibase\DataModel\Claim\Statement;
9+
use Wikibase\DataModel\Entity\PropertyId;
10+
use Wikibase\DataModel\Snak\PropertyValueSnak;
11+
use Wikibase\DataModel\Snak\SnakList;
12+
use Wikibase\DataModel\Statement\StatementList;
13+
14+
/**
15+
* @covers Wikibase\DataModel\Statement\StatementList
16+
*
17+
* @licence GNU GPL v2+
18+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
19+
*/
20+
class StatementListTest extends \PHPUnit_Framework_TestCase {
21+
22+
public function testGivenNoStatements_getPropertyIdsReturnsEmptyArray() {
23+
$list = new StatementList();
24+
$this->assertSame( array(), $list->getPropertyIds() );
25+
}
26+
27+
public function testGivenStatements_getPropertyIdsReturnsArrayWithoutDuplicates() {
28+
$list = new StatementList( array(
29+
$this->getStubStatement( 1, 'kittens' ),
30+
$this->getStubStatement( 3, 'foo' ),
31+
$this->getStubStatement( 2, 'bar' ),
32+
$this->getStubStatement( 2, 'baz' ),
33+
$this->getStubStatement( 1, 'bah' ),
34+
) );
35+
36+
$this->assertEquals(
37+
array(
38+
'P1' => new PropertyId( 'P1' ),
39+
'P3' => new PropertyId( 'P3' ),
40+
'P2' => new PropertyId( 'P2' ),
41+
),
42+
$list->getPropertyIds()
43+
);
44+
}
45+
46+
private function getStubStatement( $propertyId, $guid, $rank = Statement::RANK_NORMAL ) {
47+
$statement = $this->getMockBuilder( 'Wikibase\DataModel\Claim\Statement' )
48+
->disableOriginalConstructor()->getMock();
49+
50+
$statement->expects( $this->any() )
51+
->method( 'getGuid' )
52+
->will( $this->returnValue( $guid ) );
53+
54+
$statement->expects( $this->any() )
55+
->method( 'getPropertyId' )
56+
->will( $this->returnValue( PropertyId::newFromNumber( $propertyId ) ) );
57+
58+
$statement->expects( $this->any() )
59+
->method( 'getRank' )
60+
->will( $this->returnValue( $rank ) );
61+
62+
return $statement;
63+
}
64+
65+
public function testCanIterate() {
66+
$statement = $this->getStubStatement( 1, 'kittens' );
67+
$list = new StatementList( array( $statement ) );
68+
69+
foreach ( $list as $statementFormList ) {
70+
$this->assertEquals( $statement, $statementFormList );
71+
}
72+
}
73+
74+
public function testGetBestStatementPerProperty() {
75+
$list = new StatementList( array(
76+
$this->getStubStatement( 1, 'one', Statement::RANK_PREFERRED ),
77+
$this->getStubStatement( 1, 'two', Statement::RANK_NORMAL ),
78+
$this->getStubStatement( 1, 'three', Statement::RANK_PREFERRED ),
79+
80+
$this->getStubStatement( 2, 'four', Statement::RANK_DEPRECATED ),
81+
82+
$this->getStubStatement( 3, 'five', Statement::RANK_DEPRECATED ),
83+
$this->getStubStatement( 3, 'six', Statement::RANK_NORMAL ),
84+
85+
$this->getStubStatement( 4, 'seven', Statement::RANK_PREFERRED ),
86+
$this->getStubStatement( 4, 'eight', Statement::RANK_TRUTH ),
87+
) );
88+
89+
$this->assertEquals(
90+
array(
91+
$this->getStubStatement( 1, 'one', Statement::RANK_PREFERRED ),
92+
$this->getStubStatement( 1, 'three', Statement::RANK_PREFERRED ),
93+
94+
$this->getStubStatement( 3, 'six', Statement::RANK_NORMAL ),
95+
96+
$this->getStubStatement( 4, 'eight', Statement::RANK_TRUTH ),
97+
),
98+
$list->getBestStatementPerProperty()->toArray()
99+
);
100+
}
101+
102+
public function testGetUniqueMainSnaksReturnsListWithoutDuplicates() {
103+
$list = new StatementList( array(
104+
$this->getStatementWithSnak( 1, 'foo' ),
105+
$this->getStatementWithSnak( 2, 'foo' ),
106+
$this->getStatementWithSnak( 1, 'foo' ),
107+
$this->getStatementWithSnak( 2, 'bar' ),
108+
$this->getStatementWithSnak( 1, 'bar' ),
109+
) );
110+
111+
$this->assertEquals(
112+
array(
113+
$this->getStatementWithSnak( 1, 'foo' ),
114+
$this->getStatementWithSnak( 2, 'foo' ),
115+
$this->getStatementWithSnak( 2, 'bar' ),
116+
$this->getStatementWithSnak( 1, 'bar' ),
117+
),
118+
array_values( $list->getWithUniqueMainSnaks()->toArray() )
119+
);
120+
}
121+
122+
private function getStatementWithSnak( $propertyId, $stringValue ) {
123+
$snak = $this->newSnak( $propertyId, $stringValue );
124+
$claim = new Statement( $snak );
125+
$claim->setGuid( sha1( $snak->getHash() ) );
126+
return $claim;
127+
}
128+
129+
private function newSnak( $propertyId, $stringValue ) {
130+
return new PropertyValueSnak( $propertyId, new StringValue( $stringValue ) );
131+
}
132+
133+
public function testAddStatementWithOnlyMainSnak() {
134+
$list = new StatementList();
135+
136+
$list->addNewStatement( $this->newSnak( 42, 'foo' ) );
137+
138+
$this->assertEquals(
139+
new StatementList( array(
140+
new Statement( $this->newSnak( 42, 'foo' ) )
141+
) ),
142+
$list
143+
);
144+
}
145+
146+
public function testAddStatementWithQualifiersAsSnakArray() {
147+
$list = new StatementList();
148+
149+
$list->addNewStatement(
150+
$this->newSnak( 42, 'foo' ),
151+
array(
152+
$this->newSnak( 1, 'bar' )
153+
)
154+
);
155+
156+
$this->assertEquals(
157+
new StatementList( array(
158+
new Statement(
159+
$this->newSnak( 42, 'foo' ),
160+
new SnakList( array(
161+
$this->newSnak( 1, 'bar' )
162+
) )
163+
)
164+
) ),
165+
$list
166+
);
167+
}
168+
169+
public function testAddStatementWithQualifiersAsSnakList() {
170+
$list = new StatementList();
171+
$snakList = new SnakList( array(
172+
$this->newSnak( 1, 'bar' )
173+
) );
174+
175+
$list->addNewStatement(
176+
$this->newSnak( 42, 'foo' ),
177+
$snakList
178+
);
179+
180+
$this->assertEquals(
181+
new StatementList( array(
182+
new Statement(
183+
$this->newSnak( 42, 'foo' ),
184+
$snakList
185+
)
186+
) ),
187+
$list
188+
);
189+
}
190+
191+
public function testAddStatementWithGuid() {
192+
$list = new StatementList();
193+
194+
$list->addNewStatement(
195+
$this->newSnak( 42, 'foo' ),
196+
null,
197+
null,
198+
'kittens'
199+
);
200+
201+
$statement = new Statement(
202+
$this->newSnak( 42, 'foo' ),
203+
null
204+
);
205+
206+
$statement->setGuid( 'kittens' );
207+
208+
$this->assertEquals(
209+
new StatementList( array( $statement ) ),
210+
$list
211+
);
212+
}
213+
214+
public function testCanConstructWithClaimsObjectContainingOnlyStatements() {
215+
$statementArray = array(
216+
$this->getStatementWithSnak( 1, 'foo' ),
217+
$this->getStatementWithSnak( 2, 'bar' ),
218+
);
219+
220+
$claimsObject = new Claims( $statementArray );
221+
222+
$list = new StatementList( $claimsObject );
223+
224+
$this->assertEquals(
225+
$statementArray,
226+
array_values( $list->toArray() )
227+
);
228+
}
229+
230+
public function testGivenTraversableWithNonStatements_constructorThrowsException() {
231+
$claim = new Claim( new PropertyValueSnak( 42, new StringValue( 'foo' ) ) );
232+
$claim->setGuid( 'meh' );
233+
234+
$claimArray = array(
235+
$this->getStatementWithSnak( 1, 'foo' ),
236+
$claim,
237+
$this->getStatementWithSnak( 2, 'bar' ),
238+
);
239+
240+
$claimsObject = new Claims( $claimArray );
241+
242+
$this->setExpectedException( 'InvalidArgumentException' );
243+
new StatementList( $claimsObject );
244+
}
245+
246+
public function testGivenNonTraversable_constructorThrowsException() {
247+
$this->setExpectedException( 'InvalidArgumentException' );
248+
new StatementList( null );
249+
}
250+
251+
}

0 commit comments

Comments
 (0)