From f5d1f1fb950937b7fa96707c9991eb6636e298d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kucha=C5=99?= <honza.kuchar@grifart.cz>
Date: Sat, 21 May 2016 14:15:33 +0200
Subject: [PATCH] ArrayCursorDriver refactored

---
 src/Cursor.php                                |   2 +-
 src/Driver/ICursorDriver.php                  |   2 +-
 src/Driver/PostgresCursorDriver.php           |   2 +-
 tests/Cursor/CursorTest.phpt                  |   2 +-
 .../Cursor}/Driver/ArrayCursorDriver.php      | 116 ++++++++++++------
 tests/Cursor/SemanticCursorIntegration.phpt   |   4 +-
 tests/Cursor/TrackedCursorTest.phpt           |   4 +-
 7 files changed, 88 insertions(+), 44 deletions(-)
 rename {src => tests/Cursor}/Driver/ArrayCursorDriver.php (51%)

diff --git a/src/Cursor.php b/src/Cursor.php
index 131f99e..d2ef5ba 100644
--- a/src/Cursor.php
+++ b/src/Cursor.php
@@ -57,7 +57,7 @@ final class Cursor implements ICursor
 		if ($index < 0) {
 			throw new \InvalidArgumentException("Negative index not supported. Use moveFromEndTo() instead.");
 		}
-		$this->headOnRecord = $this->driver->moveTo($this->name, $index);
+		$this->headOnRecord = $this->driver->moveFromBeginningTo($this->name, $index);
 	}
 
 	public function moveFromEndTo(int $index)
diff --git a/src/Driver/ICursorDriver.php b/src/Driver/ICursorDriver.php
index 1fd5493..5ce13f2 100644
--- a/src/Driver/ICursorDriver.php
+++ b/src/Driver/ICursorDriver.php
@@ -33,7 +33,7 @@ interface ICursorDriver
 	 * @param int    $index
 	 * @return bool Head on record?
 	 */
-	public function moveTo(string $name, int $index) : bool;
+	public function moveFromBeginningTo(string $name, int $index) : bool;
 
 	/**
 	 * Move cursor from the END to given position
diff --git a/src/Driver/PostgresCursorDriver.php b/src/Driver/PostgresCursorDriver.php
index 984424e..656337d 100644
--- a/src/Driver/PostgresCursorDriver.php
+++ b/src/Driver/PostgresCursorDriver.php
@@ -31,7 +31,7 @@ class PostgresCursorDriver implements ICursorDriver
 		return $this->connection;
 	}
 
-	public function moveTo(string $name, int $index) : bool
+	public function moveFromBeginningTo(string $name, int $index) : bool
 	{
 		if ($index < 0) {
 			throw new \InvalidArgumentException("Negative index not supported. Use moveFromEndTo() instead.");
diff --git a/tests/Cursor/CursorTest.phpt b/tests/Cursor/CursorTest.phpt
index e6f165e..efa8741 100644
--- a/tests/Cursor/CursorTest.phpt
+++ b/tests/Cursor/CursorTest.phpt
@@ -6,7 +6,7 @@
 namespace Grifart\Mappi\Tests\Cursor;
 
 use Grifart\Mappi\Cursor\Cursor;
-use Grifart\Mappi\Cursor\Driver\ArrayCursorDriver;
+use Grifart\Mappi\Tests\Cursor\Driver\ArrayCursorDriver;
 use Grifart\Mappi\Cursor\ICursor;
 
 require_once __DIR__ . "/../bootstrap.php";
diff --git a/src/Driver/ArrayCursorDriver.php b/tests/Cursor/Driver/ArrayCursorDriver.php
similarity index 51%
rename from src/Driver/ArrayCursorDriver.php
rename to tests/Cursor/Driver/ArrayCursorDriver.php
index 029dd0d..663988e 100644
--- a/src/Driver/ArrayCursorDriver.php
+++ b/tests/Cursor/Driver/ArrayCursorDriver.php
@@ -3,8 +3,9 @@
  * This file is part of mappi/cursor.
  */
 
-namespace Grifart\Mappi\Cursor\Driver;
+namespace Grifart\Mappi\Tests\Cursor\Driver;
 use Grifart\Mappi\Cursor\CursorException;
+use Grifart\Mappi\Cursor\Driver\ICursorDriver;
 
 /**
  * Array Cursor simulates scrolling cursor as implemented in PostgreSQL 9.5.
@@ -18,29 +19,64 @@ class ArrayCursorDriver implements ICursorDriver
 	private $cursors = [];
 	private $indexes = [];
 
+	/**
+	 * Creates test cursor with given name
+	 * @param string $name
+	 * @param int    $length
+	 */
 	public function createTestCursor(string $name, int $length)
 	{
 		if(isset($this->cursors[$name])) {
 			throw new CursorException("Cursor with name $name already exists");
 		}
 
-		$this->cursors[$name] = range(0, $length+1);
-		$this->cursors[$name][0] = null;
-		$this->cursors[$name][$length+1] = null;
+		$this->cursors[$name] = $this->generateCursorData($length);
 
 		$this->indexes[$name] = 0;
 	}
 
+	/**
+	 * Closes test cursor
+	 * @param string $name
+	 * @return bool
+	 */
 	public function close(string $name) : bool
 	{
 		unset($this->cursors[$name]);
+		unset($this->indexes[$name]);
 		return TRUE;
 	}
 
+	/**
+	 * Cursor data is table with one column with name "n" which contains current row index as value
+	 * @param int $length length of values
+	 * @return array
+	 */
+	private function generateCursorData(int $length): array
+	{
+		$data = [];
+		$data[0] = null;
+		$data[$length+1] = null;
+		for($i = 1; $i <= $length; $i++) {
+			$data[$i] = ["n" => $i];
+		}
+		return $data;
+	}
+
+	/**
+	 * Returns END position index from LEFT
+	 * @param string $name The cursor name
+	 * @return int
+	 */
 	private function getMaxIndex(string $name) {
 		return count($this->cursors[$name])-1;
 	}
 
+	/**
+	 * Normalizes index to be between BEGINNING and END of cursor
+	 * @param string $name The cursor name
+	 * @param int    $index The index to normalize
+	 */
 	private function normalizeIndex(string $name, int &$index)
 	{
 		if($index < 0) {
@@ -52,38 +88,46 @@ class ArrayCursorDriver implements ICursorDriver
 		}
 	}
 
-	private function pointerMoveTo(string $name, int $index)
+	/**
+	 * Moves internal cursor pointer to position from BEGINNING
+	 * @param string $name The cursor name
+	 * @param int    $index The index from the BEGINNING; index is normalized before move
+	 */
+	private function pointerMoveToFromBeginning(string $name, int $index)
 	{
 		$this->normalizeIndex($name, $index);
 		$this->indexes[$name] = $index;
 	}
 
-	private function getCurrentIndex(string $name) : int
+	/**
+	 * Moves internal cursor pointer to position from the END
+	 * @param string $name The cursor name
+	 * @param int    $index The index from the ENG; 
+	 */
+	private function pointerMoveToFromEnd(string $name, int $index)
+	{
+		$indexFromLeft = $this->getMaxIndex($name) - $index;
+		$this->normalizeIndex($name, $indexFromLeft);
+
+		$this->moveFromBeginningTo($name, $indexFromLeft);
+	}
+	
+	private function getCurrentIndexFromBeginning(string $name) : int
 	{
 		return $this->indexes[$name];
 	}
 
-	private function getCurrentValue(string $name) {
-		return $this->cursors[$name][$this->getCurrentIndex($name)];
+	private function retrieveCurrentRow(string $name) {
+		return $this->cursors[$name][$this->getCurrentIndexFromBeginning($name)];
 	}
 
-	public function moveTo(string $name, int $index) : bool
+	public function moveFromBeginningTo(string $name, int $index) : bool
 	{
 		if ($index < 0) {
 			throw new \InvalidArgumentException("Negative index not supported. Use moveFromEndTo() instead.");
 		}
-		$this->pointerMoveTo($name, $index);
-		return !!$this->getCurrentValue($name);
-	}
-
-	private function cursorArrayMoveFromEndTo(string $name, int $index)
-	{
-		$max = $this->getMaxIndex($name);
-
-		$indexFromLeft = $max - $index;
-		$this->normalizeIndex($name, $indexFromLeft);
-
-		$this->moveTo($name, $indexFromLeft);
+		$this->pointerMoveToFromBeginning($name, $index);
+		return !!$this->retrieveCurrentRow($name);
 	}
 
 	public function moveFromEndTo(string $name, int $index) : bool
@@ -91,24 +135,24 @@ class ArrayCursorDriver implements ICursorDriver
 		if ($index < 0) {
 			throw new \InvalidArgumentException("Negative index not supported. Use moveTo() instead.");
 		}
-		$this->cursorArrayMoveFromEndTo($name, $index);
-		return !!$this->getCurrentValue($name);
+		$this->pointerMoveToFromEnd($name, $index);
+		return !!$this->retrieveCurrentRow($name);
 	}
 
 	public function moveBy(string $name, int $offset) : bool
 	{
-		$i = $this->getCurrentIndex($name);
+		$i = $this->getCurrentIndexFromBeginning($name);
 		$indexFromLeft = $i + $offset;
 		$this->normalizeIndex($name, $indexFromLeft);
-		$this->moveTo($name, $indexFromLeft);
-		return !!$this->getCurrentValue($name);
+		$this->moveFromBeginningTo($name, $indexFromLeft);
+		return !!$this->retrieveCurrentRow($name);
 	}
 
 	// ----------------- FETCH ---------------------
 
 	public function fetchRange(string $name, int $offset) : array
 	{
-		$currentKey = $this->getCurrentIndex($name);
+		$currentKey = $this->getCurrentIndexFromBeginning($name);
 		if($offset === self::FETCH_FOREGOING) {
 			$finalKey = PHP_INT_MIN;
 		} elseif ($offset === self::FETCH_REMAINING) {
@@ -120,26 +164,26 @@ class ArrayCursorDriver implements ICursorDriver
 
 		$data = [];
 		if($offset === 0) {
-			$v = $this->getCurrentValue($name);
+			$v = $this->retrieveCurrentRow($name);
 			if($v !== NULL) {
-				$data[] = ["n" => $this->getCurrentValue($name)];
+				$data[] = $v;
 			}
 			return $data;
 		}
 
 		if($finalKey < $currentKey) { // backwards
 			for($i = $currentKey-1; $i >= $finalKey; $i--) {
-				$this->moveTo($name, $i);
-				$v = $this->getCurrentValue($name);
+				$this->moveFromBeginningTo($name, $i);
+				$v = $this->retrieveCurrentRow($name);
 				if($v === NULL) {break;}
-				$data[] = ["n" => $this->getCurrentValue($name)];
+				$data[] = $v;
 			}
 		} else {
 			for($i = $currentKey+1; $i <= $finalKey; $i++) {
-				$this->moveTo($name, $i);
-				$v = $this->getCurrentValue($name);
+				$this->moveFromBeginningTo($name, $i);
+				$v = $this->retrieveCurrentRow($name);
 				if($v === NULL) {break;}
-				$data[] = ["n" => $this->getCurrentValue($name)];
+				$data[] = $v;
 			}
 		}
 		return $data;
@@ -150,7 +194,7 @@ class ArrayCursorDriver implements ICursorDriver
 		if($index < 0) {
 			$this->moveFromEndTo($name, abs($index));
 		} else {
-			$this->moveTo($name, $index);
+			$this->moveFromBeginningTo($name, abs($index));
 		}
 		$data = $this->fetchRange($name, 0);
 		if(count($data) === 0) {
diff --git a/tests/Cursor/SemanticCursorIntegration.phpt b/tests/Cursor/SemanticCursorIntegration.phpt
index b343ec8..5cf8cb2 100644
--- a/tests/Cursor/SemanticCursorIntegration.phpt
+++ b/tests/Cursor/SemanticCursorIntegration.phpt
@@ -6,7 +6,7 @@
 namespace Grifart\Mappi\Tests\Cursor;
 
 use Grifart\Mappi\Cursor\Cursor;
-use Grifart\Mappi\Cursor\Driver\ArrayCursorDriver;
+use Grifart\Mappi\Tests\Cursor\Driver\ArrayCursorDriver;
 use Grifart\Mappi\Cursor\SemanticCursor;
 use Mockery;
 
@@ -27,7 +27,7 @@ class SemanticCursorIntegrationTest extends ICursorTest
 
 	protected function setUp()
 	{
-		$driver = new ArrayCursorDriver();
+		$driver = new Driver\ArrayCursorDriver();
 		$driver->createTestCursor("test", 1000);
 		$cursor = new Cursor(
 			$driver,
diff --git a/tests/Cursor/TrackedCursorTest.phpt b/tests/Cursor/TrackedCursorTest.phpt
index 5704735..ade58f5 100644
--- a/tests/Cursor/TrackedCursorTest.phpt
+++ b/tests/Cursor/TrackedCursorTest.phpt
@@ -10,7 +10,7 @@ namespace Grifart\Mappi\Tests\Cursor;
 
 use Grifart\Mappi\Cursor\Cursor;
 use Grifart\Mappi\Cursor\CursorPosition;
-use Grifart\Mappi\Cursor\Driver\ArrayCursorDriver;
+use Grifart\Mappi\Tests\Cursor\Driver\ArrayCursorDriver;
 use Grifart\Mappi\Cursor\TrackedCursor;
 use Tester\Assert;
 
@@ -27,7 +27,7 @@ class TrackedCursorTest extends ICursorTest
 
 	protected function setUp()
 	{
-		$driver = new ArrayCursorDriver();
+		$driver = new Driver\ArrayCursorDriver();
 		$driver->createTestCursor("test", 1000);
 		$cursor = new Cursor(
 			$driver,
-- 
GitLab