View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.io;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.PushbackInputStream;
23  import java.io.UnsupportedEncodingException;
24  
25  /***
26   * This class wraps an input stream, replacing all occurrences
27   * of <CR><LF> (carriage return followed by a linefeed),
28   * which is the NETASCII standard for representing a newline, with the
29   * local line separator representation.  You would use this class to
30   * implement ASCII file transfers requiring conversion from NETASCII.
31   *
32   *
33   ***/
34  
35  public final class FromNetASCIIInputStream extends PushbackInputStream
36  {
37      static final boolean _noConversionRequired;
38      static final String _lineSeparator;
39      static final byte[] _lineSeparatorBytes;
40  
41      static {
42          _lineSeparator = System.getProperty("line.separator");
43          _noConversionRequired = _lineSeparator.equals("\r\n");
44          try {
45              _lineSeparatorBytes = _lineSeparator.getBytes("US-ASCII");
46          } catch (UnsupportedEncodingException e) {
47             throw new RuntimeException("Broken JVM - cannot find US-ASCII charset!",e);
48          }
49      }
50  
51      private int __length = 0;
52  
53      /***
54       * Returns true if the NetASCII line separator differs from the system
55       * line separator, false if they are the same.  This method is useful
56       * to determine whether or not you need to instantiate a
57       * FromNetASCIIInputStream object.
58       *
59       * @return True if the NETASCII line separator differs from the local
60       *   system line separator, false if they are the same.
61       ***/
62      public static final boolean isConversionRequired()
63      {
64          return !_noConversionRequired;
65      }
66  
67      /***
68       * Creates a FromNetASCIIInputStream instance that wraps an existing
69       * InputStream.
70       * @param input the stream to wrap
71       ***/
72      public FromNetASCIIInputStream(InputStream input)
73      {
74          super(input, _lineSeparatorBytes.length + 1);
75      }
76  
77  
78      private int __read() throws IOException
79      {
80          int ch;
81  
82          ch = super.read();
83  
84          if (ch == '\r')
85          {
86              ch = super.read();
87              if (ch == '\n')
88              {
89                  unread(_lineSeparatorBytes);
90                  ch = super.read();
91                  // This is a kluge for read(byte[], ...) to read the right amount
92                  --__length;
93              }
94              else
95              {
96                  if (ch != -1) {
97                      unread(ch);
98                  }
99                  return '\r';
100             }
101         }
102 
103         return ch;
104     }
105 
106 
107     /***
108      * Reads and returns the next byte in the stream.  If the end of the
109      * message has been reached, returns -1.  Note that a call to this method
110      * may result in multiple reads from the underlying input stream in order
111      * to convert NETASCII line separators to the local line separator format.
112      * This is transparent to the programmer and is only mentioned for
113      * completeness.
114      *
115      * @return The next character in the stream. Returns -1 if the end of the
116      *          stream has been reached.
117      * @exception IOException If an error occurs while reading the underlying
118      *            stream.
119      ***/
120     @Override
121     public int read() throws IOException
122     {
123         if (_noConversionRequired) {
124             return super.read();
125         }
126 
127         return __read();
128     }
129 
130 
131     /***
132      * Reads the next number of bytes from the stream into an array and
133      * returns the number of bytes read.  Returns -1 if the end of the
134      * stream has been reached.
135      *
136      * @param buffer  The byte array in which to store the data.
137      * @return The number of bytes read. Returns -1 if the
138      *          end of the message has been reached.
139      * @exception IOException If an error occurs in reading the underlying
140      *            stream.
141      ***/
142     @Override
143     public int read(byte buffer[]) throws IOException
144     {
145         return read(buffer, 0, buffer.length);
146     }
147 
148 
149     /***
150      * Reads the next number of bytes from the stream into an array and returns
151      * the number of bytes read.  Returns -1 if the end of the
152      * message has been reached.  The characters are stored in the array
153      * starting from the given offset and up to the length specified.
154      *
155      * @param buffer The byte array in which to store the data.
156      * @param offset  The offset into the array at which to start storing data.
157      * @param length   The number of bytes to read.
158      * @return The number of bytes read. Returns -1 if the
159      *          end of the stream has been reached.
160      * @exception IOException If an error occurs while reading the underlying
161      *            stream.
162      ***/
163     @Override
164     public int read(byte buffer[], int offset, int length) throws IOException
165     {
166         if (_noConversionRequired) {
167             return super.read(buffer, offset, length);
168         }
169 
170         if (length < 1) {
171             return 0;
172         }
173 
174         int ch, off;
175 
176         ch = available();
177 
178         __length = (length > ch ? ch : length);
179 
180         // If nothing is available, block to read only one character
181         if (__length < 1) {
182             __length = 1;
183         }
184 
185 
186         if ((ch = __read()) == -1) {
187             return -1;
188         }
189 
190         off = offset;
191 
192         do
193         {
194             buffer[offset++] = (byte)ch;
195         }
196         while (--__length > 0 && (ch = __read()) != -1);
197 
198 
199         return (offset - off);
200     }
201 
202 
203     // PushbackInputStream in JDK 1.1.3 returns the wrong thing
204     // TODO - can we delete this override now?
205     /***
206      * Returns the number of bytes that can be read without blocking EXCEPT
207      * when newline conversions have to be made somewhere within the
208      * available block of bytes.  In other words, you really should not
209      * rely on the value returned by this method if you are trying to avoid
210      * blocking.
211      ***/
212     @Override
213     public int available() throws IOException
214     {
215         if (in == null) {
216             throw new IOException("Stream closed");
217         }
218         return (buf.length - pos) + in.available();
219     }
220 
221 }