Coverage Report - org.as3collections.iterators.ListMapIterator
 
Classes in this File Line Coverage Branch Coverage Complexity
ListMapIterator
100%
49/49
N/A
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.IListMap;
 33  
         import org.as3collections.IListMapIterator;
 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 implementations of <code>IListMap</code> interface.
 41  
          * <code>ListMapIterator</code> allows to traverse the map in either direction.
 42  
          * <p><b>IMPORTANT:</b></p>
 43  
          * <p>A <code>ListMapIterator</code> has no current mapping; its cursor position always lies between the mapping that would be returned by a call to <code>previous()</code> and the mapping that would be returned by a call to <code>next()</code>.
 44  
          * An iterator for a map of length <code>n</code> has <code>n+1</code> possible cursor positions, as illustrated by the carets (^) below:</p>
 45  
          * <p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 46  
          * Element(0)&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 47  
          * Element(1)&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 48  
          * Element(2)&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 49  
          * ... Element(n-1)</p>
 50  
          * <p>cursor positions:
 51  
          * &#160;&#160;&#160;
 52  
          * ^&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 53  
          * ^&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 54  
          * ^&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 55  
          * ^&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 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 mapping 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.IListMap;
 68  
          * import org.as3collections.IListMapIterator;
 69  
          * import org.as3collections.maps.ArrayListMap;
 70  
          * 
 71  
          * var map1:IListMap = new ArrayListMap();
 72  
          * map1.put("element-1", 1);
 73  
          * map1.put("element-3", 3);
 74  
          * map1.put("element-5", 5);
 75  
          * 
 76  
          * map1                                // ["element-1"=1,"element-3"=3,"element-5"=5]
 77  
          * 
 78  
          * var it:IListMapIterator = map1.listMapIterator();
 79  
          * var e:int;
 80  
          * 
 81  
          * while (it.hasNext())
 82  
          * {
 83  
          * 
 84  
          *     ITERATION N.1
 85  
          * 
 86  
          *     it.pointer()                    // null
 87  
          *     it.nextIndex()                  // 0
 88  
          *     it.previousIndex()              // -1
 89  
          * 
 90  
          *     e = it.next();
 91  
          *     e                               // 1
 92  
          * 
 93  
          *     it.pointer()                    // "element-1"
 94  
          *     it.nextIndex()                  // 1
 95  
          *     it.previousIndex()              // 0
 96  
          * 
 97  
          *     ITERATION N.2
 98  
          * 
 99  
          *     it.pointer()                    // "element-1"
 100  
          *     it.nextIndex()                  // 1
 101  
          *     it.previousIndex()              // 0
 102  
          * 
 103  
          *     e = it.next();
 104  
          *     e                               // 3
 105  
          * 
 106  
          *     it.pointer()                    // "element-3"
 107  
          *     it.nextIndex()                  // 2
 108  
          *     it.previousIndex()              // 1
 109  
          * 
 110  
          *     if (e == 3)
 111  
          *     {
 112  
          *         //map1.put("element-4", 4)  // ConcurrentModificationError: During the iteration, the map was changed directly (without use the iterator).
 113  
          *         it.put("element-4", 4);
 114  
          *         map1                        // ["element-1"=1,"element-3"=3,"element-4"=4,"element-5"=5]
 115  
          *     }
 116  
          * 
 117  
          *     ITERATION N.3
 118  
          * 
 119  
          *     it.pointer()                    // "element-4"
 120  
          *     it.nextIndex()                  // 3
 121  
          *     it.previousIndex()              // 2
 122  
          * 
 123  
          *     e = it.next();
 124  
          *     e                               // 5
 125  
          * 
 126  
          *     it.pointer()                    // "element-5"
 127  
          *     it.nextIndex()                  // 4
 128  
          *     it.previousIndex()              // 3
 129  
          * 
 130  
          *     if (e == 5)
 131  
          *     {
 132  
          *         it.remove();
 133  
          *         map1                        // ["element-1"=1,"element-3"=3,"element-4"=4]
 134  
          *     }
 135  
          * }
 136  
          * </listing>
 137  
          * 
 138  
          * <b>Example 2</b>
 139  
          * 
 140  
          * <listing version="3.0">
 141  
          * import org.as3collections.IListMap;
 142  
          * import org.as3collections.IListMapIterator;
 143  
          * import org.as3collections.maps.ArrayListMap;
 144  
          * 
 145  
          * var map1:IListMap = new ArrayListMap();
 146  
          * map1.put("element-1", 1);
 147  
          * map1.put("element-3", 3);
 148  
          * map1.put("element-5", 5);
 149  
          * 
 150  
          * map1                                // ["element-1"=1,"element-3"=3,"element-5"=5]
 151  
          * 
 152  
          * var it:IListMapIterator = map1.listIterator(map1.size());
 153  
          * var e:int;
 154  
          * 
 155  
          * while (it.hasPrevious())
 156  
          * 
 157  
          * {
 158  
          * 
 159  
          *     ITERATION N.1
 160  
          * 
 161  
          *     it.pointer()                    // "element-5"
 162  
          *     it.nextIndex()                  // 3
 163  
          *     it.previousIndex()              // 2
 164  
          * 
 165  
          *     e = it.previous();
 166  
          *     e                               // 5
 167  
          * 
 168  
          *     it.pointer()                    // "element-3"
 169  
          *     it.nextIndex()                  // 2
 170  
          *     it.previousIndex()              // 1
 171  
          * 
 172  
          *     if (e == 5)
 173  
          *     {
 174  
          *         it.remove()
 175  
          *         map1                        // ["element-1"=1,"element-3"=3]
 176  
          *     }
 177  
          * 
 178  
          *     ITERATION N.2
 179  
          * 
 180  
          *     it.pointer()                    // "element-3"
 181  
          *     it.nextIndex()                  // 2
 182  
          *     it.previousIndex()              // 1
 183  
          * 
 184  
          *     e = it.previous();
 185  
          *     e                               // 3
 186  
          * 
 187  
          *     it.pointer()                    // "element-1"
 188  
          *     it.nextIndex()                  // 1
 189  
          *     it.previousIndex()              // 0
 190  
          * 
 191  
          *     if (e == 3)
 192  
          *     {
 193  
          *         //map1.put("element-4", 4); // ConcurrentModificationError: During the iteration, the map was changed directly (without use the iterator).
 194  
          *         it.put("element-4", 4);
 195  
          *         map1                        // [1,4,3]
 196  
          *     }
 197  
          * 
 198  
          *     ITERATION N.3
 199  
          * 
 200  
          *     it.pointer()                    // "element-3"
 201  
          *     it.nextIndex()                  // 2
 202  
          *     it.previousIndex()              // 1
 203  
          * 
 204  
          *     e = it.previous();
 205  
          *     e                               // 4
 206  
          * 
 207  
          *     it.pointer()                    // "element-1"
 208  
          *     it.nextIndex()                  // 1
 209  
          *     it.previousIndex()              // 0
 210  
          * 
 211  
          *     ITERATION N.4
 212  
          * 
 213  
          *     it.pointer()                    // "element-1"
 214  
          *     it.nextIndex()                  // 1
 215  
          *     it.previousIndex()              // 0
 216  
          * 
 217  
          *     e = it.previous();
 218  
          *     e                               // 1
 219  
          * 
 220  
          *     it.pointer()                    // null
 221  
          *     it.nextIndex()                  // 0
 222  
          *     it.previousIndex()              // -1
 223  
          * }
 224  
          * </listing>
 225  
          * 
 226  
          * @author Flávio Silva
 227  
          */
 228  
         public class ListMapIterator implements IListMapIterator
 229  
         {
 230  
                 private var _allowModification: Boolean;
 231  
                 private var _modCount: int;
 232  
                 private var _pointer: int = -1;
 233  
                 private var _removePointer: int;
 234  
                 private var _source: IListMap;
 235  
 
 236  
                 /**
 237  
                  * Constructor, creates a new <code>ListMapIterator</code> object.
 238  
                  * 
 239  
                  * @param          source                 the source <code>ListMapIterator</code> to iterate over.
 240  
                  * @param          position         indicates the first mapping that would be returned by an initial call to <code>next</code>. An initial call to <code>previous</code> would return the mapping with the specified position minus one. 
 241  
                  * @throws         ArgumentError  if the <code>source</code> argument is <code>null</code>.
 242  
                  */
 243  
                 public function ListMapIterator(source:IListMap, position:int = 0)
 244  1
                 {
 245  1
                         if (!source) throw new ArgumentError("The 'source' argument must not be 'null'.");
 246  1
                         if (position < 0 || position > source.size()) throw new IndexOutOfBoundsError("The 'position' argument is out of bounds: " + position + " (min: 0, max: " + source.size() + ")"); 
 247  
                         
 248  1
                         _source = source;
 249  1
                         _modCount = _source.modCount;
 250  1
                         _pointer += position;
 251  1
                 }
 252  
 
 253  
                 /**
 254  
                  * @inheritDoc
 255  
                  */
 256  
                 public function hasNext(): Boolean
 257  
                 {
 258  1
                         return _pointer < _source.size() - 1;
 259  
                 }
 260  
 
 261  
                 /**
 262  
                  * @inheritDoc
 263  
                  */
 264  
                 public function hasPrevious(): Boolean
 265  
                 {
 266  1
                         return _pointer >= 0;
 267  
                 }
 268  
 
 269  
                 /**
 270  
                  * Returns the next <code>value</code> in the iteration.
 271  
                  * The <code>pointer</code> operation returns the <code>key</code> associated with the returned <code>value</code>.
 272  
                  * 
 273  
                  * @throws         org.as3collections.errors.NoSuchElementError         if the iteration has no more mappings.
 274  
                  * @throws         org.as3collections.errors.ConcurrentModificationError         if the map was changed directly (without using the iterator) during iteration.
 275  
                  */
 276  
                 public function next(): *
 277  
                 {
 278  1
                         if (!hasNext()) throw new NoSuchElementError("Iterator has no next mapping. Call hasNext() method before.");
 279  
                         
 280  1
                         checkConcurrentModificationError();
 281  1
                         _allowModification = true;
 282  1
                         _pointer++;
 283  1
                         _removePointer = _pointer;
 284  1
                         return _source.getValueAt(_pointer);
 285  
                 }
 286  
 
 287  
                 /**
 288  
                  * @inheritDoc
 289  
                  */
 290  
                 public function nextIndex(): int
 291  
                 {
 292  1
                         return _pointer + 1;
 293  
                 }
 294  
 
 295  
                 /**
 296  
                  * Returns the internal pointer of the iteration.
 297  
                  * <p>In this implementation the pointer is a <code>key</code>.</p>
 298  
                  * 
 299  
                  * @return         the internal pointer of the iteration.
 300  
                   */
 301  
                 public function pointer(): *
 302  
                 {
 303  1
                         if (_pointer < 0) return null;
 304  1
                         return _source.getKeyAt(_pointer);
 305  
                 }
 306  
 
 307  
                 /**
 308  
                  * Returns the previous <code>value</code> in the iteration.
 309  
                  * The <code>pointer</code> operation returns the <code>key</code> associated with the returned <code>value</code>.
 310  
                  * 
 311  
                  * @throws         org.as3collections.errors.NoSuchElementError         if the iteration has no previous mappings.
 312  
                  * @throws         org.as3collections.errors.ConcurrentModificationError         if the map was changed directly (without using the iterator) during iteration.
 313  
                  */
 314  
                 public function previous(): *
 315  
                 {
 316  1
                         if (!hasPrevious()) throw new NoSuchElementError("Iterator has no previous mapping. Call hasPrevious() method before.");
 317  
                         
 318  1
                         checkConcurrentModificationError();
 319  1
                         _allowModification = true;
 320  1
                         _removePointer = _pointer;
 321  1
                         return _source.getValueAt(_pointer--);
 322  
                 }
 323  
 
 324  
                 /**
 325  
                  * @inheritDoc
 326  
                  */
 327  
                 public function previousIndex(): int
 328  
                 {
 329  1
                         return _pointer;
 330  
                 }
 331  
                 
 332  
                 /**
 333  
                  * Associates the specified value with the specified key in this map.
 334  
                  * The mapping is inserted immediately before the next mapping that would be returned by <code>next</code>, if any, and after the next mapping that would be returned by <code>previous</code>, if any.
 335  
                  * (If the map contains no mappings, the new mapping becomes the sole mapping on the map.)
 336  
                  * The new mapping is inserted before the implicit cursor: a subsequent call to <code>next</code> would be unaffected, and a subsequent call to <code>previous</code> would return the new mapping.
 337  
                  * (This call increases by one the value that would be returned by a call to <code>nextIndex</code> or <code>previousIndex</code>.) 
 338  
                  * 
 339  
                  * @param          key         key with which the specified value is to be associated.
 340  
                  * @param          value         value to be associated with the specified key.
 341  
                  * @throws         org.as3collections.errors.ConcurrentModificationError         if the map was changed directly (without using the iterator) during iteration.
 342  
                  * @throws         ArgumentError                                                                                          if the map already contains the specified key.
 343  
                  */
 344  
                 public function put(key:*, value:*): void
 345  
                 {
 346  1
                         checkConcurrentModificationError();
 347  
                         
 348  1
                         _source.putAt(_pointer + 1, key, value);
 349  
                         
 350  1
                         if (_modCount != _source.modCount) _pointer++;
 351  1
                         _modCount = _source.modCount;
 352  1
                         _allowModification = false;
 353  1
                 }
 354  
 
 355  
                 /**
 356  
                  * Removes from the map the last mapping that was returned by <code>next</code> or <code>previous</code>.
 357  
                  * This call can only be made once per call to <code>next</code> or <code>previous</code>.
 358  
                  * It can be made only if <code>IListMapIterator.add</code> has not been called after the last call to <code>next</code> or <code>previous</code>. 
 359  
                  * 
 360  
                  * @throws         org.as3coreaddendum.errors.UnsupportedOperationError          if the <code>remove</code> operation is not supported by this iterator.
 361  
                  * @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.
 362  
                  * @throws         org.as3collections.errors.ConcurrentModificationError         if the map was changed directly (without using the iterator) during iteration.
 363  
                  */
 364  
                 public function remove(): void
 365  
                 {
 366  1
                         checkConcurrentModificationError();
 367  
                         
 368  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.");
 369  
                         
 370  1
                         _source.removeAt(_removePointer);
 371  1
                         _modCount = _source.modCount;
 372  1
                         _allowModification = false;
 373  1
                         if (_removePointer == _pointer) _pointer--;
 374  1
                 }
 375  
 
 376  
                 /**
 377  
                  * @inheritDoc
 378  
                  */
 379  
                 public function reset(): void
 380  
                 {
 381  1
                         _pointer = -1;
 382  1
                 }
 383  
 
 384  
                 /**
 385  
                  * Replaces the last mapping returned by <code>next</code> or <code>previous</code> with the specified mapping.
 386  
                  * This call can be made only if neither <code>IListMapIterator.remove</code> nor <code>IListMapIterator.add</code> have been called after the last call to <code>next</code> or <code>previous</code>. 
 387  
                  * 
 388  
                  * @param          key         key with which the specified value is to be associated.
 389  
                  * @param          value         value to be associated with the specified key. 
 390  
                  * @throws         org.as3coreaddendum.errors.UnsupportedOperationError          if the <code>set</code> operation is not supported by this iterator.
 391  
                  * @throws         org.as3coreaddendum.errors.ClassCastError                                  if the class of the specified key or value prevents it from being added to this map.
 392  
                  * @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>.
 393  
                  * @throws         ArgumentError                                                                                          if the map already contains the specified key and it is not the replaced key.
 394  
                  */
 395  
                 public function set(key:*, value:*): void
 396  
                 {
 397  1
                         checkConcurrentModificationError();
 398  
                         
 399  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.");
 400  
                         
 401  1
                         var setIndex:int = (_removePointer > _pointer) ? _pointer + 1 : _pointer;
 402  
                         
 403  1
                         _source.setKeyAt(setIndex, key);
 404  1
                         _source.setValueAt(setIndex, value);
 405  1
                         _modCount = _source.modCount;
 406  1
                 }
 407  
 
 408  
                 /**
 409  
                  * @private
 410  
                  */
 411  
                 public function checkConcurrentModificationError(): void
 412  
                 {
 413  1
                         if (_modCount != _source.modCount) throw new ConcurrentModificationError("During the iteration, the map was changed directly (without use the iterator).");
 414  1
                 }
 415  
 
 416  
         }
 417  
 
 418  
 }