Source for gnu.inet.util.LineInputStream

   1: /*
   2:  * LineInputStream.java
   3:  * Copyright (C) 2002 The Free Software Foundation
   4:  * 
   5:  * This file is part of GNU inetlib, a library.
   6:  * 
   7:  * GNU inetlib is free software; you can redistribute it and/or modify
   8:  * it under the terms of the GNU General Public License as published by
   9:  * the Free Software Foundation; either version 2 of the License, or
  10:  * (at your option) any later version.
  11:  * 
  12:  * GNU inetlib is distributed in the hope that it will be useful,
  13:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15:  * GNU General Public License for more details.
  16:  * 
  17:  * You should have received a copy of the GNU General Public License
  18:  * along with this library; if not, write to the Free Software
  19:  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20:  *
  21:  * Linking this library statically or dynamically with other modules is
  22:  * making a combined work based on this library.  Thus, the terms and
  23:  * conditions of the GNU General Public License cover the whole
  24:  * combination.
  25:  *
  26:  * As a special exception, the copyright holders of this library give you
  27:  * permission to link this library with independent modules to produce an
  28:  * executable, regardless of the license terms of these independent
  29:  * modules, and to copy and distribute the resulting executable under
  30:  * terms of your choice, provided that you also meet, for each linked
  31:  * independent module, the terms and conditions of the license of that
  32:  * module.  An independent module is a module which is not derived from
  33:  * or based on this library.  If you modify this library, you may extend
  34:  * this exception to your version of the library, but you are not
  35:  * obliged to do so.  If you do not wish to do so, delete this
  36:  * exception statement from your version.
  37:  */
  38: 
  39: package gnu.inet.util;
  40: 
  41: import java.io.ByteArrayOutputStream;
  42: import java.io.FilterInputStream;
  43: import java.io.InputStream;
  44: import java.io.IOException;
  45: 
  46: /**
  47:  * An input stream that can read lines of input.
  48:  *
  49:  * @author <a href="mailto:dog@gnu.org">Chris Burdess</a>
  50:  */
  51: public class LineInputStream
  52:   extends FilterInputStream
  53: {
  54: 
  55:   /*
  56:    * Line buffer.
  57:    */
  58:   private ByteArrayOutputStream buf;
  59: 
  60:   /*
  61:    * Encoding to use when translating bytes to characters.
  62:    */
  63:   private String encoding;
  64: 
  65:   /*
  66:    * End-of-stream flag.
  67:    */
  68:   private boolean eof;
  69: 
  70:   /**
  71:    * Whether we can use block reads.
  72:    */
  73:   private final boolean blockReads;
  74: 
  75:   /**
  76:    * Constructor using the US-ASCII character encoding.
  77:    * @param in the underlying input stream
  78:    */
  79:   public LineInputStream(InputStream in)
  80:   {
  81:     this(in, "US-ASCII");
  82:   }
  83: 
  84:   /**
  85:    * Constructor.
  86:    * @param in the underlying input stream
  87:    * @param encoding the character encoding to use
  88:    */
  89:   public LineInputStream(InputStream in, String encoding)
  90:   {
  91:     super(in);
  92:     buf = new ByteArrayOutputStream();
  93:     this.encoding = encoding;
  94:     eof = false;
  95:     blockReads = in.markSupported();
  96:   }
  97: 
  98:   /**
  99:    * Read a line of input.
 100:    */
 101:   public String readLine()
 102:     throws IOException
 103:   {
 104:     if (eof)
 105:       {
 106:         return null;
 107:       }
 108:     do
 109:       {
 110:         if (blockReads)
 111:           {
 112:             // Use mark and reset to read chunks of bytes
 113:             final int MIN_LENGTH = 1024;
 114:             int len, pos;
 115:             
 116:             len = in.available();
 117:             len = (len < MIN_LENGTH) ? MIN_LENGTH : len;
 118:             byte[] b = new byte[len];
 119:             in.mark(len);
 120:             // Read into buffer b
 121:             len = in.read(b, 0, len);
 122:             // Handle EOF
 123:             if (len == -1)
 124:               {
 125:                 eof = true;
 126:                 if (buf.size() == 0)
 127:                   {
 128:                     return null;
 129:                   }
 130:                 else
 131:                   {
 132:                     // We don't care about resetting buf
 133:                     return buf.toString(encoding);
 134:                   }
 135:               }
 136:             // Get index of LF in b
 137:             pos = indexOf(b, len, (byte) 0x0a);
 138:             if (pos != -1)
 139:               {
 140:                 // Write pos bytes to buf
 141:                 buf.write(b, 0, pos);
 142:                 // Reset stream, and read pos + 1 bytes
 143:                 in.reset();
 144:                 pos += 1;
 145:                 while (pos > 0)
 146:                   {
 147:                     len = in.read(b, 0, pos);
 148:                     pos = (len == -1) ? -1 : pos - len;
 149:                   }
 150:                 // Return line
 151:                 String ret = buf.toString(encoding);
 152:                 buf.reset();
 153:                 return ret;
 154:               }
 155:             else
 156:               {
 157:                 // Append everything to buf and fall through to re-read.
 158:                 buf.write(b, 0, len);
 159:               }
 160:           }
 161:         else
 162:           {
 163:             // We must use character reads in order not to read too much
 164:             // from the underlying stream.
 165:             int c = in.read();
 166:             switch (c)
 167:               {
 168:               case -1:
 169:                 eof = true;
 170:                 if (buf.size() == 0)
 171:                   {
 172:                     return null;
 173:                   }
 174:                 // Fall through and return contents of buffer.
 175:               case 0x0a:                // LF
 176:                 String ret = buf.toString(encoding);
 177:                 buf.reset();
 178:                 return ret;
 179:               default:
 180:                 buf.write(c);
 181:               }
 182:           }
 183:       }
 184:     while (true);
 185:   }
 186: 
 187:   private int indexOf(byte[] b, int len, byte c)
 188:   {
 189:     for (int pos = 0; pos < len; pos++)
 190:       {
 191:         if (b[pos] == c)
 192:           {
 193:             return pos;
 194:           }
 195:       }
 196:     return -1;
 197:   }
 198: 
 199: }