1 /**
  2  * The Render Engine
  3  * Iterator
  4  *
  5  * @fileoverview A set of objects which can be used to create a collection
  6  *               of objects, and to iterate over a container.
  7  *
  8  * @author: Brett Fattori (brettf@renderengine.com)
  9  * @author: $Author: bfattori@gmail.com $
 10  * @version: $Revision: 1572 $
 11  *
 12  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 13  *
 14  * Permission is hereby granted, free of charge, to any person obtaining a copy
 15  * of this software and associated documentation files (the "Software"), to deal
 16  * in the Software without restriction, including without limitation the rights
 17  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 18  * copies of the Software, and to permit persons to whom the Software is
 19  * furnished to do so, subject to the following conditions:
 20  *
 21  * The above copyright notice and this permission notice shall be included in
 22  * all copies or substantial portions of the Software.
 23  *
 24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 29  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 30  * THE SOFTWARE.
 31  *
 32  */
 33 
 34 // The class this file defines and its required classes
 35 R.Engine.define({
 36     "class":"R.lang.Iterator",
 37     "requires":[
 38         "R.engine.PooledObject"
 39     ]
 40 });
 41 
 42 /**
 43  * @class Create an iterator over a {@link R.struct.Container} or <code>Array</code> instance. An
 44  * iterator is a convenient object to traverse the list of objects
 45  * within the container.  The benefit of using an iterator with a <code>R.struct.Container</code> is
 46  * that if the container is modified, the <code>R.lang.Iterator</code> will reflect these changes.
 47  * <p/>
 48  * The simplest way to traverse the list is as follows:
 49  * <pre>
 50  * for (var itr = R.lang.Iterator.create(containerObj); itr.hasNext(); ) {
 51  *    // Get the next object in the container
 52  *    var o = itr.next();
 53  *
 54  *    // Do something with the object
 55  *    o.doSomething();
 56  * }
 57  *
 58  * // Destroy the iterator when done
 59  * itr.destroy();
 60  * </pre>
 61  * The last step is important so that you're not creating a lot
 62  * of objects, especially if the iterator is used repeatedly.
 63  * Since the iterator is a pooled object, it will be reused.
 64  *
 65  * @param container {R.struct.Container} The container to iterate over.
 66  * @constructor
 67  * @extends R.engine.PooledObject
 68  * @description Create an iterator over a collection
 69  */
 70 R.lang.Iterator = function () {
 71     "use strict";
 72     return R.engine.PooledObject.extend(/** @scope R.lang.Iterator.prototype */{
 73 
 74         c:null,
 75         aO:null,
 76         p:null,
 77         r:false,
 78         arr:false,
 79 
 80         /**
 81          * @private
 82          */
 83         constructor:function (container /*, actualContainerObj */) {
 84             this.base("Iterator");
 85             this.c = container;
 86             this.aO = (arguments.length == 2 ? arguments[1] : null);
 87             this.arr = $.isArray(container);    // Handle plain Arrays too
 88             this.p = this.arr ? 0 : container._head;
 89             this.r = false;
 90         },
 91 
 92         /**
 93          * Release the iterator back into the object pool.
 94          */
 95         release:function () {
 96             this.base();
 97             this.c = null;
 98             this.aO = null;
 99             this.arr = false;
100             this.p = null;
101             this.r = false;
102         },
103 
104         /**
105          * Reset the iterator to the start of the collection.
106          */
107         reset:function () {
108             this.p = this.arr ? (this.r ? this.c.length - 1 : 0) : (this.r ? this.c._tail : this.c._head);
109         },
110 
111         /**
112          * Reverse the order of the elements in the container (non-destructive) before
113          * iterating over them.  You cannot call this method after you have called {@link #next},
114          * otherwise, use {@link #reset} before calling this method.
115          */
116         reverse:function () {
117             Assert((this.arr ? this.p == 0 : this.p === this.c._head), "Cannot reverse Iterator after calling next()");
118             this.r = true;
119             this.p = this.arr ? this.c.length - 1 : this.c._tail;
120         },
121 
122         /**
123          * Get the next element from the iterator.
124          * @return {Object} The next element in the iterator
125          * @throws {Error} An error if called when no more elements are available
126          */
127         next:function () {
128             // Make sure the container wasn't destroyed
129             if (this.arr ? (this.aO != null ? this.aO.isDestroyed() : false) : this.c.isDestroyed()) {
130                 throw new Error("Invalid iterator over destroyed container!");
131             }
132 
133             var o = null;
134             if (this.arr) {
135                 // For arrays
136                 Assert(this.p > -1 && this.p < this.c.length, "Iterator[" + this.getId() + "] - next() is out of range");
137                 o = this.c[this.p];
138                 this.p += (this.r ? -1 : 1);
139             } else {
140                 // For containers
141                 // Get the next and move the pointer
142                 o = this.p.ptr;
143                 this.p = (this.r ? this.p.prev : this.p.next);
144 
145                 if (o == null) {
146                     Assert(false, "Iterator[" + this.getId() + "] - next() is out of range");
147                 }
148             }
149 
150             return o;
151         },
152 
153         /**
154          * Returns <tt>true</tt> if the iterator has more elements.
155          * @return {Boolean}
156          */
157         hasNext:function () {
158             // As long as the container hasn't been destroyed
159             if (this.arr ? (this.aO != null ? !this.aO.isDestroyed() : true) : !this.c.isDestroyed()) {
160                 if (this.arr) {
161                     // For arrays (and R.struct.Container)
162                     var nxt = this.r ? -1 : 1, n = this.p,
163                         dead = (this.c[n] && this.c[n].isDestroyed && this.c[n].isDestroyed());
164                     while ((n > -1 && n < this.c.length) && dead) {
165                         // Skip dead objects
166                         n += nxt;
167                         this.p = n;
168                     }
169                     return (n > -1 && n < this.c.length);
170                 } else {
171                     // If the container hasn't been destroyed
172                     while (this.p != null && this.p.ptr != null && this.p.ptr.isDestroyed()) {
173                         // Skip destroyed objects
174                         this.p = (this.r ? this.p.prev : this.p.next);
175                     }
176                     return this.p != null;
177                 }
178             }
179             return false;
180         }
181 
182     }, /** @scope R.lang.Iterator.prototype */{
183         /**
184          * Get the class name of this object
185          *
186          * @return {String} "R.lang.Iterator"
187          */
188         getClassName:function () {
189             return "R.lang.Iterator";
190         },
191 
192         /**
193          * Create an instance of an iterator over the given container.
194          * @param container {R.struct.Container|Array} An <code>Array</code> or {@link R.struct.Container}
195          * @return {R.lang.Iterator} An iterator over the container
196          * @static
197          */
198         over:function (container) {
199             return R.lang.Iterator.create(container);
200         }
201 
202     });
203 
204 };