View Javadoc

1   // AsciiInputStream.java
2   // $Id: AsciiInputStream.java,v 1.2 2006/02/20 04:52:11 sjardine Exp $
3   //
4   // Copyright 2002 Innovation Software Group, LLC - http://www.innovationsw.com
5   //
6   // This library is free software; you can redistribute it and/or
7   // modify it under the terms of the GNU Library General Public
8   // License as published by the Free Software Foundation; either
9   // version 2 of the License, or (at your option) any later version.
10  //
11  // This library is distributed in the hope that it will be useful,
12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  // Library General Public License for more details.
15  //
16  // You should have received a copy of the GNU Library General Public
17  // License along with this library; if not, write to the Free
18  // Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  //
20  package gnu.inet.ftp;
21  
22  import java.io.*;
23  
24  /***
25   * The <code>AsciiInputStream</code> class acts as a filter to convert incoming
26   * ASCII FTP streams to the system's local ASCII format by filtering and
27   * converting line termination strings. Note that the class as currently
28   * written will only handle <code>\r\n</code> or single character line
29   * termination strings; a more technically correct implementation would be
30   * preferable.
31   */
32  public class AsciiInputStream extends FilterInputStream {
33  
34     protected boolean active;
35     protected int eol;
36     protected int buffer;
37     protected int markBuffer;
38  
39     /***
40      * Creates an AsciiInputStream by passing <code>in</code> to its superclass'
41      * constructor and determining the system-specific line termination string.
42      *
43      * @param  in  the underlying input stream.
44      * 
45      * @throws  Exception  if system line separator is longer than 1 char and is
46      *                     not <code>\r\n</code>
47      */
48     public AsciiInputStream(InputStream in) throws Exception {
49        super(in);
50  
51        String lineSeparator = System.getProperty("line.separator");
52        if (lineSeparator.equals("\r\n")) {
53           active = false;
54        } else if (lineSeparator.length() > 1) {
55           throw new Exception("System line separator longer than 1 char");
56        } else {
57           active = true;
58  	 eol = lineSeparator.charAt(0);
59  	 buffer = -1;
60        }
61     }
62    
63     /***
64      * Reads the next byte of data.
65      *
66      * @return  the next byte of data, or -1 for EOF
67      * @throws  IOException  if an I/O error occurs.
68      */
69     public int read() throws IOException {
70        if (!active)                     // no need to filter
71           return in.read();
72  
73        int cur;
74  
75        if (buffer < 0) {                // buffer not filled
76           cur = in.read();              //  \-> read new value
77        } else {                         // buffer is filled
78           cur = buffer;                 //  |-> read from buffer
79  	 buffer = -1;                  //  \-> empty buffer
80        }
81  
82        if (cur != '\r') {               // test for first half of CR/LF
83           return cur;
84        } else if ((buffer = in.read()) != '\n') {           // test for 2nd half
85           return cur;
86        } else {                         // deal with CR/LF
87           buffer = -1;
88           return eol;
89        }
90     }
91  
92     /***
93      * Reads up to <code>byte.length</code> bytes of data from this input stream
94      * into an array of bytes.
95      *
96      * @param   b  the buffer into which the data is read.
97      * 
98      * @return  the total number of bytes read into the buffer, or -1 for EOF.
99      * @throws  IOException  if an I/O error occurs.
100     */
101    public int read(byte[] b) throws IOException {
102       return read(b, 0, b.length);
103    }
104 
105    /***
106     * Reads up to <code>len</code> bytes of data from this input stream into an
107     * array of bytes.
108     * 
109     * @param  b    the buffer into which the data is read.
110     * @param  off  the start offset of the data.
111     * @param  len  the maximum number of bytes read.
112     * 
113     * @return  the total number of bytes read into the buffer, or -1 for EOF.
114     * @throws  IOException  if an I/O error occurs.
115     */
116    public int read(byte[] b, int off, int len) throws IOException {
117       if (!active)                     // no need to filter
118          return in.read(b, off, len);
119 
120       if (off < 0 || len < 0 || off+len > b.length)        // bad args
121          throw new IndexOutOfBoundsException();
122 
123       if (len == 0)                    // caller is asking for nothing
124          return 0;
125       
126       int cur = read();                // no IOException trapping
127       if (cur < 0)                     // test for EOF
128          return 0;
129 
130       b[off] = (byte)cur;
131       int count = 1;
132 
133       while (count < len) {
134          try {
135             cur = read();
136          } catch (IOException e) {     // treat exceptions as EOF
137             cur = -1;
138          }
139       
140          if (cur < 0)                  // test for EOF
141             return count;
142 
143          b[off+count] = (byte)cur;
144          count++;
145       }
146 
147       return count;
148    }
149 
150    /***
151     * Skips over and discards n bytes of data from the input stream.
152     *
153     * @param  n  the number of bytes to be skipped.
154     *
155     * @return  the actual number of bytes skipped.
156     * @throws  IOException  if an I/O error occurs.
157     */
158    public long skip(long n) throws IOException {
159       if (n <= 0L)                     // do nothing
160          return 0;
161 
162       if (!active || buffer < 0)       // no filter or buffer is empty
163          return in.skip(n);
164 
165       buffer = -1;                     // empty buffer & adjust accordingly.
166       return 1L + in.skip(n - 1L);
167    }
168 
169    /***
170     * Returns the number of bytes that can be read from this input stream
171     * without blocking.
172     * 
173     * @return  the number of bytes that can be read from the input stream
174     *          without blocking.
175     * @throws  IOException  if an I/O error occurs.
176     */
177    public int available() throws IOException {
178       if (!active || buffer < 0)       // no filter or buffer is empty
179          return in.available();
180 
181       return 1 + in.available();    // account for buffer
182    }
183 
184    /***
185     * Marks the current position in this input stream.
186     *
187     * @param  readlimit  the maximum limit of bytes that can be read before the
188     *                    mark position becomes invalid.
189     */
190    public void mark(int readlimit) {
191       if (readlimit <= 0)              // do nothing
192          return;
193 
194       if (!active) {                   // no filter
195          in.mark(readlimit);
196 	 return;
197       }
198 
199       markBuffer = buffer;             // hold onto buffer
200       in.mark(readlimit - 1);          // and adjust for buffer
201    }
202 
203    /***
204     * Repositions this stream to the position at the time the mark method was
205     * last called on this input stream.
206     * 
207     * @throws  IOException  if the stream has not been or cannot be marked or
208     *                       if the mark has been invalidated.
209     */
210    public void reset() throws IOException {
211       if (!active) {                   // no filter
212          in.reset();
213 	 return;
214       }
215 
216       if (!in.markSupported())         // can't handle this, panic.
217          throw new IOException();
218 
219       buffer = markBuffer;             // restore old buffer
220       in.reset();                      // and continue.
221    }
222 
223 }