Package com.github.kevwil.aspen.io

Source Code of com.github.kevwil.aspen.io.RubyIORackInput

package com.github.kevwil.aspen.io;

import com.github.kevwil.aspen.RackInput;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferInputStream;
import org.jruby.*;
import org.jruby.anno.JRubyMethod;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.*;
import org.jruby.runtime.builtin.IRubyObject;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;

/**
* @author kevwil
* @since Jan 27, 2011
*/
public class RubyIORackInput
extends RubyObject
implements RackInput
{
    private static final ObjectAllocator ALLOCATOR = new ObjectAllocator()
    {
        public IRubyObject allocate( Ruby runtime, RubyClass klass )
        {
            return new RubyIORackInput( runtime, klass );
        }
    };

    public static RubyClass getClass(
            Ruby runtime,
            String name,
            RubyClass parent,
            ObjectAllocator allocator,
            Class annoClass)
    {
        RubyModule aspenMod = runtime.getOrCreateModule( "Aspen" );
        RubyClass klass = aspenMod.getClass( name );
        if( klass == null )
        {
            klass = aspenMod.defineClassUnder( name, parent, allocator );
            klass.defineAnnotatedMethods( annoClass );
        }
        return klass;
    }

    public static RubyClass getRubyIORackInputClass( Ruby runtime )
    {
        return getClass( runtime, "RubyIORackInput", runtime.getObject(),
                ALLOCATOR, RubyIORackInput.class );
    }

    public RubyIORackInput( Ruby runtime, RubyClass metaClass )
    {
        super( runtime, metaClass );
    }

    public RubyIORackInput( Ruby runtime )
    {
        super( runtime, getRubyIORackInputClass( runtime ) );
    }

    /* CLASS DATA */

    private ChannelBuffer _buffer;

    public ChannelBuffer getBuffer()
    {
        return _buffer;
    }

    public void setBuffer( final ChannelBuffer buffer )
    {
        _buffer = buffer;
    }

    /* JRuby Methods */

    @JRubyMethod()
    public IRubyObject gets( final ThreadContext context )
    {
        try
        {
            String line = readLine( getBuffer() );
            if( line == null )
            {
                throw getRuntime().newEOFError();
            }
            return JavaEmbedUtils.javaToRuby( getRuntime(), line );
        }
        catch( IOException e )
        {
            throw getRuntime().newIOError( e.getLocalizedMessage() );
        }
    }

    @JRubyMethod( optional = 2 )
    public IRubyObject read( final ThreadContext context, final IRubyObject[] args )
    {
        switch( args.length )
        {
            case 0:
                return JavaEmbedUtils.javaToRuby( getRuntime(), bufferToString( getBuffer() ) );
            case 1:
                int len = RubyInteger.num2int( args[0] );
                if( len > getBuffer().readableBytes() )
                {
                    throw getRuntime().newIOError( "cannot read " + len + " bytes from input" );
                }
                ChannelBuffer chunk = getBuffer().readBytes( len );
                return JavaEmbedUtils.javaToRuby( getRuntime(), bufferToString( chunk ) );
            case 2:
                len = RubyInteger.num2int( args[0] );
                chunk = getBuffer().readBytes( len );
                RubyString buf = RubyString.stringValue( args[1] );
                buf.append( JavaEmbedUtils.javaToRuby( getRuntime(), bufferToString( chunk ) ) );
                return getRuntime().getNil();
            default:
                throw getRuntime().newArgumentError( "wrong number of arguments: " + args.length );
        }
    }

    @JRubyMethod()
    public IRubyObject each( final ThreadContext context, final Block block )
    {
        ChannelBufferInputStream stream = new ChannelBufferInputStream( getBuffer().slice() );
        AtomicReference<String> line = new AtomicReference<String>();
        if( !isEof().isTrue() )
        {
            try
            {
                do
                {
                    line.set( stream.readLine() );
                    block.yield( context, JavaEmbedUtils.javaToRuby( getRuntime(), line.get() ) );
                }
                while( line.get() != null && stream.available() > 0 );
            }
            catch( IOException e )
            {
                throw getRuntime().newIOError( e.getLocalizedMessage() );
            }
        }
        return getRuntime().getNil();
    }

    @JRubyMethod()
    public IRubyObject rewind( final ThreadContext context )
    {
        getBuffer().readerIndex( 0 );
        return getRuntime().getNil();
    }

    @JRubyMethod( name = "eof?" )
    public IRubyObject isEof()
    {
        return getRuntime().newBoolean( ! getBuffer().readable() );
    }

    @JRubyMethod( name = "binmode" )
    public IRubyObject getBinmode()
    {
        return getRuntime().newBoolean( true );
    }

    @JRubyMethod()
    public void close()
    {
        throw getRuntime().newIOError( "Rack spec prohibits calling close() on rack.input stream." );
    }


    private String readLine( final ChannelBuffer buf )
    throws IOException
    {
        int startIndex = buf.readerIndex();
        ChannelBufferInputStream stream = new ChannelBufferInputStream( buf.slice() );
        String line = stream.readLine();
        int newIndex = startIndex + line.length();
        buf.readerIndex( newIndex );
        return line;
    }

    private String bufferToString( ChannelBuffer buffer )
    {
        byte[] dest = new byte[buffer.readableBytes()];
        buffer.readBytes( dest );
        return new String( dest );
    }
}
TOP

Related Classes of com.github.kevwil.aspen.io.RubyIORackInput

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.