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