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    *      https://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.FilterInputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  
24  import org.apache.commons.net.util.NetConstants;
25  
26  /**
27   * This class wraps an input stream, replacing all singly occurring <LF> (linefeed) characters with <CR><LF> (carriage return followed by
28   * linefeed), which is the NETASCII standard for representing a newline. You would use this class to implement ASCII file transfers requiring conversion to
29   * NETASCII.
30   */
31  public final class ToNetASCIIInputStream extends FilterInputStream {
32      private static final int NOTHING_SPECIAL = 0;
33      private static final int LAST_WAS_CR = 1;
34      private static final int LAST_WAS_NL = 2;
35      private int status;
36  
37      /**
38       * Creates a ToNetASCIIInputStream instance that wraps an existing InputStream.
39       *
40       * @param input The InputStream to wrap.
41       */
42      public ToNetASCIIInputStream(final InputStream input) {
43          super(input);
44          status = NOTHING_SPECIAL;
45      }
46  
47      @Override
48      public int available() throws IOException {
49          final int result;
50  
51          result = in.available();
52  
53          if (status == LAST_WAS_NL) {
54              return result + 1;
55          }
56  
57          return result;
58      }
59  
60      /** Returns false. Mark is not supported. */
61      @Override
62      public boolean markSupported() {
63          return false;
64      }
65  
66      /**
67       * Reads and returns the next byte in the stream. If the end of the message has been reached, returns -1.
68       *
69       * @return The next character in the stream. Returns -1 if the end of the stream has been reached.
70       * @throws IOException If an error occurs while reading the underlying stream.
71       */
72      @Override
73      public int read() throws IOException {
74          final int ch;
75  
76          if (status == LAST_WAS_NL) {
77              status = NOTHING_SPECIAL;
78              return '\n';
79          }
80  
81          ch = in.read();
82  
83          switch (ch) {
84          case '\r':
85              status = LAST_WAS_CR;
86              return '\r';
87          case '\n':
88              if (status != LAST_WAS_CR) {
89                  status = LAST_WAS_NL;
90                  return '\r';
91              }
92              // falls through$
93          default:
94              status = NOTHING_SPECIAL;
95              return ch;
96          }
97          // statement not reached
98          // return ch;
99      }
100 
101     /**
102      * 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.
103      *
104      * @param buffer The byte array in which to store the data.
105      * @return The number of bytes read. Returns -1 if the end of the message has been reached.
106      * @throws IOException If an error occurs in reading the underlying stream.
107      */
108     @Override
109     public int read(final byte[] buffer) throws IOException {
110         return read(buffer, 0, buffer.length);
111     }
112 
113     /**
114      * 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.
115      * The characters are stored in the array starting from the given offset and up to the length specified.
116      *
117      * @param buffer The byte array in which to store the data.
118      * @param offset The offset into the array at which to start storing data.
119      * @param length The number of bytes to read.
120      * @return The number of bytes read. Returns -1 if the end of the stream has been reached.
121      * @throws IOException If an error occurs while reading the underlying stream.
122      */
123     @Override
124     public int read(final byte[] buffer, int offset, int length) throws IOException {
125         int ch;
126         final int off;
127 
128         if (length < 1) {
129             return 0;
130         }
131 
132         ch = available();
133 
134         if (length > ch) {
135             length = ch;
136         }
137 
138         // If nothing is available, block to read only one character
139         if (length < 1) {
140             length = 1;
141         }
142 
143         if ((ch = read()) == NetConstants.EOS) {
144             return NetConstants.EOS;
145         }
146 
147         off = offset;
148 
149         do {
150             buffer[offset++] = (byte) ch;
151         } while (--length > 0 && (ch = read()) != NetConstants.EOS);
152 
153         return offset - off;
154     }
155 }