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