/**
* Assigns an index to the given node which reflects the specified position.
* If the position is "before" or "after", an index will be chosen which makes
* the given node the previous or next node of the given reference node.
* If the position "last" is specified, an index higher than any existing index
* will be chosen.
*
* If no free index is available between two nodes (for "before" and "after"),
* the whole index of the current node level will be renumbered.
*
* @param NodeData $node The node to set the new index for
* @param integer $position The position the new index should reflect, must be one of the POSITION_* constants
* @param NodeInterface $referenceNode The reference node. Mandatory for POSITION_BEFORE and POSITION_AFTER
* @return void
* @throws \InvalidArgumentException
*/
public function setNewIndex(NodeData $node, $position, NodeInterface $referenceNode = null)
{
$parentPath = $node->getParentPath();
switch ($position) {
case self::POSITION_BEFORE:
if ($referenceNode === null) {
throw new \InvalidArgumentException('The reference node must be specified for POSITION_BEFORE.', 1317198857);
}
$referenceIndex = $referenceNode->getIndex();
$nextLowerIndex = $this->findNextLowerIndex($parentPath, $referenceIndex);
if ($nextLowerIndex === null) {
// FIXME: $nextLowerIndex returns 0 and not NULL in case no lower index is found. So this case seems to be
// never executed. We need to check that again!
$newIndex = (int) round($referenceIndex / 2);
} elseif ($nextLowerIndex < $referenceIndex - 1) {
// there is free space left between $referenceNode and preceding sibling.
$newIndex = (int) round($nextLowerIndex + ($referenceIndex - $nextLowerIndex) / 2);
} else {
// there is no free space left between $referenceNode and following sibling -> we have to make room!
$this->openIndexSpace($parentPath, $referenceIndex);
$referenceIndex = $referenceNode->getIndex();
$nextLowerIndex = $this->findNextLowerIndex($parentPath, $referenceIndex);
if ($nextLowerIndex === null) {
$newIndex = (int) round($referenceIndex / 2);
} else {
$newIndex = (int) round($nextLowerIndex + ($referenceIndex - $nextLowerIndex) / 2);
}
}
break;
case self::POSITION_AFTER:
if ($referenceNode === null) {
throw new \InvalidArgumentException('The reference node must be specified for POSITION_AFTER.', 1317198858);
}
$referenceIndex = $referenceNode->getIndex();
$nextHigherIndex = $this->findNextHigherIndex($parentPath, $referenceIndex);
if ($nextHigherIndex === null) {
// $referenceNode is last node, so we can safely add an index at the end by incrementing the reference index.
$newIndex = $referenceIndex + 100;
$this->setHighestIndexInParentPath($parentPath, $newIndex);
} elseif ($nextHigherIndex > $referenceIndex + 1) {
// $referenceNode is not last node, but there is free space left between $referenceNode and following sibling.
$newIndex = (int) round($referenceIndex + ($nextHigherIndex - $referenceIndex) / 2);
} else {
// $referenceNode is not last node, and no free space is left -> we have to make room after the reference node!
$this->openIndexSpace($parentPath, $referenceIndex + 1);
$nextHigherIndex = $this->findNextHigherIndex($parentPath, $referenceIndex);
if ($nextHigherIndex === null) {
$newIndex = $referenceIndex + 100;
$this->setHighestIndexInParentPath($parentPath, $newIndex);
} else {
$newIndex = (int) round($referenceIndex + ($nextHigherIndex - $referenceIndex) / 2);
}
}
break;
case self::POSITION_LAST:
$nextFreeIndex = $this->findNextFreeIndexInParentPath($parentPath);
$newIndex = $nextFreeIndex;
break;
default:
throw new \InvalidArgumentException('Invalid position for new node index given.', 1329729088);
}
$node->setIndex($newIndex);
}