|
|||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
UUEncoderStream.java | 50% | 84.4% | 69.2% | 75.7% |
|
1 | /** | |
2 | * | |
3 | * Copyright 2003-2004 The Apache Software Foundation | |
4 | * | |
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | * you may not use this file except in compliance with the License. | |
7 | * 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.geronimo.mail.util; | |
19 | ||
20 | import java.io.FilterOutputStream; | |
21 | import java.io.IOException; | |
22 | import java.io.OutputStream; | |
23 | import java.io.PrintStream; | |
24 | ||
25 | /** | |
26 | * An implementation of a FilterOutputStream that encodes the | |
27 | * stream data in UUencoding format. This version does the | |
28 | * encoding "on the fly" rather than encoding a single block of | |
29 | * data. Since this version is intended for use by the MimeUtilty class, | |
30 | * it also handles line breaks in the encoded data. | |
31 | */ | |
32 | public class UUEncoderStream extends FilterOutputStream { | |
33 | ||
34 | // default values included on the "begin" prefix of the data stream. | |
35 | protected static final int DEFAULT_MODE = 644; | |
36 | protected static final String DEFAULT_NAME = "encoder.buf"; | |
37 | ||
38 | protected static final int MAX_CHARS_PER_LINE = 45; | |
39 | ||
40 | // the configured name on the "begin" command. | |
41 | protected String name; | |
42 | // the configured mode for the "begin" command. | |
43 | protected int mode; | |
44 | ||
45 | // since this is a filtering stream, we need to wait until we have the first byte written for encoding | |
46 | // to write out the "begin" marker. A real pain, but necessary. | |
47 | protected boolean beginWritten = false; | |
48 | ||
49 | ||
50 | // our encoder utility class. | |
51 | protected UUEncoder encoder = new UUEncoder(); | |
52 | ||
53 | // Data is generally written out in 45 character lines, so we're going to buffer that amount before | |
54 | // asking the encoder to process this. | |
55 | ||
56 | // the buffered byte count | |
57 | protected int bufferedBytes = 0; | |
58 | ||
59 | // we'll encode this part once it is filled up. | |
60 | protected byte[] buffer = new byte[45]; | |
61 | ||
62 | /** | |
63 | * Create a Base64 encoder stream that wraps a specifed stream | |
64 | * using the default line break size. | |
65 | * | |
66 | * @param out The wrapped output stream. | |
67 | */ | |
68 | 5 | public UUEncoderStream(OutputStream out) { |
69 | 5 | this(out, DEFAULT_NAME, DEFAULT_MODE); |
70 | } | |
71 | ||
72 | ||
73 | /** | |
74 | * Create a Base64 encoder stream that wraps a specifed stream | |
75 | * using the default line break size. | |
76 | * | |
77 | * @param out The wrapped output stream. | |
78 | * @param name The filename placed on the "begin" command. | |
79 | */ | |
80 | 0 | public UUEncoderStream(OutputStream out, String name) { |
81 | 0 | this(out, name, DEFAULT_MODE); |
82 | } | |
83 | ||
84 | ||
85 | 5 | public UUEncoderStream(OutputStream out, String name, int mode) { |
86 | 5 | super(out); |
87 | // fill in the name and mode information. | |
88 | 5 | this.name = name; |
89 | 5 | this.mode = mode; |
90 | } | |
91 | ||
92 | ||
93 | 10 | private void checkBegin() throws IOException { |
94 | 10 | if (!beginWritten) { |
95 | // grumble...OutputStream doesn't directly support writing String data. We'll wrap this in | |
96 | // a PrintStream() to accomplish the task of writing the begin command. | |
97 | ||
98 | 5 | PrintStream writer = new PrintStream(out); |
99 | // write out the stream with a CRLF marker | |
100 | 5 | writer.print("begin " + mode + " " + name + "\r\n"); |
101 | 5 | writer.flush(); |
102 | 5 | beginWritten = true; |
103 | } | |
104 | } | |
105 | ||
106 | 5 | private void writeEnd() throws IOException { |
107 | 5 | PrintStream writer = new PrintStream(out); |
108 | // write out the stream with a CRLF marker | |
109 | 5 | writer.print("\nend\r\n"); |
110 | 5 | writer.flush(); |
111 | } | |
112 | ||
113 | 10 | private void flushBuffer() throws IOException { |
114 | // make sure we've written the begin marker first | |
115 | 10 | checkBegin(); |
116 | // ask the encoder to encode and write this out. | |
117 | 10 | if (bufferedBytes != 0) { |
118 | 10 | encoder.encode(buffer, 0, bufferedBytes, out); |
119 | // reset the buffer count | |
120 | 10 | bufferedBytes = 0; |
121 | } | |
122 | } | |
123 | ||
124 | 5 | private int bufferSpace() { |
125 | 5 | return MAX_CHARS_PER_LINE - bufferedBytes; |
126 | } | |
127 | ||
128 | 5 | private boolean isBufferFull() { |
129 | 5 | return bufferedBytes >= MAX_CHARS_PER_LINE; |
130 | } | |
131 | ||
132 | ||
133 | // in order for this to work, we need to override the 3 different signatures for write | |
134 | ||
135 | 0 | public void write(int ch) throws IOException { |
136 | // store this in the buffer. | |
137 | 0 | buffer[bufferedBytes++] = (byte)ch; |
138 | ||
139 | // if we filled this up, time to encode and write to the output stream. | |
140 | 0 | if (isBufferFull()) { |
141 | 0 | flushBuffer(); |
142 | } | |
143 | } | |
144 | ||
145 | 0 | public void write(byte [] data) throws IOException { |
146 | 0 | write(data, 0, data.length); |
147 | } | |
148 | ||
149 | 5 | public void write(byte [] data, int offset, int length) throws IOException { |
150 | // first check to see how much space we have left in the buffer, and copy that over | |
151 | 5 | int copyBytes = Math.min(bufferSpace(), length); |
152 | ||
153 | 5 | System.arraycopy(buffer, bufferedBytes, data, offset, copyBytes); |
154 | 5 | bufferedBytes += copyBytes; |
155 | 5 | offset += copyBytes; |
156 | 5 | length -= copyBytes; |
157 | ||
158 | // if we filled this up, time to encode and write to the output stream. | |
159 | 5 | if (isBufferFull()) { |
160 | 5 | flushBuffer(); |
161 | } | |
162 | ||
163 | // we've flushed the leading part up to the line break. Now if we have complete lines | |
164 | // of data left, we can have the encoder process all of these lines directly. | |
165 | 5 | if (length >= MAX_CHARS_PER_LINE) { |
166 | 5 | int fullLinesLength = (length / MAX_CHARS_PER_LINE) * MAX_CHARS_PER_LINE; |
167 | // ask the encoder to encode and write this out. | |
168 | 5 | encoder.encode(data, offset, fullLinesLength, out); |
169 | 5 | offset += fullLinesLength; |
170 | 5 | length -= fullLinesLength; |
171 | } | |
172 | ||
173 | // ok, now we're down to a potential trailing bit we need to move into the | |
174 | // buffer for later processing. | |
175 | ||
176 | 5 | if (length > 0) { |
177 | 5 | System.arraycopy(buffer, 0, data, offset, length); |
178 | 5 | bufferedBytes += length; |
179 | 5 | offset += length; |
180 | 5 | length -= length; |
181 | } | |
182 | } | |
183 | ||
184 | 5 | public void flush() throws IOException { |
185 | // flush any unencoded characters we're holding. | |
186 | 5 | flushBuffer(); |
187 | // write out the data end marker | |
188 | 5 | writeEnd(); |
189 | // and flush the output stream too so that this data is available. | |
190 | 5 | out.flush(); |
191 | } | |
192 | ||
193 | 0 | public void close() throws IOException { |
194 | // flush all of the streams and close the target output stream. | |
195 | 0 | flush(); |
196 | 0 | out.close(); |
197 | } | |
198 | ||
199 | } | |
200 | ||
201 |
|