Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ListIterator |
|
| 0.0;0 |
1 | /* | |
2 | * Licensed under the MIT License | |
3 | * | |
4 | * Copyright 2010 (c) Flávio Silva, http://flsilva.com | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person | |
7 | * obtaining a copy of this software and associated documentation | |
8 | * files (the "Software"), to deal in the Software without | |
9 | * restriction, including without limitation the rights to use, | |
10 | * copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | * copies of the Software, and to permit persons to whom the | |
12 | * Software is furnished to do so, subject to the following | |
13 | * conditions: | |
14 | * | |
15 | * The above copyright notice and this permission notice shall be | |
16 | * included in all copies or substantial portions of the Software. | |
17 | * | |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
25 | * OTHER DEALINGS IN THE SOFTWARE. | |
26 | * | |
27 | * http://www.opensource.org/licenses/mit-license.php | |
28 | */ | |
29 | ||
30 | 1 | package org.as3collections.iterators |
31 | { | |
32 | import org.as3collections.IList; | |
33 | import org.as3collections.IListIterator; | |
34 | import org.as3collections.errors.ConcurrentModificationError; | |
35 | import org.as3collections.errors.IndexOutOfBoundsError; | |
36 | import org.as3collections.errors.NoSuchElementError; | |
37 | import org.as3coreaddendum.errors.IllegalStateError; | |
38 | ||
39 | /** | |
40 | * An iterator to iterate over lists (implementations of the <code>IList</code> interface). | |
41 | * <code>ListIterator</code> allows to traverse the list in either direction. | |
42 | * <p><b>IMPORTANT:</b></p> | |
43 | * <p>A <code>ListIterator</code> has no current element; its cursor position always lies between the element that would be returned by a call to <code>previous()</code> and the element that would be returned by a call to <code>next()</code>. | |
44 | * An iterator for a list of length <code>n</code> has <code>n+1</code> possible cursor positions, as illustrated by the carets (^) below:</p> | |
45 | * <p>                                 | |
46 | * Element(0)        | |
47 | * Element(1)        | |
48 | * Element(2)        | |
49 | * ... Element(n-1)</p> | |
50 | * <p>cursor positions: | |
51 | *     | |
52 | * ^                     | |
53 | * ^                      | |
54 | * ^                     | |
55 | * ^                             | |
56 | * ^</p> | |
57 | * <p>Note that the <code>remove()</code> and <code>set()</code> methods are <em>not</em> defined in terms of the cursor position; they are defined to operate on the last element returned by a call to <code>next()</code> or <code>previous()</code>.</p> | |
58 | * <p>For further information do not hesitate to see the examples at the end of the page.</p> | |
59 | * <p>This documentation is partially based in the <em>Java Collections Framework</em> JavaDoc documentation. | |
60 | * For further information see <a href="http://download.oracle.com/javase/6/docs/technotes/guides/collections/index.html" target="_blank">Java Collections Framework</a></p> | |
61 | * | |
62 | * @example | |
63 | * | |
64 | * <b>Example 1</b> | |
65 | * | |
66 | * <listing version="3.0"> | |
67 | * import org.as3collections.IList; | |
68 | * import org.as3collections.IListIterator; | |
69 | * import org.as3collections.lists.ArrayList; | |
70 | * | |
71 | * var list1:IList = new ArrayList([1, 3, 5]); | |
72 | * | |
73 | * list1 // [1,3,5] | |
74 | * | |
75 | * var it:IListIterator = list1.listIterator(); | |
76 | * var e:int; | |
77 | * | |
78 | * while (it.hasNext()) | |
79 | * { | |
80 | * | |
81 | * ITERATION N.1 | |
82 | * | |
83 | * it.pointer() // -1 | |
84 | * it.nextIndex() // 0 | |
85 | * it.previousIndex() // -1 | |
86 | * | |
87 | * e = it.next(); | |
88 | * e // 1 | |
89 | * | |
90 | * it.pointer() // 0 | |
91 | * it.nextIndex() // 1 | |
92 | * it.previousIndex() // 0 | |
93 | * | |
94 | * ITERATION N.2 | |
95 | * | |
96 | * it.pointer() // 0 | |
97 | * it.nextIndex() // 1 | |
98 | * it.previousIndex() // 0 | |
99 | * | |
100 | * e = it.next(); | |
101 | * e // 3 | |
102 | * | |
103 | * it.pointer() // 1 | |
104 | * it.nextIndex() // 2 | |
105 | * it.previousIndex() // 1 | |
106 | * | |
107 | * if (e == 3) | |
108 | * { | |
109 | * //list1.add(4) // ConcurrentModificationError: During the iteration, the list was changed directly (without use the iterator). | |
110 | * it.add(4); | |
111 | * list1 // [1,3,4,5] | |
112 | * } | |
113 | * | |
114 | * ITERATION N.3 | |
115 | * | |
116 | * it.pointer() // 2 | |
117 | * it.nextIndex() // 3 | |
118 | * it.previousIndex() // 2 | |
119 | * | |
120 | * e = it.next(); | |
121 | * e // 5 | |
122 | * | |
123 | * it.pointer() // 3 | |
124 | * it.nextIndex() // 4 | |
125 | * it.previousIndex() // 3 | |
126 | * | |
127 | * if (e == 5) | |
128 | * { | |
129 | * it.remove(); | |
130 | * list1 // [1,3,4] | |
131 | * } | |
132 | * } | |
133 | * </listing> | |
134 | * | |
135 | * <b>Example 2</b> | |
136 | * | |
137 | * <listing version="3.0"> | |
138 | * import org.as3collections.IList; | |
139 | * import org.as3collections.IListIterator; | |
140 | * import org.as3collections.lists.ArrayList; | |
141 | * | |
142 | * var list1:IList = new ArrayList([1, 3, 5]); | |
143 | * | |
144 | * list1 // [1,3,5] | |
145 | * | |
146 | * var it:IListIterator = list1.listIterator(list1.size()); | |
147 | * var e:int; | |
148 | * | |
149 | * while (it.hasPrevious()) | |
150 | * | |
151 | * { | |
152 | * | |
153 | * ITERATION N.1 | |
154 | * | |
155 | * it.pointer() // 2 | |
156 | * it.nextIndex() // 3 | |
157 | * it.previousIndex() // 2 | |
158 | * | |
159 | * e = it.previous(); | |
160 | * e // 5 | |
161 | * | |
162 | * it.pointer() // 1 | |
163 | * it.nextIndex() // 2 | |
164 | * it.previousIndex() // 1 | |
165 | * | |
166 | * if (e == 5) | |
167 | * { | |
168 | * it.remove() | |
169 | * list1 // [1,3] | |
170 | * } | |
171 | * | |
172 | * ITERATION N.2 | |
173 | * | |
174 | * it.pointer() // 1 | |
175 | * it.nextIndex() // 2 | |
176 | * it.previousIndex() // 1 | |
177 | * | |
178 | * e = it.previous(); | |
179 | * e // 3 | |
180 | * | |
181 | * it.pointer() // 0 | |
182 | * it.nextIndex() // 1 | |
183 | * it.previousIndex() // 0 | |
184 | * | |
185 | * if (e == 3) | |
186 | * { | |
187 | * //list1.add(4) // ConcurrentModificationError: During the iteration, the list was changed directly (without use the iterator). | |
188 | * it.add(4); | |
189 | * list1 // [1,4,3] | |
190 | * } | |
191 | * | |
192 | * ITERATION N.3 | |
193 | * | |
194 | * it.pointer() // 1 | |
195 | * it.nextIndex() // 2 | |
196 | * it.previousIndex() // 1 | |
197 | * | |
198 | * e = it.previous(); | |
199 | * e // 4 | |
200 | * | |
201 | * it.pointer() // 0 | |
202 | * it.nextIndex() // 1 | |
203 | * it.previousIndex() // 0 | |
204 | * | |
205 | * ITERATION N.4 | |
206 | * | |
207 | * it.pointer() // 0 | |
208 | * it.nextIndex() // 1 | |
209 | * it.previousIndex() // 0 | |
210 | * | |
211 | * e = it.previous(); | |
212 | * e // 1 | |
213 | * | |
214 | * it.pointer() // -1 | |
215 | * it.nextIndex() // 0 | |
216 | * it.previousIndex() // -1 | |
217 | * } | |
218 | * </listing> | |
219 | * | |
220 | * @author Flávio Silva | |
221 | */ | |
222 | public class ListIterator implements IListIterator | |
223 | { | |
224 | private var _allowModification: Boolean; | |
225 | private var _modCount: int; | |
226 | private var _pointer: int = -1; | |
227 | private var _removePointer: int; | |
228 | private var _source: IList; | |
229 | ||
230 | /** | |
231 | * Constructor, creates a new <code>ListIterator</code> object. | |
232 | * | |
233 | * @param source the source <code>ListIterator</code> to iterate over. | |
234 | * @param position indicates the first element that would be returned by an initial call to <code>next</code>. An initial call to <code>previous</code> would return the element with the specified position minus one. | |
235 | * @throws ArgumentError if the <code>source</code> argument is <code>null</code>. | |
236 | */ | |
237 | public function ListIterator(source:IList, position:int = 0) | |
238 | 1 | { |
239 | 1 | if (!source) throw new ArgumentError("The 'source' argument must not be 'null'."); |
240 | 1 | if (position < 0 || position > source.size()) throw new IndexOutOfBoundsError("The 'position' argument is out of bounds: " + position + " (min: 0, max: " + source.size() + ")"); |
241 | ||
242 | 1 | _source = source; |
243 | 1 | _modCount = _source.modCount; |
244 | 1 | _pointer += position; |
245 | 1 | } |
246 | ||
247 | /** | |
248 | * @inheritDoc | |
249 | * @throws org.as3collections.errors.ConcurrentModificationError if the list was changed directly (without using the iterator) during iteration. | |
250 | */ | |
251 | public function add(element:*): Boolean | |
252 | { | |
253 | 1 | checkConcurrentModificationError(); |
254 | ||
255 | 1 | var added:Boolean = _source.addAt(_pointer + 1, element); |
256 | ||
257 | 1 | if (_modCount != _source.modCount) _pointer++; |
258 | 1 | _modCount = _source.modCount; |
259 | 1 | _allowModification = false; |
260 | ||
261 | 1 | return added; |
262 | } | |
263 | ||
264 | /** | |
265 | * @inheritDoc | |
266 | */ | |
267 | public function hasNext(): Boolean | |
268 | { | |
269 | 1 | return _pointer < _source.size() - 1; |
270 | } | |
271 | ||
272 | /** | |
273 | * @inheritDoc | |
274 | */ | |
275 | public function hasPrevious(): Boolean | |
276 | { | |
277 | 1 | return _pointer >= 0; |
278 | } | |
279 | ||
280 | /** | |
281 | * @inheritDoc | |
282 | * @throws org.as3collections.errors.NoSuchElementError if the iteration has no more elements. | |
283 | * @throws org.as3collections.errors.ConcurrentModificationError if the list was changed directly (without using the iterator) during iteration. | |
284 | */ | |
285 | public function next(): * | |
286 | { | |
287 | 1 | if (!hasNext()) throw new NoSuchElementError("Iterator has no next element. Call hasNext() method before."); |
288 | ||
289 | 1 | checkConcurrentModificationError(); |
290 | 1 | _allowModification = true; |
291 | 1 | _pointer++; |
292 | 1 | _removePointer = _pointer; |
293 | 1 | return _source.getAt(_pointer); |
294 | } | |
295 | ||
296 | /** | |
297 | * @inheritDoc | |
298 | */ | |
299 | public function nextIndex(): int | |
300 | { | |
301 | 1 | return _pointer + 1; |
302 | } | |
303 | ||
304 | /** | |
305 | * Returns the internal pointer of the iteration. | |
306 | * <p>In this implementation the pointer is the index (position) of the iteration, typically an <code>int</code>.</p> | |
307 | * | |
308 | * @return the internal pointer of the iteration. | |
309 | */ | |
310 | public function pointer(): * | |
311 | { | |
312 | 1 | return _pointer; |
313 | } | |
314 | ||
315 | /** | |
316 | * @inheritDoc | |
317 | * @throws org.as3collections.errors.NoSuchElementError if the iteration has no previous elements. | |
318 | * @throws org.as3collections.errors.ConcurrentModificationError if the list was changed directly (without using the iterator) during iteration. | |
319 | */ | |
320 | public function previous(): * | |
321 | { | |
322 | 1 | if (!hasPrevious()) throw new NoSuchElementError("Iterator has no previous element. Call hasPrevious() method before."); |
323 | ||
324 | 1 | checkConcurrentModificationError(); |
325 | 1 | _allowModification = true; |
326 | 1 | _removePointer = _pointer; |
327 | 1 | return _source.getAt(_pointer--); |
328 | } | |
329 | ||
330 | /** | |
331 | * @inheritDoc | |
332 | */ | |
333 | public function previousIndex(): int | |
334 | { | |
335 | 1 | return _pointer; |
336 | } | |
337 | ||
338 | /** | |
339 | * Removes from the list the last element that was returned by <code>next</code> or <code>previous</code>. This call can only be made once per call to <code>next</code> or <code>previous</code>. It can be made only if <code>IListIterator.add</code> has not been called after the last call to <code>next</code> or <code>previous</code>. | |
340 | * | |
341 | * @throws org.as3coreaddendum.errors.UnsupportedOperationError if the <code>remove</code> operation is not supported by this iterator. | |
342 | * @throws org.as3coreaddendum.errors.IllegalStateError if the <code>next</code> method has not yet been called, or the <code>remove</code> method has already been called after the last call to the <code>next</code> method. | |
343 | * @throws org.as3collections.errors.ConcurrentModificationError if the list was changed directly (without using the iterator) during iteration. | |
344 | */ | |
345 | public function remove(): void | |
346 | { | |
347 | 1 | checkConcurrentModificationError(); |
348 | ||
349 | 1 | if (!_allowModification) throw new IllegalStateError("The next or previous method has not yet been called or the add or remove method has already been called after the last call to the next or previous method."); |
350 | ||
351 | 1 | _source.removeAt(_removePointer); |
352 | 1 | _modCount = _source.modCount; |
353 | 1 | _allowModification = false; |
354 | 1 | if (_removePointer == _pointer) _pointer--; |
355 | 1 | } |
356 | ||
357 | /** | |
358 | * @inheritDoc | |
359 | */ | |
360 | public function reset(): void | |
361 | { | |
362 | 1 | _pointer = -1; |
363 | 1 | } |
364 | ||
365 | /** | |
366 | * Replaces the last element returned by <code>next</code> or <code>previous</code> with the specified element (optional operation). This call can be made only if neither <code>IListIterator.remove</code> nor <code>IListIterator.add</code> have been called after the last call to <code>next</code> or <code>previous</code>. | |
367 | * | |
368 | * @param element the element with which to replace the last element returned by <code>next</code> or <code>previous</code>. | |
369 | * @throws org.as3coreaddendum.errors.UnsupportedOperationError if the <code>set</code> operation is not supported by this iterator. | |
370 | * @throws org.as3coreaddendum.errors.ClassCastError if the class of the specified element prevents it from being added to this list. | |
371 | * @throws org.as3coreaddendum.errors.IllegalStateError if neither <code>next</code> or <code>previous</code> have been called, or <code>remove</code> or <code>add</code> have been called after the last call to <code>next</code> or <code>previous</code>. | |
372 | */ | |
373 | public function set(element:*): void | |
374 | { | |
375 | 1 | checkConcurrentModificationError(); |
376 | ||
377 | 1 | if (!_allowModification) throw new IllegalStateError("The next or previous method has not yet been called or the add or remove method has already been called after the last call to the next or previous method."); |
378 | ||
379 | 1 | var setIndex:int = (_removePointer > _pointer) ? _pointer + 1 : _pointer; |
380 | ||
381 | 1 | _source.setAt(setIndex, element); |
382 | 1 | _modCount = _source.modCount; |
383 | 1 | } |
384 | ||
385 | /** | |
386 | * @private | |
387 | */ | |
388 | public function checkConcurrentModificationError(): void | |
389 | { | |
390 | 1 | if (_modCount != _source.modCount) throw new ConcurrentModificationError("During the iteration, the list was changed directly (without use the iterator)."); |
391 | 1 | } |
392 | ||
393 | } | |
394 | ||
395 | } |