home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1995 November / PCWK1195.iso / inne / win95 / sieciowe / hotja32.lzh / hotjava / classsrc / java / io / textinputstream.java < prev   
Text File  |  1995-08-11  |  10KB  |  381 lines

  1. /*
  2.  * @(#)TextInputStream.java    1.3 95/04/02 Chuck McManis
  3.  *
  4.  * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * Permission to use, copy, modify, and distribute this software
  7.  * and its documentation for NON-COMMERCIAL purposes and without
  8.  * fee is hereby granted provided that this copyright notice
  9.  * appears in all copies. Please refer to the file "copyright.html"
  10.  * for further important copyright and licensing information.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  13.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  14.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  15.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  16.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  17.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  18.  */
  19.  
  20. /*-
  21.  * Originally I wanted to subclass BufferedInputStream but the requirement
  22.  * to do lineNumber processing screwed me up eventually. This class tries
  23.  * very hard to keep an accurate track of which line it is on.
  24.  */
  25.  
  26. package java.io;
  27.  
  28. /**
  29.  * An input stream for reading text files. This class is useful for reading
  30.  * a text file and breaking it up into Java strings. Each string will contain
  31.  * one line of text, lines with only a line terminator will be represented
  32.  * by zero length Java strings. At any time you can request the line number
  33.  * of the file you are on, but invoking the currentLine() method.
  34.  *
  35.  * <b>NOTE: This class reads ASCII date into Unicode strings, if you wish
  36.  * to read UTF (UNICODE Text Format) strings you should use DataInputStream
  37.  * instead.</b>
  38.  *
  39.  * @version     1.3, 02 Apr 1995
  40.  * @author    Chuck McManis
  41.  * @see        String
  42.  */
  43. public
  44. class TextInputStream extends FilterInputStream {
  45.     /** The current line number. */
  46.     protected int    lineNumber = 0;
  47.  
  48.     protected byte inputBuffer[];
  49.     protected int curBufferPos;
  50.  
  51.     private byte stringBuffer[];
  52.     protected boolean atEOF;
  53.  
  54.  
  55.     /**
  56.      * Create a textInputStream with a specific buffer size.
  57.      */
  58.     public TextInputStream(InputStream in, int bufSize) {
  59.     super(in);
  60.     inputBuffer = new byte[bufSize];
  61.     stringBuffer = new byte[1024];
  62.     curBufferPos = bufSize;
  63.     atEOF = false;
  64.     }
  65.  
  66.     /**
  67.      * Creates a TextInputStream.
  68.      * @param in the input stream
  69.      */
  70.     public TextInputStream(InputStream in) {
  71.     super(in);
  72.     inputBuffer = new byte[8192];
  73.     stringBuffer = new byte[1024];
  74.     curBufferPos = 8192;
  75.     atEOF = false;
  76.     }
  77.  
  78.     public boolean EOF() {
  79.     return (atEOF);
  80.     }
  81.  
  82.     /**
  83.      * a private helper function to refill the buffer. The only tricky
  84.      * part here is that if the read cannot completely fill the inputBuffer
  85.      * it 'moves' what was read to the end of the input buffer and points
  86.      * curBufferPos at it. This makes the end of file detection code easier.
  87.      */
  88.     private int fillBuffer() {
  89.     int i, j;
  90.  
  91.     /* It wasn't a parsed string so refill the buffer. */
  92.     j = super.in.read(inputBuffer);
  93.     if (j == -1) {
  94.         atEOF = true;
  95.         return (-1);
  96.     }
  97.     if (j < inputBuffer.length) {
  98.         for (i = 0; i < j; i++) {
  99.         inputBuffer[(inputBuffer.length - 1) - i] =
  100.                             inputBuffer[(j - 1) - i];
  101.         }
  102.         curBufferPos = inputBuffer.length - j;
  103.     } else {
  104.         curBufferPos = 0;
  105.     }
  106.     return (j);
  107.     }
  108.  
  109.     /**
  110.      * Read an ISO-Latin1 line of text from the input stream and return a 
  111.      * Java String.
  112.      *
  113.      * The line terminator is stripped from the returned string. This 
  114.      * method recognizes <CR>, <CR><LF>, and <LF> as the end of line 
  115.      * sequence. <b>Note: it treats a <LF><CR> sequence as two lines.</b>
  116.      * While this might be considered a bug, no current operating
  117.      * system uses <LF><CR> to terminate lines in text files.
  118.      *     @return null on end-of-file.
  119.      */
  120.     public synchronized String readLine() {
  121.     int i, j, stringlen;
  122.  
  123.     i = 0;
  124.     stringlen =0;
  125.     if (atEOF) {
  126.         return (null);
  127.     }
  128.  
  129.     while (true) {
  130.         while (curBufferPos < inputBuffer.length) {
  131.         if ((inputBuffer[curBufferPos] == '\n') ||
  132.             (inputBuffer[curBufferPos] == '\r')) {
  133.             break;
  134.         }
  135.  
  136.         stringBuffer[stringlen] = inputBuffer[curBufferPos];
  137.         stringlen++;
  138.         curBufferPos++;
  139.         currentReadLimit--;
  140.         if (stringlen == 1024) {
  141.             throw new Exception(
  142.                 "TextInputStream: String Buffer Overflow!");
  143.         }
  144.         }
  145.  
  146.         /* 
  147.          * At this point we can either be buffer starved or ready
  148.          * to process a string that has been read in.
  149.          */
  150.         if (curBufferPos < inputBuffer.length) {
  151.         byte    tmpChar;
  152.  
  153.         lineNumber++;
  154.  
  155.         /*
  156.          * Case 1:  Character at the current point is a line-feed
  157.          *          this is Guaranteed to be the end of a line.
  158.          */
  159.         if (inputBuffer[curBufferPos] == '\n') {
  160.             curBufferPos++;
  161.             return (new String(stringBuffer, 0, 0, stringlen));
  162.         }
  163.  
  164.         /*
  165.          * Case 2: Character at the current point is a <CR> and
  166.          *         the following character is in the buffer. Thus
  167.          *         we can check for <CR> only, and <CR><LF> without
  168.          *        reading any more data.
  169.          */
  170.         if ((curBufferPos + 1) < inputBuffer.length) {
  171.             curBufferPos++;
  172.             if (inputBuffer[curBufferPos] == '\n') {
  173.             curBufferPos++;
  174.             }
  175.             return (new String(stringBuffer, 0, 0, stringlen));
  176.         }
  177.  
  178.         /*
  179.          * Case 3: We're at the end of the buffer and we've seen
  180.          *       a <CR>. We read one byte from the file and
  181.          *        this generates its own sub cases.
  182.          */
  183.             tmpChar = (byte) super.in.read();
  184.  
  185.         /* Case 3a: End of file. Known good string so return it */
  186.         if (tmpChar == -1) {
  187.             atEOF = true;
  188.             return (new String(stringBuffer, 0, 0, stringlen));
  189.         }
  190.  
  191.         /*
  192.          * Case 3b: Next char was <lf> so we toss it and return
  193.          *         the string.
  194.          */
  195.         if (tmpChar == '\n') {
  196.             return (new String(stringBuffer, 0, 0, stringlen));
  197.         }
  198.  
  199.         /*
  200.          * Case 3c: It wasn't a <lf> so we stick it at the end
  201.          *        of the buffer (will get processed on next
  202.          *        read.)
  203.          */
  204.         inputBuffer[curBufferPos] = tmpChar;
  205.         return (new String(stringBuffer, 0, 0, stringlen));
  206.         }
  207.  
  208.         /* It wasn't a parsed string so refill the buffer. */
  209.         j = fillBuffer();
  210.         if (j == -1) {
  211.         if (stringlen != 0) {
  212.             lineNumber++;
  213.             return (new String(stringBuffer, 0, 0, stringlen));
  214.         } else {
  215.             return (null);
  216.         }
  217.         }
  218.     }
  219.     }
  220.  
  221.     private int EOLState = 0;
  222.  
  223.     /**
  224.      * lineStateMachine - is a private function that keeps tracks of lines
  225.      * that have been passed while reading through the read() interfaces.
  226.      */
  227.     void lineStateMachine(byte b) {
  228.     switch (b) {
  229.         case '\n' : 
  230.         EOLState = 0;
  231.         lineNumber++;
  232.         break;
  233.         case '\r' :
  234.         EOLState = 1;    // Potential end of line
  235.         break;
  236.         default:
  237.         if (EOLState == 1) {
  238.             lineNumber++;
  239.             EOLState = 0;
  240.         }
  241.         break;
  242.     }
  243.     }
  244.  
  245.     
  246.     /**
  247.      * read a byte - reads a single byte from the input stream and returns
  248.      * -1 on end of file. Internally the line number is tracked by watching
  249.      * for line terminator characters to go by.
  250.      */
  251.     public synchronized int read() {
  252.     int j;
  253.     if (curBufferPos < inputBuffer.length) {
  254.         lineStateMachine(inputBuffer[curBufferPos]);
  255.         currentReadLimit--;
  256.         return (inputBuffer[curBufferPos++]);
  257.     }
  258.     j = fillBuffer();
  259.     if (j == -1) {
  260.         return (-1);
  261.     }
  262.     lineStateMachine(inputBuffer[curBufferPos]);
  263.     currentReadLimit--;
  264.     return (inputBuffer[curBufferPos++]);
  265.     }
  266.  
  267.     /**
  268.      * read into a byte array. This is augmented to manage the line number
  269.      * state.
  270.      */
  271.     public synchronized int read(byte b[]) {
  272.     int j, bytesFilled;
  273.  
  274.     j = 0;
  275.     bytesFilled = 0;
  276.     if (atEOF) {
  277.         return (-1);
  278.     }
  279.     while (true) {
  280.         while (curBufferPos < inputBuffer.length) {
  281.         lineStateMachine(inputBuffer[curBufferPos]);
  282.         b[bytesFilled++] = inputBuffer[curBufferPos++];
  283.         currentReadLimit--;
  284.         if (bytesFilled == b.length) {
  285.            return (bytesFilled);
  286.         }
  287.         }
  288.         j = fillBuffer();
  289.         if (j == -1) {
  290.         if (bytesFilled > 0) {
  291.             return (bytesFilled);
  292.         } else {
  293.             return (-1);
  294.         }
  295.         }
  296.     }
  297.     }
  298.         
  299.  
  300.     /**
  301.      * Returns the current line number.
  302.      */
  303.     public int currentLine() {
  304.     return lineNumber;
  305.     }
  306.  
  307.     /**
  308.      * Skip some number of bytes of input, note that this actually reads
  309.      * the bytes that are skipped to keep track of the current line number.
  310.      */
  311.     public int skip(int n) {
  312.     int    bytesSkipped = 0, j;
  313.  
  314.     while (n > 0) {
  315.         while (curBufferPos < inputBuffer.length) {
  316.         lineStateMachine(inputBuffer[curBufferPos]);
  317.         n--;
  318.         bytesSkipped++;
  319.         currentReadLimit--;
  320.         curBufferPos++;
  321.         }
  322.         j = fillBuffer();
  323.         if (j == -1) {
  324.         return (bytesSkipped);
  325.         }
  326.     }
  327.     return (bytesSkipped);
  328.     }
  329.  
  330.     /**
  331.      * Returns the number of bytes that can be read, this is the sum of
  332.      * what is in the buffer and what the stream returns.
  333.      */
  334.     public synchronized int available() {
  335.     return ((inputBuffer.length - curBufferPos) + in.available());
  336.     }
  337.  
  338.     private int markedLineNumber = 0;
  339.     private int markReadLimit = 0;
  340.     private int markEOLState = 0;
  341.     private int currentReadLimit = 0;
  342.  
  343.     /**
  344.      * Set a mark on the InputStream, if that stream supports marks. In
  345.      * this class we will keep track of the current line number as well.
  346.      *
  347.      * @see FilterInputStream
  348.      */
  349.     public synchronized void mark(int readlimit) {
  350.     if (super.in.markSupported()) {
  351.         markedLineNumber = lineNumber;
  352.         markEOLState = EOLState;
  353.         markReadLimit = readlimit;
  354.         currentReadLimit = readlimit;
  355.         super.in.mark(readlimit);
  356.     }
  357.     }
  358.  
  359.     /**
  360.      * Reposition the stream to the last marked position, restores the line
  361.      * number to the number that existed when the mark was set.
  362.      *
  363.      * @see FilterInputStream
  364.      */
  365.     public synchronized void reset() {
  366.     /*
  367.      * Check to see if the call to reset() will in fact reset to
  368.      * a mark, or if it will just reset to the beginning. If it
  369.      * will reset to a mark then we adjust the line number accordingly.
  370.      */
  371.     if ((super.in.markSupported()) && (currentReadLimit > 0)) {
  372.         lineNumber = markedLineNumber;
  373.         EOLState = markEOLState;
  374.         currentReadLimit = markReadLimit;
  375.     } else {
  376.         lineNumber = 0;
  377.     }
  378.     super.in.reset();
  379.     }
  380. }
  381.