Skip to content

Commit

Permalink
Add StatementListChanger
Browse files Browse the repository at this point in the history
  • Loading branch information
thiemowmde committed Nov 11, 2016
1 parent 64acca5 commit f069c22
Show file tree
Hide file tree
Showing 3 changed files with 452 additions and 1 deletion.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"vendor/bin/phpcs src/* tests/* --standard=phpcs.xml --extensions=php -sp"
],
"phpmd": [
"vendor/bin/phpmd src/,tests/unit/ text phpmd.xml"
"vendor/bin/phpmd src/ text phpmd.xml"
]
}
}
169 changes: 169 additions & 0 deletions src/Statement/StatementListChanger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php

namespace Wikibase\DataModel\Services\Statement;

use Wikibase\DataModel\Entity\PropertyId;
use Wikibase\DataModel\Statement\Statement;
use Wikibase\DataModel\Statement\StatementList;

/**
* A collection of higher level utility functions to manipulate StatementList objects.
*
* @since 3.7
*
* @license GPL-2.0+
* @author Thiemo Mättig
*/
class StatementListChanger {

/**
* @var StatementList
*/
private $statementList;

public function __construct( StatementList $statementList ) {
$this->statementList = $statementList;
}

/**
* Removes all statements from the $statementList object given in the constructor.
*/
public function clear() {
foreach ( $this->statementList->toArray() as $statement ) {
$this->statementList->removeStatementsWithGuid( $statement->getGuid() );
}
}

/**
* Makes sure all statements with the same property are next to each other (forming a group),
* and reorders them if necessary. The position of the group in the list is determined by the
* first statement with the same property.
*/
public function groupByProperty() {
$byId = [];

foreach ( $this->statementList->toArray() as $statement ) {
$id = $statement->getPropertyId()->getSerialization();
$byId[$id][] = $statement;
}

$this->clear();

foreach ( $byId as $statements ) {
foreach ( $statements as $statement ) {
$this->statementList->addStatement( $statement );
}
}
}

/**
* @param Statement $newStatement
* @param int|null $index An absolute index in the list. If the index is not next to a statement
* with the same property, the closest possible position is used instead. Default is null,
* which adds the new statement after the last statement with the same property, or to the end.
*/
public function addToGroup( Statement $newStatement, $index = null ) {
$statements = $this->statementList->toArray();
$id = $newStatement->getPropertyId();

if ( $index === null ) {
$index = $this->getLastIndexWithinGroup( $statements, $id );
} else {
// Limit search range to avoid looping non-existing positions
$validIndex = min( max( 0, $index ), count( $statements ) );
$index = $this->getClosestIndexWithinGroup( $statements, $id, $validIndex );
if ( $index === null ) {
$index = $this->getClosestIndexAtGroupBorder( $statements, $validIndex );
}
}

$this->statementList->addStatement( $newStatement, $index );
}

/**
* @param Statement[] $statements
* @param PropertyId $id
*
* @return int|null
*/
private function getLastIndexWithinGroup( array $statements, PropertyId $id ) {
// Start searching from the end and stop at the first match
for ( $i = count( $statements ); $i > 0; $i-- ) {
if ( $statements[$i - 1]->getPropertyId()->equals( $id ) ) {
return $i;
}
}

return null;
}

/**
* @param Statement[] $statements
* @param PropertyId $id
* @param int $index
*
* @return int|null
*/
private function getClosestIndexWithinGroup( array $statements, PropertyId $id, $index ) {
$longestDistance = max( $index, count( $statements ) - $index );

for ( $i = 0; $i <= $longestDistance; $i++ ) {
if ( $this->isWithinGroup( $statements, $id, $index - $i ) ) {
return $index - $i;
} elseif ( $i && $this->isWithinGroup( $statements, $id, $index + $i ) ) {
return $index + $i;
}
}

return null;
}

/**
* @param Statement[] $statements
* @param int $index
*
* @return int|null
*/
private function getClosestIndexAtGroupBorder( array $statements, $index ) {
$longestDistance = max( $index, count( $statements ) - $index );

for ( $i = 0; $i <= $longestDistance; $i++ ) {
if ( $this->isGroupBorder( $statements, $index - $i ) ) {
return $index - $i;
} elseif ( $i && $this->isGroupBorder( $statements, $index + $i ) ) {
return $index + $i;
}
}

return null;
}

/**
* @param Statement[] $statements
* @param PropertyId $id
* @param int $index
*
* @return bool
*/
private function isWithinGroup( array $statements, PropertyId $id, $index ) {
$count = count( $statements );

// Valid if the index either prepends ot appends a statement with the same property
return $index > 0 && $index <= $count && $statements[$index - 1]->getPropertyId()->equals( $id )
|| $index >= 0 && $index < $count && $statements[$index]->getPropertyId()->equals( $id );
}

/**
* @param Statement[] $statements
* @param int $index
*
* @return bool
*/
private function isGroupBorder( array $statements, $index ) {
// First and last possible position is always a border
return $index <= 0
|| $index >= count( $statements )
|| !$statements[$index - 1]->getPropertyId()->equals( $statements[$index]->getPropertyId() );
}

}
Loading

0 comments on commit f069c22

Please sign in to comment.