ChannelReader.java :  » UnTagged » growlforandroid » com » growlforandroid » common » Android Open Source

Android Open Source » UnTagged » growlforandroid 
growlforandroid » com » growlforandroid » common » ChannelReader.java
package com.growlforandroid.common;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

import com.growlforandroid.gntp.Constants;

import android.util.Log;

/**
 * Reads byte and UTF-8 character data from a channel
 * @author Carey Bishop
  */
public class ChannelReader {
  protected static final int BUFFER_SIZE = 8192;
  
  private static final byte UTF8_MULTI_START = (byte) 0x80;
  private static final byte UTF8_MULTI_MASK = (byte) 0xC0;
  
  private static final byte UTF8_MULTI_START_2 = (byte) 0xC0;
  private static final byte UTF8_MULTI_START_2_MASK = (byte) 0xE0;
  
  private static final byte UTF8_MULTI_START_3 = (byte) 0xE0;
  private static final byte UTF8_MULTI_START_3_MASK = (byte) 0xF0;
  
  private static final byte UTF8_MULTI_START_4 = (byte) 0xF0;
  private static final byte UTF8_MULTI_START_4_MASK = (byte) 0xF8;
  
  private final SocketChannel _channel;
  protected ByteBuffer _buffer;
  protected int _availableBytes = 0;

  public ChannelReader(SocketChannel channel) {
    _channel = channel;
    _buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
  }
  
  /**
   * Fills the byte buffer with any available data from the channel
   * and updates _availableBytes
   * @throws IOException
   */
  private void fillBuffer() throws IOException {
    _buffer.rewind();
    Log.i("ChannelReader.fillBuffer", "Filling the buffer...");
    while (_availableBytes == 0) {
      _availableBytes =_channel.read(_buffer);
    }
    Log.i("ChannelReader.fillBuffer", "Read " + _availableBytes + " bytes into the buffer");
    _buffer.rewind();
  }
  
  /**
   * Reads an array of bytes from the channel
   * @param length  The number of bytes to be read
   * @return A new array of byte data
   * @throws IOException
   */
  public byte[] readBytes(int length) throws IOException {
    byte[] data = new byte[length];
    for(int i=0; i < data.length; i++) {
      data[i] = readByte();
    }
    return data;
  }
  
  /**
   * Reads an array of bytes from the channel and discards the result
   * @param length  The number of bytes to be skipped
   * @throws IOException
   */
  public void skipBytes(int length) throws IOException {
    for(int i=0; i < length; i++) {
      readByte();
    }
  }
  
  /**
   * Reads a single byte from the channel
   * @return
   * @throws IOException
   */
  public byte readByte() throws IOException {
    if (_availableBytes == 0) {
      fillBuffer();
    }
    byte data = _buffer.get();
    _availableBytes--;
    return data;
  }
  
  public ByteBuffer readBytesUntil(byte[] delimiter) throws IOException {
    if ((delimiter == null) || (delimiter.length == 0))
      throw new IOException("delimiter is invalid");
    
    byte endOfDelimiter = delimiter[delimiter.length - 1];
    ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
    
    while (true) {
      // Read a byte into the buffer
      byte data = readByte();
      buffer.put(data);
      
      // If this byte is the last byte of the delimiter
      if ((buffer.position() >= delimiter.length) && (data == endOfDelimiter)) {
        // Check to see if the preceding bytes also match the delimiter
        boolean found = true;
        int startOfDelimiter = buffer.position() - delimiter.length;
        for (int i = 0; i < delimiter.length; i++) {
          data = buffer.get(startOfDelimiter + i);
          if (data != delimiter[i]) {
            found = false;
            break;
          }
        }
        
        if (found) {
          return buffer;
        }
      }
    }
  }
  
  /**
   * Reads a byte from the channel and checks that it is a valid UTF-8 suffix byte
   * @return
   * @throws IOException
   */
  private byte readUTF8MultiByte() throws IOException {
    byte data = readByte();
    if ((data & UTF8_MULTI_MASK) != UTF8_MULTI_START) {
      throw new IOException("Invalid UTF-8 data");
    }
    return data;
  }
  
  /**
   * Reads a single UTF-8 character from the channel
   * @return
   * @throws IOException
   */
  public char readChar() throws IOException {
    byte data = readByte();
    if ((data & UTF8_MULTI_START) == UTF8_MULTI_START) {
      if ((data & UTF8_MULTI_START_2_MASK) == UTF8_MULTI_START_2) {
        return readTwoByteChar(data);
      } else if ((data & UTF8_MULTI_START_3_MASK) == UTF8_MULTI_START_3) {
        return readThreeByteChar(data);
      } else if ((data & UTF8_MULTI_START_4_MASK) == UTF8_MULTI_START_4) {
        return readFourByteChar(data);
      }
      throw new IOException("Invalid UTF-8 data");
      
    } else {
      return (char)data;
    }
  }
  
  /**
   * Reads a two-byte UTF-8 character from the channel
   * @param firstByte    The contents of the first byte of the character byte sequence
   * @return
   * @throws IOException
   */
  private char readTwoByteChar(byte firstByte) throws IOException {
    byte secondByte = readUTF8MultiByte();
    int data = ((firstByte & 0x1C) << 8) | (secondByte & 0x3F);
    return (char)data;
  }
  
  /**
   * Reads a three-byte UTF-8 character from the channel
   * @param firstByte    The contents of the first byte of the character byte sequence
   * @return
   * @throws IOException
   */
  private char readThreeByteChar(byte firstByte) throws IOException {
    byte secondByte = readUTF8MultiByte();
    byte thirdByte = readUTF8MultiByte();
    int data =
      ((firstByte & 0x0F) << 12) |
      ((secondByte & 0x3F) << 6) |
      (thirdByte & 0x3F);
    return (char)data;
  }
  
  /**
   * Reads a four-byte UTF-8 character from the channel
   * @param firstByte    The contents of the first byte of the character byte sequence
   * @return
   * @throws IOException
   */
  private char readFourByteChar(byte firstByte) throws IOException {
    byte secondByte = readUTF8MultiByte();
    byte thirdByte = readUTF8MultiByte();
    byte fourthByte = readUTF8MultiByte();
    int data =
      ((firstByte & 0x07) << 18) |
      ((secondByte & 0x3F) << 12) |
      ((thirdByte & 0x3F) << 6) |
      (fourthByte & 0x3F);
    return (char)data;  
  }
  
  /**
   * Reads characters from the channel until delimiter is found.
   * The delimiter is included in the output. 
   * @param delimiter    The character to stop reading at when found
   * @return
   * @throws IOException
   */
  private String readCharsUntil(char delimiter) throws IOException {
    StringBuffer buffer = new StringBuffer(255);
    char next;
    do {
      next = readChar();
      buffer.append(next);
    } while (next != delimiter);
    
    String result = buffer.toString();
    return result;
  }
  
  /**
   * Reads characters from the channel until delimiter is found.
   * The delimiter is included in the output.
   * @param delimiter    The string that delimits the end of the data
   * @return
   * @throws IOException
   */
  public String readCharsUntil(String delimiter) throws IOException {
    String line = "";
    
    // Grab the last character of the delimiter
    char lastChar = delimiter.charAt(delimiter.length() - 1);
    while (!line.endsWith(delimiter)) {
      line += readCharsUntil(lastChar);
    }

    return line;
  }
  
  public String readLine() throws IOException {
    String line = readCharsUntil(Constants.END_OF_LINE);
    int withoutDelimiter = line.length() - Constants.END_OF_LINE.length();
    if ((line != null) && (withoutDelimiter >= 0))
      line = line.substring(0, withoutDelimiter);
    // Log.d("ChannelReader.readLine", line);
    return line;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.