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