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.FilterOutputStream;
21 import java.io.IOException;
22 import java.io.OutputStream;
23
24 /**
25 * This class wraps an output stream, replacing all occurrences of <CR><LF> (carriage return followed by a linefeed), which is the NETASCII standard
26 * for representing a newline, with the local line separator representation. You would use this class to implement ASCII file transfers requiring conversion
27 * from NETASCII.
28 * <p>
29 * Because of the translation process, a call to {@code flush()} will not flush the last byte written if that byte was a carriage return. A call to
30 * {@link #close close()}, however, will flush the carriage return.
31 * </p>
32 */
33 public final class FromNetASCIIOutputStream extends FilterOutputStream {
34 private boolean lastWasCR;
35
36 /**
37 * Creates a FromNetASCIIOutputStream instance that wraps an existing OutputStream.
38 *
39 * @param output The OutputStream to wrap.
40 */
41 public FromNetASCIIOutputStream(final OutputStream output) {
42 super(output);
43 }
44
45 /**
46 * Closes the stream, writing all pending data.
47 *
48 * @throws IOException If an error occurs while closing the stream.
49 */
50 @Override
51 public synchronized void close() throws IOException {
52 if (FromNetASCIIInputStream.NO_CONVERSION_REQUIRED) {
53 super.close();
54 return;
55 }
56 if (lastWasCR) {
57 out.write('\r');
58 }
59 super.close();
60 }
61
62 /**
63 * Writes a byte array to the stream.
64 *
65 * @param buffer The byte array to write.
66 * @throws IOException If an error occurs while writing to the underlying stream.
67 */
68 @Override
69 public synchronized void write(final byte buffer[]) throws IOException {
70 write(buffer, 0, buffer.length);
71 }
72
73 /**
74 * Writes a number of bytes from a byte array to the stream starting from a given offset.
75 *
76 * @param buffer The byte array to write.
77 * @param offset The offset into the array at which to start copying data.
78 * @param length The number of bytes to write.
79 * @throws IOException If an error occurs while writing to the underlying stream.
80 */
81 @Override
82 public synchronized void write(final byte buffer[], int offset, int length) throws IOException {
83 if (FromNetASCIIInputStream.NO_CONVERSION_REQUIRED) {
84 // FilterOutputStream method is very slow.
85 // super.write(buffer, offset, length);
86 out.write(buffer, offset, length);
87 return;
88 }
89 while (length-- > 0) {
90 writeInt(buffer[offset++]);
91 }
92 }
93
94 /**
95 * Writes a byte to the stream. Note that a call to this method might not actually write a byte to the underlying stream until a subsequent character is
96 * written, from which it can be determined if a NETASCII line separator was encountered. This is transparent to the programmer and is only mentioned for
97 * completeness.
98 *
99 * @param ch The byte to write.
100 * @throws IOException If an error occurs while writing to the underlying stream.
101 */
102 @Override
103 public synchronized void write(final int ch) throws IOException {
104 if (FromNetASCIIInputStream.NO_CONVERSION_REQUIRED) {
105 out.write(ch);
106 return;
107 }
108 writeInt(ch);
109 }
110
111 private void writeInt(final int ch) throws IOException {
112 switch (ch) {
113 case '\r':
114 lastWasCR = true;
115 // Don't write anything. We need to see if next one is linefeed
116 break;
117 case '\n':
118 if (lastWasCR) {
119 out.write(FromNetASCIIInputStream.LINE_SEPARATOR_BYTES);
120 lastWasCR = false;
121 break;
122 }
123 out.write('\n');
124 break;
125 default:
126 if (lastWasCR) {
127 out.write('\r');
128 lastWasCR = false;
129 }
130 out.write(ch);
131 break;
132 }
133 }
134 }