1 /*
2 * Copyright 2007 Werner Guttmann
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.castor.core.util;
17
18 import java.io.IOException;
19 import java.io.OutputStream;
20 import java.io.ByteArrayOutputStream;
21
22 /**
23 * Hex encoder/decoder implementation (borrowed from BouncyCastle=.
24 *
25 * @author Johan Lindquist
26 * @since 1.1.1
27 * @version $Revision$
28 */
29 public final class HexDecoder {
30
31 /**
32 * Identifies the data type supported by this decoder.
33 */
34 public static final String DATA_TYPE = "hexBinary";
35
36 /**
37 * Initial size of the decoding table.
38 */
39 private static final int DECODING_TABLE_SIZE = 128;
40
41 /**
42 * Encoding table.
43 */
44 protected static final byte[] ENCODING_TABLE = {
45 (byte) '0', (byte) '1', (byte) '2', (byte) '3',
46 (byte) '4', (byte) '5', (byte) '6', (byte) '7',
47 (byte) '8', (byte) '9', (byte) 'A', (byte) 'B',
48 (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F'
49 };
50
51 /**
52 * Decoding table.
53 */
54 protected static final byte[] DECODING_TABLE = new byte[DECODING_TABLE_SIZE];
55
56 /**
57 * Initialize the decoding table.
58 */
59 protected static void initialiseDecodingTable() {
60 for (int i = 0; i < ENCODING_TABLE.length; i++) {
61 DECODING_TABLE[ENCODING_TABLE[i]] = (byte) i;
62 }
63
64 // deal with lower case letters as well
65 DECODING_TABLE['a'] = DECODING_TABLE['A'];
66 DECODING_TABLE['b'] = DECODING_TABLE['B'];
67 DECODING_TABLE['c'] = DECODING_TABLE['C'];
68 DECODING_TABLE['d'] = DECODING_TABLE['D'];
69 DECODING_TABLE['e'] = DECODING_TABLE['E'];
70 DECODING_TABLE['f'] = DECODING_TABLE['F'];
71 }
72
73 static {
74 initialiseDecodingTable();
75 }
76
77 /**
78 * Creates an instance of this class.
79 */
80 private HexDecoder() {
81 // Nothing to do ...
82 }
83
84 /**
85 * Encodes the input data producing a Hex output stream.
86 * @param data The input data to be HEX encoded
87 * @param off Initiak offset
88 * @param length Initial length of the input data array
89 * @param out The {@link OutputStream} instance holding the encoded input data.
90 * @return the number of bytes produced.
91 * @throws IOException If encoding fails.
92 */
93 public static int encode(final byte[] data, final int off, final int length,
94 final OutputStream out) throws IOException {
95 for (int i = off; i < (off + length); i++) {
96 int v = data[i] & 0xff;
97
98 out.write(ENCODING_TABLE[(v >>> 4)]);
99 out.write(ENCODING_TABLE[v & 0xf]);
100 }
101
102 return length * 2;
103 }
104
105 /**
106 * Indicates whether a given character should be ignored during en-/decoding.
107 * @param c The character at question.
108 * @return True if the given character should be ignored.
109 */
110 private static boolean ignore(final char c) {
111 return (c == '\n' || c == '\r' || c == '\t' || c == ' ');
112 }
113
114 /**
115 * Decodes the Hex encoded byte data writing it to the given output stream,
116 * whitespace characters will be ignored.
117 * @param data The data to be encoded
118 * @param off Initial offset.
119 * @param length Initial length
120 * @param out The {@link OutputStream} instance
121 * @return the number of bytes produced.
122 * @throws IOException If encoding failed.
123 */
124 public static int decode(final byte[] data, final int off, final int length,
125 final OutputStream out) throws IOException {
126 byte b1, b2;
127 int outLen = 0;
128
129 int end = off + length;
130
131 while (end > off) {
132 if (!ignore((char) data[end - 1])) {
133 break;
134 }
135
136 end--;
137 }
138
139 int i = off;
140 while (i < end) {
141 while (i < end && ignore((char) data[i])) {
142 i++;
143 }
144
145 b1 = DECODING_TABLE[data[i++]];
146
147 while (i < end && ignore((char) data[i])) {
148 i++;
149 }
150
151 b2 = DECODING_TABLE[data[i++]];
152
153 out.write((b1 << 4) | b2);
154
155 outLen++;
156 }
157
158 return outLen;
159 }
160
161 /**
162 * Decodes the Hex encoded String data writing it to the given output stream,
163 * whitespace characters will be ignored.
164 *
165 * @param data The data to be encoded
166 * @param out The {@link OutputStream} instance
167 * @return the number of bytes produced.
168 * @throws IOException If encoding failed.
169 */
170 public static int decode(final String data, final OutputStream out) throws IOException {
171 byte b1, b2;
172 int length = 0;
173
174 int end = data.length();
175
176 while (end > 0) {
177 if (!ignore(data.charAt(end - 1))) {
178 break;
179 }
180
181 end--;
182 }
183
184 int i = 0;
185 while (i < end) {
186 while (i < end && ignore(data.charAt(i))) {
187 i++;
188 }
189
190 b1 = DECODING_TABLE[data.charAt(i++)];
191
192 while (i < end && ignore(data.charAt(i))) {
193 i++;
194 }
195
196 b2 = DECODING_TABLE[data.charAt(i++)];
197
198 out.write((b1 << 4) | b2);
199
200 length++;
201 }
202
203 return length;
204 }
205
206 /**
207 * Encodes the input data producing a Hex output stream.
208 * @param data Input data to encode.
209 * @return the number of bytes produced.
210 */
211 public static String encode(final byte[] data) {
212 try {
213 final ByteArrayOutputStream out = new ByteArrayOutputStream();
214 encode(data, 0, data.length, out);
215 out.close();
216 return new String(out.toByteArray());
217 } catch (IOException e) {
218 e.printStackTrace();
219 throw new RuntimeException(e.getMessage(), e);
220 }
221 }
222
223 /**
224 * Decodes the HEX input data producing a output stream.
225 * @param data Input data to be decoded.
226 * @return A byte array representing the decoded input data.
227 */
228 public static byte[] decode(final String data) {
229 try {
230 final ByteArrayOutputStream out = new ByteArrayOutputStream();
231 decode(data, out);
232 out.close();
233 return out.toByteArray();
234 } catch (IOException e) {
235 e.printStackTrace();
236 throw new RuntimeException(e.getMessage(), e);
237 }
238 }
239
240 }