1<?php
2
3/**
4 * Hoa
5 *
6 *
7 * @license
8 *
9 * New BSD License
10 *
11 * Copyright © 2007-2017, Hoa community. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 *     * Redistributions of source code must retain the above copyright
16 *       notice, this list of conditions and the following disclaimer.
17 *     * Redistributions in binary form must reproduce the above copyright
18 *       notice, this list of conditions and the following disclaimer in the
19 *       documentation and/or other materials provided with the distribution.
20 *     * Neither the name of the Hoa nor the names of its contributors may be
21 *       used to endorse or promote products derived from this software without
22 *       specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37namespace Hoa\Iterator;
38
39/**
40 * Class \Hoa\Iterator\Buffer.
41 *
42 * Buffer iterator: Can go backward up to a certain limit, and forward.
43 *
44 * @copyright  Copyright © 2007-2017 Hoa community
45 * @license    New BSD License
46 */
47class Buffer extends IteratorIterator implements Outer
48{
49    /**
50     * Buffer key index.
51     *
52     * @const int
53     */
54    const BUFFER_KEY   = 0;
55
56    /**
57     * Buffer value index.
58     *
59     * @const int
60     */
61    const BUFFER_VALUE = 1;
62
63    /**
64     * Current iterator.
65     *
66     * @var \Iterator
67     */
68    protected $_iterator   = null;
69
70    /**
71     * Buffer.
72     *
73     * @var \SplDoublyLinkedList
74     */
75    protected $_buffer     = null;
76
77    /**
78     * Maximum buffer size.
79     *
80     * @var int
81     */
82    protected $_bufferSize = 1;
83
84
85
86    /**
87     * Construct.
88     *
89     * @param   \Iterator  $iterator      Iterator.
90     * @param   int        $bufferSize    Buffer size.
91     */
92    public function __construct(\Iterator $iterator, $bufferSize)
93    {
94        $this->_iterator   = $iterator;
95        $this->_bufferSize = max($bufferSize, 1);
96        $this->_buffer     = new \SplDoublyLinkedList();
97
98        return;
99    }
100
101    /**
102     * Get inner iterator.
103     *
104     * @return  \Iterator
105     */
106    public function getInnerIterator()
107    {
108        return $this->_iterator;
109    }
110
111    /**
112     * Get buffer.
113     *
114     * @return  \SplDoublyLinkedList
115     */
116    protected function getBuffer()
117    {
118        return $this->_buffer;
119    }
120
121    /**
122     * Get buffer size.
123     *
124     * @return  int
125     */
126    public function getBufferSize()
127    {
128        return $this->_bufferSize;
129    }
130
131    /**
132     * Return the current element.
133     *
134     * @return  mixed
135     */
136    public function current()
137    {
138        return $this->getBuffer()->current()[self::BUFFER_VALUE];
139    }
140
141    /**
142     * Return the key of the current element.
143     *
144     * @return  mixed
145     */
146    public function key()
147    {
148        return $this->getBuffer()->current()[self::BUFFER_KEY];
149    }
150
151    /**
152     * Move forward to next element.
153     *
154     * @return  void
155     */
156    public function next()
157    {
158        $innerIterator = $this->getInnerIterator();
159        $buffer        = $this->getBuffer();
160
161        $buffer->next();
162
163        // End of the buffer, need a new value.
164        if (false === $buffer->valid()) {
165            for (
166                $bufferSize        = count($buffer),
167                $maximumBufferSize = $this->getBufferSize();
168                $bufferSize >= $maximumBufferSize;
169                --$bufferSize
170            ) {
171                $buffer->shift();
172            }
173
174            $innerIterator->next();
175
176            $buffer->push([
177                self::BUFFER_KEY   => $innerIterator->key(),
178                self::BUFFER_VALUE => $innerIterator->current()
179            ]);
180
181            // Seek to the end of the buffer.
182            $buffer->setIteratorMode($buffer::IT_MODE_LIFO | $buffer::IT_MODE_KEEP);
183            $buffer->rewind();
184            $buffer->setIteratorMode($buffer::IT_MODE_FIFO | $buffer::IT_MODE_KEEP);
185        }
186
187        return;
188    }
189
190    /**
191     * Move backward to previous element.
192     *
193     * @return  void
194     */
195    public function previous()
196    {
197        $this->getBuffer()->prev();
198
199        return;
200    }
201
202    /**
203     * Rewind the iterator to the first element.
204     *
205     * @return  void
206     */
207    public function rewind()
208    {
209        $innerIterator = $this->getInnerIterator();
210        $buffer        = $this->getBuffer();
211
212        $innerIterator->rewind();
213
214        if (true === $buffer->isEmpty()) {
215            $buffer->push([
216                self::BUFFER_KEY   => $innerIterator->key(),
217                self::BUFFER_VALUE => $innerIterator->current()
218            ]);
219        }
220
221        $buffer->rewind();
222
223        return;
224    }
225
226    /**
227     * Check if current position is valid.
228     *
229     * @return  bool
230     */
231    public function valid()
232    {
233        return
234            $this->getBuffer()->valid() &&
235            $this->getInnerIterator()->valid();
236    }
237}
238