Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
BEGIN LICENSE BLOCK ***** Version: EPL 1.0/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Eclipse Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.eclipse.org/legal/epl-v10.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyright (C) 2006 Michael Studman <me@michaelstudman.com> Alternatively, the contents of this file may be used under the terms of either of the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the EPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the EPL, the GPL or the LGPL. END LICENSE BLOCK ***
 
 package org.jruby;
 
 
 
 import static org.jruby.CompatVersion.RUBY1_8;
 import static org.jruby.CompatVersion.RUBY1_9;
 import static org.jruby.CompatVersion.RUBY2_0;
 import static org.jruby.runtime.Visibility.PRIVATE;

Implementation of Ruby's Enumerator module.
 
 @JRubyModule(name="Enumerable::Enumerator", include="Enumerable")
 public class RubyEnumerator extends RubyObject {
    
target for each operation
 
     private IRubyObject object;

    
method to invoke for each operation
 
     private String method;

    
args to each method
 
     private IRubyObject[] methodArgs;
    
    
A value or proc to provide the size of the Enumerator content
 
     private IRubyObject size;
 
     public static void defineEnumerator(Ruby runtime) {
         RubyModule enm = runtime.getClassFromPath("Enumerable");
         
         final RubyClass enmr;
         if (runtime.is1_9() || runtime.is2_0()) {
             enmr = runtime.defineClass("Enumerator"runtime.getObject(), );
         } else {
             enmr = enm.defineClassUnder("Enumerator"runtime.getObject(), );
         }
 
         enmr.includeModule(enm);
         enmr.defineAnnotatedMethods(RubyEnumerator.class);
         runtime.setEnumerator(enmr);
 
         RubyYielder.createYielderClass(runtime);
     }
 
     private static ObjectAllocator ENUMERATOR_ALLOCATOR = new ObjectAllocator() {
         public IRubyObject allocate(Ruby runtimeRubyClass klass) {
             return new RubyEnumerator(runtimeklass);
         }
     };
 
     private RubyEnumerator(Ruby runtimeRubyClass type) {
         super(runtimetype);
          = runtime.getNil();
         initialize(runtime.getNil(), RubyString.newEmptyString(runtime), .);
     }
 
     private RubyEnumerator(Ruby runtimeRubyClass typeIRubyObject objectIRubyObject methodIRubyObject[]args) {
        super(runtimetype);
        initialize(objectmethodargs);
    }
    public static IRubyObject enumeratorize(Ruby runtimeIRubyObject objectString method) {
        return new RubyEnumerator(runtimeruntime.getEnumerator(), objectruntime.fastNewSymbol(method), .);
    }
    public static IRubyObject enumeratorize(Ruby runtimeIRubyObject objectString methodIRubyObject arg) {
        return new RubyEnumerator(runtimeruntime.getEnumerator(), objectruntime.fastNewSymbol(method), new IRubyObject[] {arg});
    }
    public static IRubyObject enumeratorize(Ruby runtimeIRubyObject objectString methodIRubyObject[] args) {
        return new RubyEnumerator(runtimeruntime.getEnumerator(), objectruntime.fastNewSymbol(method), args); // TODO: make sure it's really safe to not to copy it
    }
    public static IRubyObject enumeratorize(Ruby runtimeRubyClass typeIRubyObject objectString method) {
        return new RubyEnumerator(runtimetypeobjectruntime.fastNewSymbol(method), .);
    }
    public static IRubyObject enumeratorize(Ruby runtimeRubyClass typeIRubyObject objectString methodIRubyObject arg) {
        return new RubyEnumerator(runtimetypeobjectruntime.fastNewSymbol(method), new IRubyObject[] {arg});
    }
    public static IRubyObject enumeratorize(Ruby runtimeRubyClass typeIRubyObject objectString methodIRubyObject[] args) {
        return new RubyEnumerator(runtimetypeobjectruntime.fastNewSymbol(method), args); // TODO: make sure it's really safe to not to copy it
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize(ThreadContext context) {
        throw context.runtime.newArgumentError(0, 1);
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize19(ThreadContext contextBlock block) {
        if(!block.isGiven()) {
            throw context.runtime.newArgumentError(0, 1);
        }
        // TODO: avoid double lookup
        IRubyObject obj = context.runtime.getModule("JRuby").getClass("Generator").callMethod(context"new"new IRubyObject[0], block);
        return initialize19(contextobjblock);
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize20(ThreadContext contextBlock block) {
        return initialize19(contextblock);
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize(ThreadContext contextIRubyObject object) {
        return initialize(objectcontext.runtime.fastNewSymbol("each"), );
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize19(ThreadContext contextIRubyObject objectBlock block) {
        return initialize(objectcontext.runtime.fastNewSymbol("each"), );
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize20(ThreadContext contextIRubyObject objectBlock block) {
        Ruby runtime = context.runtime;
        RubySymbol each = runtime.newSymbol("each");
        
        // check for size
        if ((object.isNil() || runtime.getProc().isInstance(object)) ||
                runtime.getFloat().isInstance(object) && ((RubyFloat)object).getDoubleValue() == .) {
            // object is nil, a proc, or infinity; use it for size
            IRubyObject gen = context.runtime.getModule("JRuby").getClass("Generator").callMethod(context"new"new IRubyObject[0], block);
            return initialize20(geneachobject);
        }
        return initialize(objecteach);
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize(ThreadContext contextIRubyObject objectIRubyObject method) {
        return initialize(objectmethod);
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize19(ThreadContext contextIRubyObject objectIRubyObject methodBlock block) {
        return initialize(objectmethod);
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize20(ThreadContext contextIRubyObject objectIRubyObject methodBlock block) {
        return initialize(objectmethod);
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize(ThreadContext contextIRubyObject objectIRubyObject methodIRubyObject methodArg) {
        return initialize(objectmethodnew IRubyObject[] { methodArg });
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize19(ThreadContext contextIRubyObject objectIRubyObject methodIRubyObject methodArgBlock block) {
        return initialize(objectmethodnew IRubyObject[] { methodArg });
    }
    @JRubyMethod(name = "initialize", visibility = , compat = )
    public IRubyObject initialize20(ThreadContext contextIRubyObject objectIRubyObject methodIRubyObject methodArgBlock block) {
        return initialize(objectmethodnew IRubyObject[] { methodArg });
    }
    @JRubyMethod(name = "initialize", rest = true, visibility = , compat = )
    public IRubyObject initialize(ThreadContext contextIRubyObject[] args) {
        switch (args.length) {
            case 0: return initialize(context);
            case 1: return initialize(contextargs[0]);
            case 2: return initialize(contextargs[0], args[1]);
        }
        IRubyObject[] methArgs = new IRubyObject[args.length - 2];
        System.arraycopy(args, 2, methArgs, 0, methArgs.length);
        return initialize(args[0], args[1], methArgs);
    }
    @JRubyMethod(name = "initialize", rest = true, visibility = , compat = )
    public IRubyObject initialize19(ThreadContext contextIRubyObject[] argsBlock block) {
        switch (args.length) {
            case 0: return initialize19(contextblock);
            case 1: return initialize19(contextargs[0], block);
            case 2: return initialize19(contextargs[0], args[1], block);
        }
        IRubyObject[] methArgs = new IRubyObject[args.length - 2];
        System.arraycopy(args, 2, methArgs, 0, methArgs.length);
        return initialize(args[0], args[1], methArgs);
    }
    @JRubyMethod(name = "initialize", rest = true, visibility = , compat = )
    public IRubyObject initialize20(ThreadContext contextIRubyObject[] argsBlock block) {
        return initialize19(contextargsblock);
    }
    private IRubyObject initialize(IRubyObject objectIRubyObject methodIRubyObject[] methodArgs) {
        this. = object;
        this. = method.asJavaString();
        this. = methodArgs;
        setInstanceVariable("@__object__"object);
        setInstanceVariable("@__method__"method);
        setInstanceVariable("@__args__", RubyArray.newArrayNoCopyLight(getRuntime(), methodArgs));
        return this;
    }
    private IRubyObject initialize20(IRubyObject objectIRubyObject methodIRubyObject[] methodArgsIRubyObject size) {
        this. = object;
        this. = method.asJavaString();
        this. = methodArgs;
        this. = size;
        setInstanceVariable("@__object__"object);
        setInstanceVariable("@__method__"method);
        setInstanceVariable("@__args__", RubyArray.newArrayNoCopyLight(getRuntime(), methodArgs));
        return this;
    }
    @JRubyMethod(name = "dup")
    @Override
    public IRubyObject dup() {
        // JRUBY-5013: Enumerator needs to copy private fields in order to have a valid structure
        RubyEnumerator copy = (RubyEnumeratorsuper.dup();
        copy.object     = this.;
        copy.method     = this.;
        copy.methodArgs = this.;
        return copy;
    }

    
Send current block and supplied args to method on target. According to MRI Block may not be given and "each" should just ignore it and call on through to underlying method.
    public IRubyObject each(ThreadContext contextBlock block) {
        return .callMethod(contextblock);
    }
    
    @JRubyMethod(rest = true)
    public IRubyObject each(ThreadContext contextIRubyObject[] argsBlock block) {
        if (args.length == 0) {
            return each(contextblock);
        }
        
        IRubyObject[] newArgs = new IRubyObject[. + args.length];
        System.arraycopy(, 0, newArgs, 0, .);
        System.arraycopy(args, 0, newArgs.args.length);
        
        return new RubyEnumerator(context.runtimegetType(), context.runtime.newSymbol("each"), newArgs);
    }
    @JRubyMethod(name = "inspect", compat = )
    public IRubyObject inspect19(ThreadContext context) {
        Ruby runtime = context.runtime;
        if (runtime.isInspecting(this)) return inspect(contexttrue);
        try {
            runtime.registerInspecting(this);
            return inspect(contextfalse);
        } finally {
            runtime.unregisterInspecting(this);
        }
    }
    private IRubyObject inspect(ThreadContext contextboolean recurse) {
        Ruby runtime = context.runtime;
        ByteList bytes = new ByteList();
        bytes.append((byte)'#').append((byte)'<');
        bytes.append(getMetaClass().getName().getBytes());
        bytes.append((byte)':').append((byte)' ');
        if (recurse) {
            bytes.append("...>".getBytes());
            return RubyString.newStringNoCopy(runtimebytes).taint(context);
        } else {
            boolean tainted = isTaint();
            bytes.append(RubyObject.inspect(context).getByteList());
            bytes.append((byte)':');
            bytes.append(.getBytes());
            if (. > 0) {
                bytes.append((byte)'(');
                for (int i= 0; i < .i++) {
                    bytes.append(RubyObject.inspect(context[i]).getByteList());
                    if (i < . - 1) {
                        bytes.append((byte)',').append((byte)' ');
                    } else {
                        bytes.append((byte)')');
                    }
                    if ([i].isTaint()) tainted = true;
                }
            }
            bytes.append((byte)'>');
            RubyString result = RubyString.newStringNoCopy(runtimebytes);
            if (taintedresult.setTaint(true);
            return result;
        }
    }
    protected static IRubyObject newEnumerator(ThreadContext contextIRubyObject arg) {
        return context.runtime.getEnumerator().callMethod(context"new"arg);
    }
    protected static IRubyObject newEnumerator(ThreadContext contextIRubyObject arg1IRubyObject arg2) {
        return Helpers.invoke(contextcontext.runtime.getEnumerator(), "new"arg1arg2);
    }
    protected static IRubyObject newEnumerator(ThreadContext contextIRubyObject arg1IRubyObject arg2IRubyObject arg3) {
        return Helpers.invoke(contextcontext.runtime.getEnumerator(), "new"arg1arg2arg3);
    }
    @JRubyMethod(required = 1, compat = .)
    public IRubyObject each_with_object(ThreadContext contextIRubyObject argBlock block) {
        return block.isGiven() ? RubyEnumerable.each_with_objectCommon19(contextthisblockarg) : enumeratorize(context.runtimegetType(), this"each_with_object"arg);
    }
    @JRubyMethod(compat = )
    public IRubyObject with_object(ThreadContext contextfinal IRubyObject argfinal Block block) {
        return block.isGiven() ? RubyEnumerable.each_with_objectCommon19(contextthisblockarg) : enumeratorize(context.runtimegetType(), this"with_object"arg);
    }
    @JRubyMethod(rest = true, compat = .)
    public IRubyObject each_entry(ThreadContext contextfinal IRubyObject[] argsfinal Block block) {
        return block.isGiven() ? RubyEnumerable.each_entryCommon(contextthisargsblock) : enumeratorize(context.runtimegetType(), this"each_entry"args);
    }
    @JRubyMethod(name = "each_slice")
    public IRubyObject each_slice19(ThreadContext contextIRubyObject argfinal Block block) {
        return block.isGiven() ? RubyEnumerable.each_slice(contextthisargblock) : enumeratorize(context.runtimegetType(), this"each_slice"arg);
    }
    @JRubyMethod(name = "enum_slice", compat = )
    public IRubyObject enum_slice(ThreadContext contextIRubyObject argfinal Block block) {
        return block.isGiven() ? RubyEnumerable.each_slice(contextthisargblock) : enumeratorize(context.runtimegetType(), this"enum_slice"arg);
    }
    @JRubyMethod(name = "each_cons")
    public IRubyObject each_cons19(ThreadContext contextIRubyObject argfinal Block block) {
        return block.isGiven() ? RubyEnumerable.each_cons(contextthisargblock) : enumeratorize(context.runtimegetType(), this"each_cons"arg);
    }
    @JRubyMethod(name = "enum_cons", compat = )
    public IRubyObject enum_cons(ThreadContext contextIRubyObject argfinal Block block) {
        return block.isGiven() ? RubyEnumerable.each_cons(contextthisargblock) : enumeratorize(context.runtimegetType(), this"enum_cons"arg);
    }
    
    @JRubyMethod(compat = )
    public IRubyObject size(ThreadContext context) {
        if ( != null) {
            if (context.runtime.getProc().isInstance()) {
                return ((RubyProc)).call(context);
            }
            
            return ;
        }
        
        return context.nil;
    }
    private static IRubyObject with_index_common(ThreadContext contextIRubyObject self
            final Block blockfinal String rubyMethodNameIRubyObject arg) {
        final Ruby runtime = context.runtime;
        int index = arg.isNil() ? 0 : RubyNumeric.num2int(arg);
        if (!block.isGiven()) {
            return arg.isNil() ? enumeratorize(runtimeself.getType(), selfrubyMethodName) :
                enumeratorize(runtimeself.getType(), self , rubyMethodNameruntime.newFixnum(index));
        }
        return RubyEnumerable.callEach(runtimecontextselfnew RubyEnumerable.EachWithIndex(contextblockindex));
    }
    public static IRubyObject each_with_index(ThreadContext contextIRubyObject selffinal Block block) {
        return with_index_common(contextselfblock"each_with_index"context.runtime.getNil());
    }
    @JRubyMethod(compat = )
    public static IRubyObject with_index(ThreadContext contextIRubyObject selffinal Block block) {
        return with_index_common(contextselfblock"with_index"context.runtime.getNil());
    }
    @JRubyMethod(name = "with_index", compat = )
    public static IRubyObject with_index19(ThreadContext contextIRubyObject selffinal Block block) {
        return with_index_common(contextselfblock"with_index"context.runtime.getNil());
    }
    @JRubyMethod(name = "with_index", compat = )
    public static IRubyObject with_index19(ThreadContext contextIRubyObject selfIRubyObject argfinal Block block) {
        return with_index_common(contextselfblock"with_index"arg);
    }
    
    private volatile Nexter nexter = null;
    
    public synchronized IRubyObject next(ThreadContext context) {
        ensureNexter(context);
        
        return .next();
    }
    
    public synchronized IRubyObject rewind(ThreadContext context) {
        if (.respondsTo("rewind")) .callMethod(context"rewind");
        
        if ( != null) {
            .shutdown();
             = null;
        }
        
        return this;
    }
    
    public synchronized IRubyObject peek(ThreadContext context) {
        ensureNexter(context);
        
        return .peek();
    }
    
    private void ensureNexter(ThreadContext context) {
        if ( == null) {
            if (..load()) {
                if ( instanceof RubyArray && .equals("each") && . == 0) {
                     = new ArrayNexter(context.runtime);
                } else {
                     = new ThreadedNexter(context.runtime);
                }
            } else {
                 = new ThreadedNexter(context.runtime);
            }
        }
    }
    
    protected void finalize() throws Throwable {
        try {
            Nexter nexter = this.;
            if (nexter != null) {
                nexter.shutdown();
                nexter = null;
            }
        } finally {
            super.finalize();
        }
    }
    
    private static abstract class Nexter {
        
the runtime associated with all objects
        protected final Ruby runtime;
        
        
target for each operation
        protected final IRubyObject object;

        
method to invoke for each operation
        protected final String method;

        
args to each method
        protected final IRubyObject[] methodArgs;
        
        public Nexter(Ruby runtimeIRubyObject objectString methodIRubyObject[] methodArgs) {
            this. = object;
            this. = method;
            this. = methodArgs;
            this. = runtime;
        }
        
        public abstract IRubyObject next();
        
        public abstract void shutdown();
        
        public abstract IRubyObject peek();
    }
    
    private static class ArrayNexter extends Nexter {
        private final RubyArray array;
        private int index = 0;
        
        public ArrayNexter(Ruby runtimeIRubyObject objectString methodIRubyObject[] methodArgs) {
            super(runtimeobjectmethodmethodArgs);
             = (RubyArray)object;
        }
        @Override
        public IRubyObject next() {
            IRubyObject obj = peek();
             += 1;
            return obj;
        }
        @Override
        public void shutdown() {
            // not really anything to do
             = 0;
        }
        @Override
        public IRubyObject peek() {
            checkIndex();
            
            return get();
        }
        
        protected IRubyObject get() {
            return .eltOk();
        }
        private void checkIndex() throws RaiseException {
            if ( >= .size()) throw .newLightweightStopIterationError("stop iteration");
        }
    }
    
    private static class ThreadedNexter extends Nexter implements Runnable {
        private static final boolean DEBUG = false;
        
        
sync queue to wait for values
        private SynchronousQueue<IRubyObjectout = new SynchronousQueue<IRubyObject>();
        
        
thread that's executing this Nexter
        private volatile Thread thread;
        
        
whether we're done iterating
        private IRubyObject doneObject;
        
        
future to cancel job if it has not started
        private Future future;
        
        
death mark
        protected volatile boolean die = false;

        
the last value we got, used for peek
        private IRubyObject lastValue;
        
        public ThreadedNexter(Ruby runtimeIRubyObject objectString methodIRubyObject[] methodArgs) {
            super(runtimeobjectmethodmethodArgs);
        }
        
        public synchronized IRubyObject next() {
            if ( != null) {
                return returnValue();
            }
            
            ensureStarted();
            
            return returnValue(take());
        }
        
        public synchronized void shutdown() {
            // cancel future in case we have not been started
            .cancel(true);
                
            // mark for death
             = true;
            
            Thread myThread = ;
            if (myThread != null) {
                if (..println("clearing for shutdown");
                
                // we interrupt twice, to break out of iteration and
                // (potentially) break out of final exchange
                myThread.interrupt();
                myThread.interrupt();
                
                // release references
                 = null;
                 = null;
            }
        }
        
        public synchronized IRubyObject peek() {
            if ( != null) {
                return returnValue();
            }
            
            ensureStarted();
            
            if ( != null) {
                return ;
            }
            
            peekTake();
            
            return returnValue();
        }
        
        private void ensureStarted() {
            if ( == null = .getExecutor().submit(this);
        }
        
        private IRubyObject peekTake() {
            try {
                return  = .take();
            } catch (InterruptedException ie) {
                throw .newThreadError("interrupted during iteration");
            }
        }
        
        private IRubyObject take() {
            try {
                if ( != null) {
                    return ;
                }
                
                return .take();
            } catch (InterruptedException ie) {
                throw .newThreadError("interrupted during iteration");
            } finally {
                 = null;
            }
        }
        
        private IRubyObject returnValue(IRubyObject value) {
            // if it's the NEVER object, raise StopIteration
            if (value == ) {
                 = value;
                throw .newLightweightStopIterationError("stop iteration");
            }
            
            // if it's an exception, raise it
            if (value instanceof RubyException) {
                 = value;
                throw new RaiseException((RubyException)value);
            }
            
            // otherwise, just return it
            return value;
        }
        
        public void run() {
            if (return;
            
             = Thread.currentThread();
            ThreadContext context = .getCurrentContext();
            
            if (..println(Thread.currentThread().getName() + ": starting up nexter thread");
            
            IRubyObject finalObject = ;
            
            try {
                try {
                    .callMethod(context, CallBlock.newCallClosure(.getMetaClass(), .new BlockCallback() {
                        @Override
                        public IRubyObject call(ThreadContext contextIRubyObject[] argsBlock block) {
                            try {
                                if (..println(Thread.currentThread().getName() + ": exchanging: " + args[0]);
                                if (throw new JumpException.BreakJump(-1, );
                                .put(RubyEnumerable.packEnumValues(args));
                                if (throw new JumpException.BreakJump(-1, );
                            } catch (InterruptedException ie) {
                                if (..println(Thread.currentThread().getName() + ": interrupted");
                                throw new JumpException.BreakJump(-1, );
                            }
                            return context.nil;
                        }
                    }, context));
                } catch (JumpException.BreakJump bj) {
                    // ignore, we're shutting down
                } catch (RaiseException re) {
                    if (..println(Thread.currentThread().getName() + ": exception at toplevel: " + re.getException());
                    finalObject = re.getException();
                }
                try {
                    if (!.put(finalObject);
                } catch (InterruptedException ie) {
                    // ignore
                }
            } finally {
                // disassociate this Nexter with the thread running it
                 = null;
            }
        }
    }