From debf0de288a679a8df1b413470d1db02e02bad10 Mon Sep 17 00:00:00 2001 From: jeroendedauw Date: Fri, 22 Aug 2014 08:47:42 +0200 Subject: [PATCH 1/5] Add StatementList --- src/Statement/StatementList.php | 146 ++++++++++++ tests/unit/Statement/StatementListTest.php | 251 +++++++++++++++++++++ 2 files changed, 397 insertions(+) create mode 100644 src/Statement/StatementList.php create mode 100644 tests/unit/Statement/StatementListTest.php diff --git a/src/Statement/StatementList.php b/src/Statement/StatementList.php new file mode 100644 index 00000000..dc7d4433 --- /dev/null +++ b/src/Statement/StatementList.php @@ -0,0 +1,146 @@ + + */ +class StatementList implements \IteratorAggregate { + + /** + * @var Statement[] + */ + private $statements = array(); + + /** + * @param Statement[]|Traversable $statements + * + * @throws InvalidArgumentException + */ + public function __construct( $statements = array() ) { + $this->addStatements( $statements ); + } + + /** + * Returns the best statements per property. + * The best statements are those with the highest rank for a particular property. + * Deprecated ranks are never included. + * + * @return self + */ + public function getBestStatementPerProperty() { + $statementList = new self(); + + foreach ( $this->getPropertyIds() as $propertyId ) { + $claims = new Claims( $this->statements ); + $statementList->addStatements( $claims->getClaimsForProperty( $propertyId )->getBestClaims() ); + } + + return $statementList; + } + + private function addStatements( $statements ) { + $this->assertAreStatements( $statements ); + + foreach ( $statements as $statement ) { + $this->statements[] = $statement; + } + } + + private function assertAreStatements( $statements ) { + if ( !is_array( $statements ) && !( $statements instanceof Traversable ) ) { + throw new InvalidArgumentException( '$statements should be an array or a Traversable' ); + } + + foreach ( $statements as $statement ) { + if ( !( $statement instanceof Statement ) ) { + throw new InvalidArgumentException( 'All elements need to be of type Statement' ); + } + } + } + + /** + * Returns the property ids used by the statements. + * The keys of the returned array hold the serializations of the property ids. + * + * @return PropertyId[] + */ + public function getPropertyIds() { + $propertyIds = array(); + + foreach ( $this->statements as $statement ) { + $propertyIds[$statement->getPropertyId()->getSerialization()] = $statement->getPropertyId(); + } + + return $propertyIds; + } + + public function addStatement( Statement $statement ) { + $this->statements[] = $statement; + } + + /** + * @param Snak $mainSnak + * @param Snak[]|Snaks|null $qualifiers + * @param Reference[]|References|null $references + * @param string|null $guid + */ + public function addNewStatement( Snak $mainSnak, $qualifiers = null, $references = null, $guid = null ) { + $qualifiers = is_array( $qualifiers ) ? new SnakList( $qualifiers ) : $qualifiers; + $references = is_array( $references ) ? new ReferenceList( $references ) : $references; + + $statement = new Statement( $mainSnak, $qualifiers, $references ); + $statement->setGuid( $guid ); + + $this->addStatement( $statement ); + } + + /** + * Statements that have a main snak already in the list are filtered out. + * The last occurrences are retained. + * + * @return self + */ + public function getWithUniqueMainSnaks() { + $statements = array(); + + foreach ( $this->statements as $statement ) { + $statements[$statement->getMainSnak()->getHash()] = $statement; + } + + return new self( $statements ); + } + + /** + * @return Traversable + */ + public function getIterator() { + return new \ArrayIterator( $this->statements ); + } + + /** + * @return Statement[] + */ + public function toArray() { + return $this->statements; + } + +} diff --git a/tests/unit/Statement/StatementListTest.php b/tests/unit/Statement/StatementListTest.php new file mode 100644 index 00000000..81a7a5f1 --- /dev/null +++ b/tests/unit/Statement/StatementListTest.php @@ -0,0 +1,251 @@ + + */ +class StatementListTest extends \PHPUnit_Framework_TestCase { + + public function testGivenNoStatements_getPropertyIdsReturnsEmptyArray() { + $list = new StatementList(); + $this->assertSame( array(), $list->getPropertyIds() ); + } + + public function testGivenStatements_getPropertyIdsReturnsArrayWithoutDuplicates() { + $list = new StatementList( array( + $this->getStubStatement( 1, 'kittens' ), + $this->getStubStatement( 3, 'foo' ), + $this->getStubStatement( 2, 'bar' ), + $this->getStubStatement( 2, 'baz' ), + $this->getStubStatement( 1, 'bah' ), + ) ); + + $this->assertEquals( + array( + 'P1' => new PropertyId( 'P1' ), + 'P3' => new PropertyId( 'P3' ), + 'P2' => new PropertyId( 'P2' ), + ), + $list->getPropertyIds() + ); + } + + private function getStubStatement( $propertyId, $guid, $rank = Statement::RANK_NORMAL ) { + $statement = $this->getMockBuilder( 'Wikibase\DataModel\Claim\Statement' ) + ->disableOriginalConstructor()->getMock(); + + $statement->expects( $this->any() ) + ->method( 'getGuid' ) + ->will( $this->returnValue( $guid ) ); + + $statement->expects( $this->any() ) + ->method( 'getPropertyId' ) + ->will( $this->returnValue( PropertyId::newFromNumber( $propertyId ) ) ); + + $statement->expects( $this->any() ) + ->method( 'getRank' ) + ->will( $this->returnValue( $rank ) ); + + return $statement; + } + + public function testCanIterate() { + $statement = $this->getStubStatement( 1, 'kittens' ); + $list = new StatementList( array( $statement ) ); + + foreach ( $list as $statementFormList ) { + $this->assertEquals( $statement, $statementFormList ); + } + } + + public function testGetBestStatementPerProperty() { + $list = new StatementList( array( + $this->getStubStatement( 1, 'one', Statement::RANK_PREFERRED ), + $this->getStubStatement( 1, 'two', Statement::RANK_NORMAL ), + $this->getStubStatement( 1, 'three', Statement::RANK_PREFERRED ), + + $this->getStubStatement( 2, 'four', Statement::RANK_DEPRECATED ), + + $this->getStubStatement( 3, 'five', Statement::RANK_DEPRECATED ), + $this->getStubStatement( 3, 'six', Statement::RANK_NORMAL ), + + $this->getStubStatement( 4, 'seven', Statement::RANK_PREFERRED ), + $this->getStubStatement( 4, 'eight', Statement::RANK_TRUTH ), + ) ); + + $this->assertEquals( + array( + $this->getStubStatement( 1, 'one', Statement::RANK_PREFERRED ), + $this->getStubStatement( 1, 'three', Statement::RANK_PREFERRED ), + + $this->getStubStatement( 3, 'six', Statement::RANK_NORMAL ), + + $this->getStubStatement( 4, 'eight', Statement::RANK_TRUTH ), + ), + $list->getBestStatementPerProperty()->toArray() + ); + } + + public function testGetUniqueMainSnaksReturnsListWithoutDuplicates() { + $list = new StatementList( array( + $this->getStatementWithSnak( 1, 'foo' ), + $this->getStatementWithSnak( 2, 'foo' ), + $this->getStatementWithSnak( 1, 'foo' ), + $this->getStatementWithSnak( 2, 'bar' ), + $this->getStatementWithSnak( 1, 'bar' ), + ) ); + + $this->assertEquals( + array( + $this->getStatementWithSnak( 1, 'foo' ), + $this->getStatementWithSnak( 2, 'foo' ), + $this->getStatementWithSnak( 2, 'bar' ), + $this->getStatementWithSnak( 1, 'bar' ), + ), + array_values( $list->getWithUniqueMainSnaks()->toArray() ) + ); + } + + private function getStatementWithSnak( $propertyId, $stringValue ) { + $snak = $this->newSnak( $propertyId, $stringValue ); + $claim = new Statement( $snak ); + $claim->setGuid( sha1( $snak->getHash() ) ); + return $claim; + } + + private function newSnak( $propertyId, $stringValue ) { + return new PropertyValueSnak( $propertyId, new StringValue( $stringValue ) ); + } + + public function testAddStatementWithOnlyMainSnak() { + $list = new StatementList(); + + $list->addNewStatement( $this->newSnak( 42, 'foo' ) ); + + $this->assertEquals( + new StatementList( array( + new Statement( $this->newSnak( 42, 'foo' ) ) + ) ), + $list + ); + } + + public function testAddStatementWithQualifiersAsSnakArray() { + $list = new StatementList(); + + $list->addNewStatement( + $this->newSnak( 42, 'foo' ), + array( + $this->newSnak( 1, 'bar' ) + ) + ); + + $this->assertEquals( + new StatementList( array( + new Statement( + $this->newSnak( 42, 'foo' ), + new SnakList( array( + $this->newSnak( 1, 'bar' ) + ) ) + ) + ) ), + $list + ); + } + + public function testAddStatementWithQualifiersAsSnakList() { + $list = new StatementList(); + $snakList = new SnakList( array( + $this->newSnak( 1, 'bar' ) + ) ); + + $list->addNewStatement( + $this->newSnak( 42, 'foo' ), + $snakList + ); + + $this->assertEquals( + new StatementList( array( + new Statement( + $this->newSnak( 42, 'foo' ), + $snakList + ) + ) ), + $list + ); + } + + public function testAddStatementWithGuid() { + $list = new StatementList(); + + $list->addNewStatement( + $this->newSnak( 42, 'foo' ), + null, + null, + 'kittens' + ); + + $statement = new Statement( + $this->newSnak( 42, 'foo' ), + null + ); + + $statement->setGuid( 'kittens' ); + + $this->assertEquals( + new StatementList( array( $statement ) ), + $list + ); + } + + public function testCanConstructWithClaimsObjectContainingOnlyStatements() { + $statementArray = array( + $this->getStatementWithSnak( 1, 'foo' ), + $this->getStatementWithSnak( 2, 'bar' ), + ); + + $claimsObject = new Claims( $statementArray ); + + $list = new StatementList( $claimsObject ); + + $this->assertEquals( + $statementArray, + array_values( $list->toArray() ) + ); + } + + public function testGivenTraversableWithNonStatements_constructorThrowsException() { + $claim = new Claim( new PropertyValueSnak( 42, new StringValue( 'foo' ) ) ); + $claim->setGuid( 'meh' ); + + $claimArray = array( + $this->getStatementWithSnak( 1, 'foo' ), + $claim, + $this->getStatementWithSnak( 2, 'bar' ), + ); + + $claimsObject = new Claims( $claimArray ); + + $this->setExpectedException( 'InvalidArgumentException' ); + new StatementList( $claimsObject ); + } + + public function testGivenNonTraversable_constructorThrowsException() { + $this->setExpectedException( 'InvalidArgumentException' ); + new StatementList( null ); + } + +} From 9528b8fdcf9498948ae504f0e3c54c0e85fc8b94 Mon Sep 17 00:00:00 2001 From: jeroendedauw Date: Fri, 22 Aug 2014 08:49:11 +0200 Subject: [PATCH 2/5] Remove getBestClaimPerProperty from ClaimList The rank is a Statement concept, not a Claim one --- src/Claim/ClaimList.php | 26 -------------------------- tests/unit/Claim/ClaimListTest.php | 28 ---------------------------- 2 files changed, 54 deletions(-) diff --git a/src/Claim/ClaimList.php b/src/Claim/ClaimList.php index 499bbaa9..3cea4149 100644 --- a/src/Claim/ClaimList.php +++ b/src/Claim/ClaimList.php @@ -41,26 +41,6 @@ public function __construct( $claims = array() ) { $this->claims = $claims; } - /** - * Returns the best claims per property. - * The best claims are those with the highest rank for a particular property. - * Deprecated ranks are never included. - * - * Caution: the ranking is done per property, not globally, as in the Claims class. - * - * @return self - */ - public function getBestClaimPerProperty() { - $claimList = new self(); - - foreach ( $this->getPropertyIds() as $propertyId ) { - $claims = new Claims( $this->claims ); - $claimList->addClaims( $claims->getClaimsForProperty( $propertyId )->getBestClaims() ); - } - - return $claimList; - } - /** * Returns the property ids used by the claims. * The keys of the returned array hold the serializations of the property ids. @@ -77,12 +57,6 @@ public function getPropertyIds() { return $propertyIds; } - private function addClaims( Claims $claims ) { - foreach ( $claims as $claim ) { - $this->addClaim( $claim ); - } - } - public function addClaim( Claim $claim ) { $this->claims[] = $claim; } diff --git a/tests/unit/Claim/ClaimListTest.php b/tests/unit/Claim/ClaimListTest.php index 2941393b..2528c65e 100644 --- a/tests/unit/Claim/ClaimListTest.php +++ b/tests/unit/Claim/ClaimListTest.php @@ -89,34 +89,6 @@ public function testCanIterate() { } } - public function testGetBestClaimPerProperty() { - $list = new ClaimList( array( - $this->getStubClaim( 1, 'one', Claim::RANK_PREFERRED ), - $this->getStubClaim( 1, 'two', Claim::RANK_NORMAL ), - $this->getStubClaim( 1, 'three', Claim::RANK_PREFERRED ), - - $this->getStubClaim( 2, 'four', Claim::RANK_DEPRECATED ), - - $this->getStubClaim( 3, 'five', Claim::RANK_DEPRECATED ), - $this->getStubClaim( 3, 'six', Claim::RANK_NORMAL ), - - $this->getStubClaim( 4, 'seven', Claim::RANK_PREFERRED ), - $this->getStubClaim( 4, 'eight', Claim::RANK_TRUTH ), - ) ); - - $this->assertEquals( - array( - $this->getStubClaim( 1, 'one', Claim::RANK_PREFERRED ), - $this->getStubClaim( 1, 'three', Claim::RANK_PREFERRED ), - - $this->getStubClaim( 3, 'six', Claim::RANK_NORMAL ), - - $this->getStubClaim( 4, 'eight', Claim::RANK_TRUTH ), - ), - $list->getBestClaimPerProperty()->toArray() - ); - } - public function testGetUniqueMainSnaksReturnsListWithoutDuplicates() { $list = new ClaimList( array( $this->getClaimWithSnak( 1, 'foo' ), From 65e182ad8980ea3d14690af8bfa7ba55f6bb89a2 Mon Sep 17 00:00:00 2001 From: jeroendedauw Date: Fri, 22 Aug 2014 08:57:10 +0200 Subject: [PATCH 3/5] Move Statement from Claim to Statement sub NS --- Aliases.php | 11 ++++++++++- WikibaseDataModel.php | 3 ++- src/Claim/Claim.php | 1 + src/Entity/Item.php | 4 ++-- src/{Claim => Statement}/Statement.php | 3 ++- src/Statement/StatementList.php | 2 +- tests/unit/ByPropertyIdArrayTest.php | 2 +- tests/unit/Claim/ClaimStandaloneTest.php | 2 +- tests/unit/Claim/ClaimsTest.php | 2 +- tests/unit/Entity/Diff/ItemDifferTest.php | 2 +- tests/unit/Entity/EntityTest.php | 2 +- tests/unit/Entity/ItemTest.php | 4 ++-- tests/unit/Statement/StatementListTest.php | 4 ++-- tests/unit/{Claim => Statement}/StatementTest.php | 6 +++--- 14 files changed, 30 insertions(+), 18 deletions(-) rename src/{Claim => Statement}/Statement.php (97%) rename tests/unit/{Claim => Statement}/StatementTest.php (97%) diff --git a/Aliases.php b/Aliases.php index 9bf06be5..f396a662 100644 --- a/Aliases.php +++ b/Aliases.php @@ -88,7 +88,7 @@ class Claims extends \Wikibase\DataModel\Claim\Claims {} /** * @deprecated since 0.6, use the base class instead. */ - class Statement extends \Wikibase\DataModel\Claim\Statement {} + class Statement extends DataModel\Statement\Statement {} /** * @deprecated since 0.6, use the base class instead. */ @@ -165,3 +165,12 @@ class ItemDiff extends \Wikibase\DataModel\Entity\Diff\ItemDiff {} class EntityDiff extends \Wikibase\DataModel\Entity\Diff\EntityDiff {} } + +namespace Wikibase\DataModel\Claim { + + /** + * @deprecated since 1.0, use the base class instead. + */ + class Statement extends \Wikibase\DataModel\Statement\Statement {} + +} diff --git a/WikibaseDataModel.php b/WikibaseDataModel.php index 4e6d6c54..7ed8f646 100644 --- a/WikibaseDataModel.php +++ b/WikibaseDataModel.php @@ -38,7 +38,7 @@ class_alias( 'Wikibase\DataModel\ByPropertyIdArray', 'Wikibase\ByPropertyIdArray class_alias( 'Wikibase\DataModel\Claim\Claim', 'Wikibase\Claim' ); class_alias( 'Wikibase\DataModel\Claim\ClaimListAccess', 'Wikibase\ClaimListAccess' ); class_alias( 'Wikibase\DataModel\Claim\Claims', 'Wikibase\Claims' ); -class_alias( 'Wikibase\DataModel\Claim\Statement', 'Wikibase\Statement' ); +class_alias( 'Wikibase\DataModel\Statement\Statement', 'Wikibase\Statement' ); class_alias( 'Wikibase\DataModel\Entity\Entity', 'Wikibase\Entity' ); class_alias( 'Wikibase\DataModel\Entity\Item', 'Wikibase\Item' ); class_alias( 'Wikibase\DataModel\Entity\Property', 'Wikibase\Property' ); @@ -60,3 +60,4 @@ class_alias( 'Wikibase\DataModel\LegacyIdInterpreter', 'Wikibase\DataModel\Inter // Aliases introduced in 1.0 class_alias( 'Wikibase\DataModel\Entity\Diff\EntityDiff', 'Wikibase\DataModel\Entity\EntityDiff' ); class_alias( 'Wikibase\DataModel\Entity\Diff\ItemDiff', 'Wikibase\DataModel\Entity\ItemDiff' ); +class_alias( 'Wikibase\DataModel\Statement\Statement', 'Wikibase\DataModel\Claim\Statement' ); diff --git a/src/Claim/Claim.php b/src/Claim/Claim.php index f48d2e8e..f628d1af 100644 --- a/src/Claim/Claim.php +++ b/src/Claim/Claim.php @@ -9,6 +9,7 @@ use Wikibase\DataModel\Snak\Snak; use Wikibase\DataModel\Snak\SnakList; use Wikibase\DataModel\Snak\Snaks; +use Wikibase\DataModel\Statement\Statement; /** * Class that represents a single Wikibase claim. diff --git a/src/Entity/Item.php b/src/Entity/Item.php index 91a20f80..588b489c 100644 --- a/src/Entity/Item.php +++ b/src/Entity/Item.php @@ -10,7 +10,7 @@ use OutOfBoundsException; use Wikibase\DataModel\Claim\Claim; use Wikibase\DataModel\Claim\Claims; -use Wikibase\DataModel\Claim\Statement; +use Wikibase\DataModel\Statement\Statement; use Wikibase\DataModel\Entity\Diff\EntityDiff; use Wikibase\DataModel\Entity\Diff\ItemDiff; use Wikibase\DataModel\SiteLink; @@ -341,7 +341,7 @@ public function addClaim( Claim $claim ) { /** * @since 1.0 * - * @return Statement[] + * @return \Wikibase\DataModel\Statement\Statement[] */ public function getStatements() { return $this->statements; diff --git a/src/Claim/Statement.php b/src/Statement/Statement.php similarity index 97% rename from src/Claim/Statement.php rename to src/Statement/Statement.php index 218fadd1..0b4a81cf 100644 --- a/src/Claim/Statement.php +++ b/src/Statement/Statement.php @@ -1,8 +1,9 @@ newClaim( $snak ); - $this->assertInstanceOf( 'Wikibase\DataModel\Claim\Statement', $statement ); + $this->assertInstanceOf( 'Wikibase\DataModel\Statement\Statement', $statement ); $this->assertEquals( $snak, $statement->getMainSnak() ); } diff --git a/tests/unit/Statement/StatementListTest.php b/tests/unit/Statement/StatementListTest.php index 81a7a5f1..069cc32c 100644 --- a/tests/unit/Statement/StatementListTest.php +++ b/tests/unit/Statement/StatementListTest.php @@ -5,7 +5,7 @@ use DataValues\StringValue; use Wikibase\DataModel\Claim\Claim; use Wikibase\DataModel\Claim\Claims; -use Wikibase\DataModel\Claim\Statement; +use Wikibase\DataModel\Statement\Statement; use Wikibase\DataModel\Entity\PropertyId; use Wikibase\DataModel\Snak\PropertyValueSnak; use Wikibase\DataModel\Snak\SnakList; @@ -44,7 +44,7 @@ public function testGivenStatements_getPropertyIdsReturnsArrayWithoutDuplicates( } private function getStubStatement( $propertyId, $guid, $rank = Statement::RANK_NORMAL ) { - $statement = $this->getMockBuilder( 'Wikibase\DataModel\Claim\Statement' ) + $statement = $this->getMockBuilder( 'Wikibase\DataModel\Statement\Statement' ) ->disableOriginalConstructor()->getMock(); $statement->expects( $this->any() ) diff --git a/tests/unit/Claim/StatementTest.php b/tests/unit/Statement/StatementTest.php similarity index 97% rename from tests/unit/Claim/StatementTest.php rename to tests/unit/Statement/StatementTest.php index 59397fe9..d10c2d75 100644 --- a/tests/unit/Claim/StatementTest.php +++ b/tests/unit/Statement/StatementTest.php @@ -4,7 +4,7 @@ use DataValues\StringValue; use Wikibase\DataModel\Claim\Claim; -use Wikibase\DataModel\Claim\Statement; +use Wikibase\DataModel\Statement\Statement; use Wikibase\DataModel\Entity\PropertyId; use Wikibase\DataModel\Reference; use Wikibase\DataModel\ReferenceList; @@ -14,7 +14,7 @@ use Wikibase\DataModel\Snak\SnakList; /** - * @covers Wikibase\DataModel\Claim\Statement + * @covers Wikibase\DataModel\Statement\Statement * * @group Wikibase * @group WikibaseDataModel @@ -162,7 +162,7 @@ public function testGetHash() { * @dataProvider instanceProvider */ public function testGetAllSnaks( Claim $claim ) { - /* @var Statement $statement */ + /* @var \Wikibase\DataModel\Statement\Statement $statement */ $statement = $claim; $snaks = $statement->getAllSnaks(); From 972728683f3816ef2f1f7198e64f4b3f56b8a213 Mon Sep 17 00:00:00 2001 From: jeroendedauw Date: Fri, 22 Aug 2014 08:57:38 +0200 Subject: [PATCH 4/5] Remove no longer needed import --- src/Statement/StatementList.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Statement/StatementList.php b/src/Statement/StatementList.php index 32f4803d..3167dca5 100644 --- a/src/Statement/StatementList.php +++ b/src/Statement/StatementList.php @@ -5,7 +5,6 @@ use InvalidArgumentException; use Traversable; use Wikibase\DataModel\Claim\Claims; -use Wikibase\DataModel\Statement\Statement; use Wikibase\DataModel\Entity\PropertyId; use Wikibase\DataModel\Reference; use Wikibase\DataModel\ReferenceList; From 28993cb8c86976b2839ed1a53d5d43ef02f5e1e8 Mon Sep 17 00:00:00 2001 From: jeroendedauw Date: Fri, 22 Aug 2014 08:58:56 +0200 Subject: [PATCH 5/5] Update release notes --- RELEASE-NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index ad1bcdda..6407fc9a 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -52,6 +52,7 @@ Other breaking changes: #### Additions * Added `ClaimList` +* Added `StatementList` * Added `ClaimListDiffer` * Added `PropertyDataTypeLookup` and trivial implementation `InMemoryDataTypeLookup` * Added `PropertyNotFoundException` @@ -65,6 +66,7 @@ Other breaking changes: * Undeprecated passing an integer to `Item::setId` and `Property::setId` * Deprecated `Entity::setId`, `Entity::newClaim` and `Entity::getAllSnaks` * Deprecated `Item::getClaims` in favour of `Item::getStatements` +* The FQN of `Statement` is now `Wikibase\DataModel\Statement\Statement`. The old FQN is deprecated. ## Version 0.9 (2014-08-15)