Current File : /home/jvzmxxx/wiki1/extensions/Wikibase/repo/includes/ChangeOp/ChangeOpStatement.php
<?php

namespace Wikibase\ChangeOp;

use InvalidArgumentException;
use OutOfBoundsException;
use ValueValidators\Result;
use Wikibase\DataModel\ByPropertyIdArray;
use Wikibase\DataModel\Entity\EntityDocument;
use Wikibase\DataModel\Services\Statement\GuidGenerator;
use Wikibase\DataModel\Services\Statement\StatementGuidParser;
use Wikibase\DataModel\Services\Statement\StatementGuidValidator;
use Wikibase\DataModel\Statement\Statement;
use Wikibase\DataModel\Statement\StatementList;
use Wikibase\DataModel\Statement\StatementListHolder;
use Wikibase\Repo\Validators\SnakValidator;
use Wikibase\Summary;

/**
 * Class for statement modification operations
 *
 * @since 0.4
 *
 * @license GPL-2.0+
 * @author Addshore
 * @author H. Snater < mediawiki@snater.com >
 * @author Thiemo Mättig
 * @author Bene* < benestar.wikimedia@gmail.com >
 */
class ChangeOpStatement extends ChangeOpBase {

	/**
	 * @var Statement
	 */
	private $statement;

	/**
	 * @var GuidGenerator
	 */
	private $guidGenerator;

	/**
	 * @var StatementGuidValidator
	 */
	private $guidValidator;

	/**
	 * @var StatementGuidParser
	 */
	private $guidParser;

	/**
	 * @var SnakValidator
	 */
	private $snakValidator;

	/**
	 * @var int|null
	 */
	private $index;

	/**
	 * @param Statement $statement
	 * @param GuidGenerator $guidGenerator
	 * @param StatementGuidValidator $guidValidator
	 * @param StatementGuidParser $guidParser
	 * @param SnakValidator $snakValidator
	 * @param int|null $index Where the claim should be placed among the other claims.
	 *
	 * @throws InvalidArgumentException
	 */
	public function __construct(
		Statement $statement,
		GuidGenerator $guidGenerator,
		StatementGuidValidator $guidValidator,
		StatementGuidParser $guidParser,
		SnakValidator $snakValidator,
		$index = null
	) {
		if ( !is_int( $index ) && $index !== null ) {
			throw new InvalidArgumentException( '$index must be an integer or null' );
		}

		$this->statement = $statement;
		$this->guidGenerator = $guidGenerator;
		$this->guidValidator = $guidValidator;
		$this->guidParser = $guidParser;
		$this->snakValidator = $snakValidator;
		$this->index = $index;
	}

	/**
	 * @see ChangeOp::apply
	 *
	 * @param EntityDocument $entity
	 * @param Summary|null $summary
	 *
	 * @throws InvalidArgumentException
	 * @throws ChangeOpException
	 */
	public function apply( EntityDocument $entity, Summary $summary = null ) {
		if ( !( $entity instanceof StatementListHolder ) ) {
			throw new InvalidArgumentException( '$entity must be a StatementListHolder' );
		}

		if ( $this->statement->getGuid() === null ) {
			$this->statement->setGuid( $this->guidGenerator->newGuid( $entity->getId() ) );
		}

		$guid = $this->guidParser->parse( $this->statement->getGuid() );

		if ( $this->guidValidator->validate( $guid->getSerialization() ) === false ) {
			throw new ChangeOpException( "Claim does not have a valid GUID" );
		} elseif ( !$entity->getId()->equals( $guid->getEntityId() ) ) {
			throw new ChangeOpException( "Claim GUID invalid for given entity" );
		}

		$this->applyStatementToEntity( $entity, $summary );
	}

	/**
	 * @param StatementListHolder $statementListHolder
	 * @param Summary|null $summary
	 *
	 * @throws InvalidArgumentException
	 */
	private function applyStatementToEntity( StatementListHolder $statementListHolder, Summary $summary = null ) {
		$statements = $this->removeStatement( $statementListHolder->getStatements()->toArray(), $summary );
		$statements = $this->addStatement( $statements );
		$statementListHolder->setStatements( new StatementList( $statements ) );
	}

	/**
	 * @param Statement[] $statements
	 * @param Summary|null $summary
	 *
	 * @return Statement[]
	 */
	private function removeStatement( array $statements, Summary $summary = null ) {
		$guid = $this->statement->getGuid();
		$newStatements = array();
		$oldStatement = null;

		foreach ( $statements as $statement ) {
			if ( $statement->getGuid() === $guid && $oldStatement === null ) {
				$oldStatement = $statement;

				if ( $this->index === null ) {
					$this->index = count( $newStatements );
				}
			} else {
				$newStatements[] = $statement;
			}
		}

		if ( $oldStatement === null ) {
			$this->updateSummary( $summary, 'create' );
		} else {
			$this->checkMainSnakUpdate( $oldStatement );
			$this->updateSummary( $summary, 'update' );
		}

		return $newStatements;
	}

	/**
	 * Checks that the update of the main snak is permissible.
	 *
	 * This checks that the main snaks of the old and the new claim
	 * refer to the same property.
	 *
	 * @param Statement $oldStatement
	 *
	 * @throws ChangeOpException If the main snak update is illegal.
	 */
	private function checkMainSnakUpdate( Statement $oldStatement ) {
		$newMainSnak = $this->statement->getMainSnak();
		$oldPropertyId = $oldStatement->getMainSnak()->getPropertyId();

		if ( !$oldPropertyId->equals( $newMainSnak->getPropertyId() ) ) {
			$guid = $this->statement->getGuid();
			throw new ChangeOpException( "Claim with GUID $guid uses property "
				. $oldPropertyId . ", can't change to "
				. $newMainSnak->getPropertyId() );
		}
	}

	/**
	 * @param Statement[] $statements
	 *
	 * @throws ChangeOpException
	 * @return Statement[]
	 */
	private function addStatement( array $statements ) {
		// If we fail with the user supplied index and the index is greater than or equal 0
		// presume the user wants to have the index at the end of the list.
		if ( $this->index < 0 ) {
			throw new ChangeOpException( 'Can not add claim at given index: '. $this->index );
		}

		$indexedStatements = new ByPropertyIdArray( $statements );
		$indexedStatements->buildIndex();

		try {
			$indexedStatements->addObjectAtIndex( $this->statement, $this->index );
			$statements = $indexedStatements->toFlatArray();
		} catch ( OutOfBoundsException $ex ) {
			$statements[] = $this->statement;
		}

		return $statements;
	}

	/**
	 * @see ChangeOp::validate
	 *
	 * @param EntityDocument $entity
	 *
	 * @return Result
	 */
	public function validate( EntityDocument $entity ) {
		return $this->snakValidator->validateClaimSnaks( $this->statement );
	}

}