1 /*
2 * Copyright (c) 2003
3 * Information Desire GmbH
4 * All rights reserved.
5 */
6 package com.infodesire.infobit.external.impl;
7
8 import org.apache.commons.logging.Log;
9 import org.apache.commons.logging.LogFactory;
10
11 import java.io.IOException;
12 import java.io.OutputStream;
13
14 /***
15 * Decodes binary data encoded as base64.
16 *
17 * @author peter2
18 * @created 1. September 2003
19 * @version $Revision: 1.1 $
20 */
21 class Base64Decoder implements Decoder {
22
23 private final static Log _log = LogFactory.getLog(Base64Decoder.class);
24
25 /***
26 * The destination where to write the decoded data to
27 */
28 private OutputStream _destination;
29
30 /***
31 * The text chunk currently being decoded
32 */
33 private char[] _textChunk = new char[24 / 6];
34
35 /***
36 * The data chunk currently being decoded
37 */
38 private byte[] _dataChunk = new byte[24 / 8];
39
40 /***
41 * Number of unprocessed characters hold in the {@link _textChunk input
42 * buffer}
43 */
44 private int _textLength;
45
46 /***
47 * Number of bytes to decode
48 */
49 private int _contentLength;
50
51 /***
52 * Number of decoded bytes
53 */
54 private int _writtenLength;
55
56 /***
57 * Inconsistent encoding data
58 */
59 private boolean _error;
60
61 /***
62 * A description of the first decoding error encountered
63 */
64 private String _errorText = "OK";
65
66
67 /***
68 * Gets the WrittenLength attribute of the Base64Decoder object
69 *
70 * @return The WrittenLength value
71 */
72 public int getWrittenLength() {
73 return _writtenLength;
74 }
75
76
77 /***
78 * Gets the ErrorText attribute of the Base64Decoder object
79 *
80 * @return The ErrorText value
81 */
82 public String getErrorText() {
83 return _errorText;
84 }
85
86
87 /***
88 * Sets the Destination attribute of the Base64Decoder object
89 *
90 * @param dest The new Destination value
91 */
92 public void setDestination(OutputStream dest) {
93 _destination = dest;
94 }
95
96
97 /***
98 * Defines the number of bytes to process.
99 *
100 * @param contentLength The new ContentLength value
101 */
102 public void setContentLength(int contentLength) {
103 _contentLength = contentLength;
104 }
105
106
107 /***
108 * DOCUMENT METHOD
109 *
110 * @return Description of the Returned Value
111 */
112 public boolean hasError() {
113 return _error;
114 }
115
116
117 /***
118 * DOCUMENT METHOD
119 *
120 * @param b Description of Parameter
121 * @param offset Description of Parameter
122 * @param length Description of Parameter
123 * @exception IOException Description of Exception
124 */
125 public void decodeChunk(char[] b, int offset, int length)
126 throws IOException {
127
128 int n = 0;
129
130 while (!_error &&
131 (n = fill(b, offset, length)) > -1
132 && _textLength == _textChunk.length) {
133
134 offset += n;
135 length -= n;
136
137 for (int i = 0; !_error && i < _textChunk.length; ++i) {
138
139 int j = 6 * i / 8;
140 int k = 6 * i % 8;
141 int d = decode(_textChunk[i]) << k;
142 int low = (d & 0xff);
143 int high = (d >>> 8);
144 int maskLow = (0x3f << k) & 0xff;
145 int maskHigh = 0x3f << k >>> 8;
146 _dataChunk[j] = (byte) (_dataChunk[j] & ~maskLow | low);
147
148 if (j + 1 < _dataChunk.length) {
149 _dataChunk[j + 1] =
150 (byte) (_dataChunk[j + 1] & ~maskHigh | high);
151 }
152 }
153
154 _textLength = 0;
155 int l = Math.min(_contentLength - _writtenLength,
156 _dataChunk.length);
157 _destination.write(_dataChunk, 0, l);
158 _writtenLength += l;
159 }
160 }
161
162
163 /***
164 * DOCUMENT METHOD
165 */
166 public void endOfData() {
167 if (_writtenLength < _contentLength) {
168 recordError("short data: " + (_contentLength - _writtenLength) +
169 " Bytes");
170 }
171 }
172
173
174 /***
175 * Marks an error. Does not override previously set errors.
176 *
177 * @param desc Brief error description
178 */
179 private void recordError(String desc) {
180 if (!_error) {
181 _errorText = desc;
182 _error = true;
183 }
184 }
185
186
187 /***
188 * Fills the {@link _textChunk input buffer} from the specified source. Test
189 * for overflow. Does not accept data on error.
190 *
191 * @param b Holds the data from which to fill the input buffer
192 * @param offset Index of the first element from which to fill the input
193 * buffer
194 * @param length Number of contiguous characters from which to fill the
195 * input buffer.
196 * @return The number of characters consumed from <code>b</code>, or
197 * -1 if an error occured
198 */
199 private int fill(char[] b, int offset, int length) {
200 int n = 0;
201 boolean checkOverflow = _writtenLength == _contentLength;
202
203 while (!_error && _textLength < _textChunk.length && n < length) {
204 char c = b[offset + n++];
205
206 if (!Character.isWhitespace(c)) {
207 if (checkOverflow) {
208 recordError("Input too large");
209 // _error is true
210 }
211
212 _textChunk[_textLength++] = c;
213 }
214 }
215
216 return _error ? -1 : n;
217 }
218
219
220 /***
221 * Decodes a character of the base64 alphabet.
222 *
223 * @param c The base64 character to decode
224 * @return The value mapping to <code>c</code>, or -1 for error
225 */
226 private int decode(char c) {
227 int d = -1;
228
229 if ('A' <= c && c <= 'Z') {
230 d = c - 'A';
231 }
232 else if ('a' <= c && c <= 'z') {
233 d = c - 'a' + 26;
234 }
235 else if ('0' <= c && c <= '9') {
236 d = c - '0' + 52;
237 }
238 else if (c == '+') {
239 d = 62;
240 }
241 else if (c == '/') {
242 d = 63;
243 }
244 else if (c == '=') {
245 d = 0;
246 }
247 else {
248 recordError("Illegal input data: 0x" + Integer.toHexString(c));
249 }
250
251 return d;
252 }
253
254 }
This page was automatically generated by Maven