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  
24  /***
25   * This class wraps an input stream, replacing all occurrences
26   * of <CR><LF> (carriage return followed by a linefeed),
27   * which is the NETASCII standard for representing a newline, with the
28   * local line separator representation.  You would use this class to
29   * implement ASCII file transfers requiring conversion from NETASCII.
30   * <p>
31   * <p>
32   ***/
33  
34  public final class FromNetASCIIInputStream extends PushbackInputStream
35  {
36      static final boolean _noConversionRequired;
37      static final String _lineSeparator;
38      static final byte[] _lineSeparatorBytes;
39  
40      static {
41          _lineSeparator = System.getProperty("line.separator");
42          _noConversionRequired = _lineSeparator.equals("\r\n");
43          _lineSeparatorBytes = _lineSeparator.getBytes();
44      }
45  
46      private int __length = 0;
47  
48      /***
49       * Returns true if the NetASCII line separator differs from the system
50       * line separator, false if they are the same.  This method is useful
51       * to determine whether or not you need to instantiate a
52       * FromNetASCIIInputStream object.
53       * <p>
54       * @return True if the NETASCII line separator differs from the local
55       *   system line separator, false if they are the same.
56       ***/
57      public static final boolean isConversionRequired()
58      {
59          return !_noConversionRequired;
60      }
61  
62      /***
63       * Creates a FromNetASCIIInputStream instance that wraps an existing
64       * InputStream.
65       ***/
66      public FromNetASCIIInputStream(InputStream input)
67      {
68          super(input, _lineSeparatorBytes.length + 1);
69      }
70  
71  
72      private int __read() throws IOException
73      {
74          int ch;
75  
76          ch = super.read();
77  
78          if (ch == '\r')
79          {
80              ch = super.read();
81              if (ch == '\n')
82              {
83                  unread(_lineSeparatorBytes);
84                  ch = super.read();
85                  // This is a kluge for read(byte[], ...) to read the right amount
86                  --__length;
87              }
88              else
89              {
90                  if (ch != -1) {
91                      unread(ch);
92                  }
93                  return '\r';
94              }
95          }
96  
97          return ch;
98      }
99  
100 
101     /***
102      * Reads and returns the next byte in the stream.  If the end of the
103      * message has been reached, returns -1.  Note that a call to this method
104      * may result in multiple reads from the underlying input stream in order
105      * to convert NETASCII line separators to the local line separator format.
106      * This is transparent to the programmer and is only mentioned for
107      * completeness.
108      * <p>
109      * @return The next character in the stream. Returns -1 if the end of the
110      *          stream has been reached.
111      * @exception IOException If an error occurs while reading the underlying
112      *            stream.
113      ***/
114     @Override
115     public int read() throws IOException
116     {
117         if (_noConversionRequired) {
118             return super.read();
119         }
120 
121         return __read();
122     }
123 
124 
125     /***
126      * Reads the next number of bytes from the stream into an array and
127      * returns the number of bytes read.  Returns -1 if the end of the
128      * stream has been reached.
129      * <p>
130      * @param buffer  The byte array in which to store the data.
131      * @return The number of bytes read. Returns -1 if the
132      *          end of the message has been reached.
133      * @exception IOException If an error occurs in reading the underlying
134      *            stream.
135      ***/
136     @Override
137     public int read(byte buffer[]) throws IOException
138     {
139         return read(buffer, 0, buffer.length);
140     }
141 
142 
143     /***
144      * Reads the next number of bytes from the stream into an array and returns
145      * the number of bytes read.  Returns -1 if the end of the
146      * message has been reached.  The characters are stored in the array
147      * starting from the given offset and up to the length specified.
148      * <p>
149      * @param buffer The byte array in which to store the data.
150      * @param offset  The offset into the array at which to start storing data.
151      * @param length   The number of bytes to read.
152      * @return The number of bytes read. Returns -1 if the
153      *          end of the stream has been reached.
154      * @exception IOException If an error occurs while reading the underlying
155      *            stream.
156      ***/
157     @Override
158     public int read(byte buffer[], int offset, int length) throws IOException
159     {
160         if (_noConversionRequired) {
161             return super.read(buffer, offset, length);
162         }
163 
164         if (length < 1) {
165             return 0;
166         }
167 
168         int ch, off;
169 
170         ch = available();
171 
172         __length = (length > ch ? ch : length);
173 
174         // If nothing is available, block to read only one character
175         if (__length < 1) {
176             __length = 1;
177         }
178 
179 
180         if ((ch = __read()) == -1) {
181             return -1;
182         }
183 
184         off = offset;
185 
186         do
187         {
188             buffer[offset++] = (byte)ch;
189         }
190         while (--__length > 0 && (ch = __read()) != -1);
191 
192 
193         return (offset - off);
194     }
195 
196 
197     // PushbackInputStream in JDK 1.1.3 returns the wrong thing
198     // TODO - can we delete this override now?
199     /***
200      * Returns the number of bytes that can be read without blocking EXCEPT
201      * when newline conversions have to be made somewhere within the
202      * available block of bytes.  In other words, you really should not
203      * rely on the value returned by this method if you are trying to avoid
204      * blocking.
205      ***/
206     @Override
207     public int available() throws IOException
208     {
209         if (in == null) {
210             throw new IOException("Stream closed");
211         }
212         return (buf.length - pos) + in.available();
213     }
214 
215 }