| Current File : /home/jvzmxxx/wiki1/extensions/Wikibase/lib/includes/Store/GenericEntityInfoBuilder.php |
<?php
namespace Wikibase\Lib\Store;
use Wikibase\DataModel\Entity\EntityId;
use Wikibase\DataModel\Entity\Item;
use Wikibase\DataModel\Entity\Property;
use Wikibase\DataModel\Entity\EntityIdParser;
use Wikibase\DataModel\Term\AliasGroupList;
use Wikibase\DataModel\Term\Fingerprint;
use Wikibase\DataModel\Term\FingerprintProvider;
use Wikibase\DataModel\Term\TermList;
/**
* EntityInfoBuilder based on an EntityLookup.
*
* This is a rather inefficient implementation of EntityInfoBuilder, intended
* mainly for testing.
*
* @since 0.5
*
* @license GPL-2.0+
* @author Daniel Kinzler
*/
class GenericEntityInfoBuilder implements EntityInfoBuilder {
/**
* The entity info data structure. This data structure is exposed via getEntityInfo().
* After resolveRedirects() is called, this will contain entries for the redirect targets
* in addition to the entries for the redirected IDs. Entries for the redirected IDs
* will be php references to the entries that use the actual (target) IDs as keys.
*
* @see EntityInfoBuilder::getEntityInfo()
*
* @var array[]|null map of id-strings to entity record arrays:
* id-string => entity-record
*/
private $entityInfo = null;
/**
* @var EntityIdParser
*/
private $idParser;
/**
* @var EntityRevisionLookup
*/
private $entityRevisionLookup;
/**
* A map of entity id strings to EntityId objects, representing any
* redirects present in the list of entities provided to the constructor.
*
* Initialized lazily by resolveRedirects().
*
* @var string[]|null map of id-strings to EntityId objects:
* id-string => EntityId
*/
private $redirects = null;
/**
* @param EntityId[] $entityIds
* @param EntityIdParser $entityIdParser
* @param EntityRevisionLookup $entityRevisionLookup
*/
public function __construct(
array $entityIds,
EntityIdParser $entityIdParser,
EntityRevisionLookup $entityRevisionLookup
) {
$this->setEntityIds( $entityIds );
$this->idParser = $entityIdParser;
$this->entityRevisionLookup = $entityRevisionLookup;
}
/**
* @param EntityId[] $entityIds
*/
private function setEntityIds( array $entityIds ) {
$this->entityInfo = array();
foreach ( $entityIds as $entityId ) {
$key = $entityId->getSerialization();
$type = $entityId->getEntityType();
$this->entityInfo[$key] = array(
'id' => $key,
'type' => $type,
);
}
}
private function parseId( $id ) {
return $this->idParser->parse( $id );
}
private function getEntity( EntityId $id ) {
try {
$rev = $this->entityRevisionLookup->getEntityRevision( $id );
return $rev === null ? null : $rev->getEntity();
} catch ( RevisionedUnresolvedRedirectException $ex ) {
return null;
}
}
private function getRedirect( EntityId $id ) {
try {
$this->entityRevisionLookup->getEntityRevision( $id );
return null;
} catch ( RevisionedUnresolvedRedirectException $ex ) {
return $ex->getRedirectTargetId();
}
}
/**
* @see EntityInfoBuilder::resolveRedirects
*/
public function resolveRedirects() {
$ids = array_keys( $this->entityInfo );
$this->redirects = array();
foreach ( $ids as $idString ) {
$id = $this->parseId( $idString );
$targetId = $this->getRedirect( $id );
if ( $targetId ) {
$this->redirects[$idString] = $targetId;
$this->applyRedirect( $idString, $targetId );
}
}
}
/**
* Applied the given redirect to the internal data structure
*
* @param string $idString The redirected entity id
* @param EntityId $targetId The redirect target
*/
private function applyRedirect( $idString, EntityId $targetId ) {
$targetKey = $targetId->getSerialization();
if ( $idString === $targetKey ) {
// Sanity check: self-redirect, nothing to do.
return;
}
// If the redirect target doesn't have a record yet, copy the old record.
// Since two IDs may be redirected to the same target, this may already have
// happened.
if ( !isset( $this->entityInfo[$targetKey] ) ) {
$this->entityInfo[$targetKey] = $this->entityInfo[$idString]; // copy
$this->entityInfo[$targetKey]['id'] = $targetKey; // update id
}
// Make the redirected key a reference to the target record.
unset( $this->entityInfo[$idString] ); // just to be sure not to cause a mess
$this->entityInfo[$idString] = & $this->entityInfo[$targetKey];
}
/**
* @see EntityInfoBuilder::collectTerms
*
* @param string[]|null $types Which types of terms to include (e.g. "label", "description", "aliases").
* @param string[]|null $languages Which languages to include
*/
public function collectTerms( array $types = null, array $languages = null ) {
foreach ( $this->entityInfo as $id => &$entityRecord ) {
$id = $this->parseId( $id );
$entity = $this->getEntity( $id );
if ( !$entity ) {
// hack: fake an empty entity, so the field get initialized
$entity = new Item();
}
// FIXME: OCP violation
// This code does not allow extensions that define new entity types with
// new types of terms to register appropriate support for those here.
if ( $entity instanceof FingerprintProvider ) {
$this->injectFingerprint( $types, $entityRecord, $entity->getFingerprint(), $languages );
}
}
}
private function injectFingerprint(
array $types = null,
array &$entityRecord,
Fingerprint $fingerprint,
array $languages = null
) {
if ( $types === null || in_array( 'label', $types ) ) {
$labels = $fingerprint->getLabels();
if ( $languages !== null ) {
$labels = $labels->getWithLanguages( $languages );
}
$this->injectLabels( $entityRecord, $labels );
}
if ( $types === null || in_array( 'description', $types ) ) {
$descriptions = $fingerprint->getDescriptions();
if ( $languages !== null ) {
$descriptions = $descriptions->getWithLanguages( $languages );
}
$this->injectDescriptions( $entityRecord, $descriptions );
}
if ( $types === null || in_array( 'alias', $types ) ) {
$aliases = $fingerprint->getAliasGroups();
if ( $languages !== null ) {
$aliases = $aliases->getWithLanguages( $languages );
}
$this->injectAliases( $entityRecord, $aliases );
}
}
private function injectLabels( array &$entityRecord, TermList $labels ) {
if ( !isset( $entityRecord['labels'] ) ) {
$entityRecord['labels'] = array();
}
foreach ( $labels->toTextArray() as $lang => $text ) {
$entityRecord['labels'][$lang] = array(
'language' => $lang,
'value' => $text,
);
}
}
private function injectDescriptions( array &$entityRecord, TermList $descriptions ) {
if ( !isset( $entityRecord['descriptions'] ) ) {
$entityRecord['descriptions'] = array();
}
foreach ( $descriptions->toTextArray() as $lang => $text ) {
$entityRecord['descriptions'][$lang] = array(
'language' => $lang,
'value' => $text,
);
}
}
private function injectAliases( array &$entityRecord, AliasGroupList $aliasGroups ) {
if ( !isset( $entityRecord['aliases'] ) ) {
$entityRecord['aliases'] = array();
}
foreach ( $aliasGroups->toArray() as $aliasGroup ) {
$lang = $aliasGroup->getLanguageCode();
$entityRecord['aliases'][$lang] = array();
foreach ( $aliasGroup->getAliases() as $text ) {
$entityRecord['aliases'][$lang][] = array( // note: append
'language' => $lang,
'value' => $text,
);
}
}
}
/**
* @see EntityInfoBuilder::collectDataTypes
*/
public function collectDataTypes() {
foreach ( $this->entityInfo as $id => &$entityRecord ) {
$id = $this->parseId( $id );
if ( $id->getEntityType() !== Property::ENTITY_TYPE ) {
continue;
}
$entity = $this->getEntity( $id );
if ( $entity instanceof Property ) {
$entityRecord['datatype'] = $entity->getDataTypeId();
} else {
$entityRecord['datatype'] = null;
}
}
}
/**
* @see EntityInfoBuilder::removeMissing
*/
public function removeMissing( $redirects = 'keep-redirects' ) {
foreach ( array_keys( $this->entityInfo ) as $key ) {
$id = $this->parseId( $key );
try {
$rev = $this->entityRevisionLookup->getEntityRevision( $id );
} catch ( RevisionedUnresolvedRedirectException $ex ) {
if ( $redirects === 'keep-redirects' ) {
continue;
} else {
$rev = null;
}
}
if ( !$rev ) {
unset( $this->entityInfo[$key] );
}
}
}
/**
* @see EntityInfoBuilder::getEntityInfo
*
* @return EntityInfo
*/
public function getEntityInfo() {
return new EntityInfo( $this->entityInfo );
}
/**
* @param EntityId[] $ids
*
* @return string[]
*/
private function convertEntityIdsToStrings( array $ids ) {
return array_map( function ( EntityId $id ) {
return $id->getSerialization();
}, $ids );
}
/**
* Remove info records for the given EntityIds.
*
* @param EntityId[] $ids
*/
public function removeEntityInfo( array $ids ) {
$remove = $this->convertEntityIdsToStrings( $ids );
$this->entityInfo = array_diff_key( $this->entityInfo, array_flip( $remove ) );
}
/**
* Retain only info records for the given EntityIds.
* Useful e.g. after resolveRedirects(), to remove explicit entries for
* redirect targets not present in the original input.
*
* @param EntityId[] $ids
*/
public function retainEntityInfo( array $ids ) {
$retain = $this->convertEntityIdsToStrings( $ids );
$this->entityInfo = array_intersect_key( $this->entityInfo, array_flip( $retain ) );
}
}