KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > RubyEnumerator


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2006 Michael Studman <me@michaelstudman.com>
15  *
16  * Alternatively, the contents of this file may be used under the terms of
17  * either of the GNU General Public License Version 2 or later (the "GPL"),
18  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
19  * in which case the provisions of the GPL or the LGPL are applicable instead
20  * of those above. If you wish to allow use of your version of this file only
21  * under the terms of either the GPL or the LGPL, and not to allow others to
22  * use your version of this file under the terms of the CPL, indicate your
23  * decision by deleting the provisions above and replace them with the notice
24  * and other provisions required by the GPL or the LGPL. If you do not delete
25  * the provisions above, a recipient may use your version of this file under
26  * the terms of any one of the CPL, the GPL or the LGPL.
27  ***** END LICENSE BLOCK *****/

28 package org.jruby;
29
30 import org.jruby.internal.runtime.methods.MultiStubMethod;
31 import org.jruby.internal.runtime.methods.NoopMultiStub;
32
33 import org.jruby.runtime.Arity;
34 import org.jruby.runtime.Block;
35 import org.jruby.runtime.BlockCallback;
36 import org.jruby.runtime.ObjectAllocator;
37 import org.jruby.runtime.ThreadContext;
38 import org.jruby.runtime.Visibility;
39
40 import org.jruby.runtime.builtin.IRubyObject;
41
42 /**
43  * Implementation of Ruby's Enumerator module.
44  */

45 public class RubyEnumerator extends RubyObject {
46     /** target for each operation */
47     private IRubyObject object;
48     
49     /** method to invoke for each operation */
50     private IRubyObject method;
51     
52     /** args to each method */
53     private IRubyObject[] methodArgs;
54     
55     private static ObjectAllocator ENUMERATOR_ALLOCATOR = new ObjectAllocator() {
56         public IRubyObject allocate(Ruby runtime, RubyClass klass) {
57             return new RubyEnumerator(runtime, klass);
58         }
59     };
60
61     public static void defineEnumerator(Ruby runtime) {
62         RubyModule enumerableModule = runtime.getModule("Enumerable");
63         RubyClass object = runtime.getObject();
64         RubyClass enumeratorClass = enumerableModule.defineClassUnder("Enumerator", object, ENUMERATOR_ALLOCATOR);
65         RubyEnumeratorStub0 enumeratorStub = RubyEnumeratorStub0.createStub(enumeratorClass, object, enumerableModule);
66
67         enumeratorClass.includeModule(enumerableModule);
68         enumeratorClass.getMetaClass().addMethod("new", enumeratorStub.enumerator__new);
69         enumeratorClass.addMethod("initialize", enumeratorStub.enumerator__initialize);
70         enumeratorClass.addMethod("each", enumeratorStub.enumerator__each);
71
72         object.addMethod("to_enum", enumeratorStub.object__to_enum);
73         object.addMethod("enum_for", enumeratorStub.object__to_enum);
74
75         enumerableModule.addMethod("enum_with_index", enumeratorStub.enumerable__enum_with_index);
76         enumerableModule.addMethod("each_slice", enumeratorStub.enumerable__each_slice);
77         enumerableModule.addMethod("enum_slice", enumeratorStub.enumerable__enum_slice);
78         enumerableModule.addMethod("each_cons", enumeratorStub.enumerable__each_cons);
79         enumerableModule.addMethod("enum_cons", enumeratorStub.enumerable__enum_cons);
80     }
81
82     private RubyEnumerator(Ruby runtime, RubyClass type) {
83         super(runtime, type);
84     }
85
86     /** Primes the instance. Little validation is done at this stage */
87     private IRubyObject initialize(ThreadContext tc, IRubyObject[] args, Block block) {
88         checkArgumentCount(args, 1, -1);
89            
90         object = args[0];
91         methodArgs = new IRubyObject[Math.max(0, args.length - 2)];
92
93         if (args.length >= 2) {
94             method = args[1];
95         } else {
96             method = RubySymbol.newSymbol(tc.getRuntime(), "each");
97         }
98
99         if (args.length >= 3) {
100             System.arraycopy(args, 2, methodArgs, 0, args.length - 2);
101         } else {
102             methodArgs = new IRubyObject[0];
103         }
104
105         return this;
106     }
107
108     /**
109      * Send current block and supplied args to method on target. According to MRI
110      * Block may not be given and "each" should just ignore it and call on through to
111      * underlying method.
112      */

113     private IRubyObject each(ThreadContext tc, IRubyObject[] args, Block block) {
114         checkArgumentCount(args, 0, 0);
115
116         return object.callMethod(tc, method.asSymbol(), methodArgs, block);
117     }
118
119     /** Block callback for slicing the results of calling the client block */
120     public static class SlicedBlockCallback implements BlockCallback {
121         protected RubyArray slice;
122         protected final long sliceSize;
123         protected final Block clientBlock;
124         protected final Ruby runtime;
125
126         public SlicedBlockCallback(Ruby runtime, Block clientBlock, long sliceSize) {
127             this.runtime = runtime;
128             this.clientBlock = clientBlock;
129             this.sliceSize = sliceSize;
130             this.slice = RubyArray.newArray(runtime, sliceSize);
131         }
132
133         public IRubyObject call(ThreadContext context, IRubyObject[] args, IRubyObject replacementSelf, Block block) {
134             if (args.length > 1) {
135                 slice.append(RubyArray.newArray(runtime, args));
136             } else {
137                 slice.append(args[0]);
138             }
139
140             if (slice.getLength() == sliceSize) {
141                 //no need to dup slice as we create a new one momentarily
142
clientBlock.call(context, new IRubyObject[] { slice }, null);
143
144                 slice = RubyArray.newArray(runtime, sliceSize);
145             }
146
147             return runtime.getNil();
148         }
149
150
151         /** Slice may be over but there weren't enough items to make the slice */
152         public boolean hasLeftovers() {
153             return (slice.getLength() > 0) && (slice.getLength() < sliceSize);
154         }
155
156         /** Pass slice dregs on to client blcok */
157         public void yieldLeftovers(ThreadContext context) {
158             clientBlock.call(context, new IRubyObject[] { slice }, null);
159         }
160     }
161
162     /** Block callback for viewing consecutive results from calling the client block */
163     public static class ConsecutiveBlockCallback implements BlockCallback {
164         protected final RubyArray cont;
165         protected final long contSize;
166         protected final Block clientBlock;
167         protected final Ruby runtime;
168
169
170         public ConsecutiveBlockCallback(Ruby runtime, Block clientBlock, long contSize) {
171             this.runtime = runtime;
172             this.clientBlock = clientBlock;
173             this.contSize = contSize;
174             this.cont = RubyArray.newArray(runtime, contSize);
175         }
176
177         public IRubyObject call(ThreadContext context, IRubyObject[] args, IRubyObject replacementSelf, Block block) {
178             if (cont.getLength() == contSize) {
179                 cont.shift();
180             }
181
182             if (args.length > 1) {
183                 cont.append(RubyArray.newArray(runtime, args));
184             } else {
185                 cont.append(args[0]);
186             }
187
188             if (cont.getLength() == contSize) {
189                 //dup so we are in control of the array
190
clientBlock.call(context, new IRubyObject[] { cont.dup() }, null);
191             }
192
193             return runtime.getNil();
194         }
195     }
196
197     /** Multi-stub for all enumerator methods */
198     public static class RubyEnumeratorStub0 extends NoopMultiStub {
199         private final RubyModule enumerator;
200         private final Ruby runtime;
201         
202         public static RubyEnumeratorStub0 createStub(RubyClass enumeratorClass,
203                 RubyClass objectClass, RubyModule enumerableModule) {
204             return new RubyEnumeratorStub0(enumeratorClass, objectClass, enumerableModule);
205         }
206
207         public final MultiStubMethod enumerator__new;
208         public final MultiStubMethod enumerator__initialize;
209         public final MultiStubMethod enumerator__each;
210         public final MultiStubMethod object__to_enum;
211         public final MultiStubMethod enumerable__each_slice;
212         public final MultiStubMethod enumerable__each_cons;
213         public final MultiStubMethod enumerable__enum_with_index;
214         public final MultiStubMethod enumerable__enum_slice;
215         public final MultiStubMethod enumerable__enum_cons;
216
217         private RubyEnumeratorStub0(RubyClass enumeratorClass,
218                 RubyClass objectClass, RubyModule enumerableModule) {
219             enumerator = enumeratorClass;
220             runtime = enumeratorClass.getRuntime();
221             enumerator__new = new MultiStubMethod(RubyEnumeratorStub0.this, 0,
222                     enumeratorClass, Arity.required(1), Visibility.PUBLIC);
223             enumerator__initialize = new MultiStubMethod(RubyEnumeratorStub0.this, 1,
224                     enumeratorClass, Arity.required(1), Visibility.PRIVATE);
225             enumerator__each = new MultiStubMethod(RubyEnumeratorStub0.this, 2,
226                     enumeratorClass, Arity.optional(), Visibility.PUBLIC);
227             object__to_enum = new MultiStubMethod(RubyEnumeratorStub0.this, 3,
228                     objectClass, Arity.optional(), Visibility.PUBLIC);
229             enumerable__each_slice = new MultiStubMethod(RubyEnumeratorStub0.this, 4,
230                     enumerableModule, Arity.singleArgument(), Visibility.PUBLIC);
231             enumerable__each_cons = new MultiStubMethod(RubyEnumeratorStub0.this, 5,
232                     enumerableModule, Arity.singleArgument(), Visibility.PUBLIC);
233             enumerable__enum_with_index = new MultiStubMethod(RubyEnumeratorStub0.this, 6,
234                     enumerableModule, Arity.noArguments(), Visibility.PUBLIC);
235             enumerable__enum_slice = new MultiStubMethod(RubyEnumeratorStub0.this, 7,
236                     enumerableModule, Arity.singleArgument(), Visibility.PUBLIC);
237             enumerable__enum_cons = new MultiStubMethod(RubyEnumeratorStub0.this, 8,
238                     enumerableModule, Arity.singleArgument(), Visibility.PUBLIC);
239         }
240
241         /** Enumerable::Enumerator#new */
242         public IRubyObject method0(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) {
243             RubyClass klass = (RubyClass)self;
244             
245             RubyEnumerator result = (RubyEnumerator) klass.allocate();
246             
247             result.callInit(args, block);
248             
249             return result;
250         }
251
252         /** Enumerable::Enumerator#initialize */
253         public IRubyObject method1(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) {
254             return ((RubyEnumerator) self).initialize(tc, args, block);
255         }
256
257         /** Enumerable::Enumerator#each */
258         public IRubyObject method2(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) {
259             return ((RubyEnumerator) self).each(tc, args, block);
260         }
261
262         /** Object#to_enum and Object#enum_for. Just like Enumerable::Enumerator.new(self, arg_0) */
263         public IRubyObject method3(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) {
264             IRubyObject[] newArgs = new IRubyObject[args.length + 1];
265             newArgs[0] = self;
266             System.arraycopy(args, 0, newArgs, 1, args.length);
267
268             return enumerator.callMethod(tc, "new", newArgs);
269         }
270
271         /** Enumerable:#each_slice */
272         public IRubyObject method4(final ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) {
273             self.checkArgumentCount(args, 1, 1);
274
275             long sliceSize = args[0].convertToInteger().getLongValue();
276
277             if (sliceSize <= 0L) {
278                 throw runtime.newArgumentError("invalid slice size");
279             }
280
281             SlicedBlockCallback sliceBlock = new SlicedBlockCallback(runtime, block, sliceSize);
282
283             RubyEnumerable.callEach(tc, self, self.getMetaClass(), sliceBlock);
284             
285             if (sliceBlock.hasLeftovers()) {
286                 sliceBlock.yieldLeftovers(tc);
287             }
288
289             return runtime.getNil();
290         }
291
292         /** Enumerable:#each_cons */
293         public IRubyObject method5(final ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) {
294             self.checkArgumentCount(args, 1, 1);
295
296             long consecutiveSize = args[0].convertToInteger().getLongValue();
297
298             if (consecutiveSize <= 0L) {
299                 throw runtime.newArgumentError("invalid size");
300             }
301
302             RubyEnumerable.callEach(tc, self, self.getMetaClass(),
303                     new ConsecutiveBlockCallback(runtime, block, consecutiveSize));
304
305             return runtime.getNil();
306         }
307
308         /** Enumerable#enum_with_index */
309         public IRubyObject method6(final ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) {
310             self.checkArgumentCount(args, 0, 0);
311
312             return enumerator.callMethod(tc, "new",
313                     new IRubyObject[] { self, runtime.newSymbol("each_with_index") });
314         }
315
316         /** Enumerable#enum_slice */
317         public IRubyObject method7(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) {
318             self.checkArgumentCount(args, 1, 1);
319
320             return enumerator.callMethod(tc, "new",
321                     new IRubyObject[] { self, runtime.newSymbol("each_slice"), args[0] });
322         }
323
324         /** Enumerable#enum_cons */
325         public IRubyObject method8(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) {
326             self.checkArgumentCount(args, 1, 1);
327
328             return enumerator.callMethod(tc, "new",
329                     new IRubyObject[] { self, runtime.newSymbol("each_cons"), args[0] });
330         }
331     }
332 }
333
Popular Tags