1<?php
2
3/**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7
8declare(strict_types=1);
9
10namespace Nette\Iterators;
11
12use Nette;
13
14
15/**
16 * Smarter caching iterator.
17 *
18 * @property-read bool $first
19 * @property-read bool $last
20 * @property-read bool $empty
21 * @property-read bool $odd
22 * @property-read bool $even
23 * @property-read int $counter
24 * @property-read mixed $nextKey
25 * @property-read mixed $nextValue
26 */
27class CachingIterator extends \CachingIterator implements \Countable
28{
29	use Nette\SmartObject;
30
31	private int $counter = 0;
32
33
34	public function __construct($iterator)
35	{
36		if (is_array($iterator) || $iterator instanceof \stdClass) {
37			$iterator = new \ArrayIterator($iterator);
38
39		} elseif ($iterator instanceof \IteratorAggregate) {
40			do {
41				$iterator = $iterator->getIterator();
42			} while ($iterator instanceof \IteratorAggregate);
43
44			assert($iterator instanceof \Iterator);
45
46		} elseif ($iterator instanceof \Iterator) {
47		} elseif ($iterator instanceof \Traversable) {
48			$iterator = new \IteratorIterator($iterator);
49		} else {
50			throw new Nette\InvalidArgumentException(sprintf('Invalid argument passed to %s; array or Traversable expected, %s given.', self::class, get_debug_type($iterator)));
51		}
52
53		parent::__construct($iterator, 0);
54	}
55
56
57	/**
58	 * Is the current element the first one?
59	 */
60	public function isFirst(?int $gridWidth = null): bool
61	{
62		return $this->counter === 1 || ($gridWidth && $this->counter !== 0 && (($this->counter - 1) % $gridWidth) === 0);
63	}
64
65
66	/**
67	 * Is the current element the last one?
68	 */
69	public function isLast(?int $gridWidth = null): bool
70	{
71		return !$this->hasNext() || ($gridWidth && ($this->counter % $gridWidth) === 0);
72	}
73
74
75	/**
76	 * Is the iterator empty?
77	 */
78	public function isEmpty(): bool
79	{
80		return $this->counter === 0;
81	}
82
83
84	/**
85	 * Is the counter odd?
86	 */
87	public function isOdd(): bool
88	{
89		return $this->counter % 2 === 1;
90	}
91
92
93	/**
94	 * Is the counter even?
95	 */
96	public function isEven(): bool
97	{
98		return $this->counter % 2 === 0;
99	}
100
101
102	/**
103	 * Returns the counter.
104	 */
105	public function getCounter(): int
106	{
107		return $this->counter;
108	}
109
110
111	/**
112	 * Returns the count of elements.
113	 */
114	public function count(): int
115	{
116		$inner = $this->getInnerIterator();
117		if ($inner instanceof \Countable) {
118			return $inner->count();
119
120		} else {
121			throw new Nette\NotSupportedException('Iterator is not countable.');
122		}
123	}
124
125
126	/**
127	 * Forwards to the next element.
128	 */
129	public function next(): void
130	{
131		parent::next();
132		if (parent::valid()) {
133			$this->counter++;
134		}
135	}
136
137
138	/**
139	 * Rewinds the Iterator.
140	 */
141	public function rewind(): void
142	{
143		parent::rewind();
144		$this->counter = parent::valid() ? 1 : 0;
145	}
146
147
148	/**
149	 * Returns the next key.
150	 */
151	public function getNextKey(): mixed
152	{
153		return $this->getInnerIterator()->key();
154	}
155
156
157	/**
158	 * Returns the next element.
159	 */
160	public function getNextValue(): mixed
161	{
162		return $this->getInnerIterator()->current();
163	}
164}
165