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.miloss.fgsms.common.codec;
19 import java.nio.ByteBuffer;
20 import java.nio.charset.Charset;
21
22 /**
23 * Converts hexadecimal Strings. The charset used for certain operation can be set, the default is set in
24 * {@link #DEFAULT_CHARSET_NAME}
25 *
26 * This class is thread-safe.
27 *
28 * @since 1.1
29 * @version $Id$
30 */
31 public class Hex implements BinaryEncoder, BinaryDecoder {
32
33 /**
34 * Default charset is {@link Charsets#UTF_8}
35 *
36 * @since 1.7
37 */
38 public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
39
40 /**
41 * Default charset name is {@link CharEncoding#UTF_8}
42 *
43 * @since 1.4
44 */
45 public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8;
46
47 /**
48 * Used to build output as Hex
49 */
50 private static final char[] DIGITS_LOWER =
51 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
52
53 /**
54 * Used to build output as Hex
55 */
56 private static final char[] DIGITS_UPPER =
57 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
58
59 /**
60 * Converts a String representing hexadecimal values into an array of bytes of those same values. The
61 * returned array will be half the length of the passed String, as it takes two characters to represent any given
62 * byte. An exception is thrown if the passed String has an odd number of elements.
63 *
64 * @param data
65 * A String containing hexadecimal digits
66 * @return A byte array containing binary data decoded from the supplied char array.
67 * @throws DecoderException
68 * Thrown if an odd number or illegal of characters is supplied
69 * @since 1.11
70 */
71 public static byte[] decodeHex(String data) throws DecoderException {
72 return decodeHex(data.toCharArray());
73 }
74
75 /**
76 * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
77 * returned array will be half the length of the passed array, as it takes two characters to represent any given
78 * byte. An exception is thrown if the passed char array has an odd number of elements.
79 *
80 * @param data
81 * An array of characters containing hexadecimal digits
82 * @return A byte array containing binary data decoded from the supplied char array.
83 * @throws DecoderException
84 * Thrown if an odd number or illegal of characters is supplied
85 */
86 public static byte[] decodeHex(final char[] data) throws DecoderException {
87
88 final int len = data.length;
89
90 if ((len & 0x01) != 0) {
91 throw new DecoderException("Odd number of characters.");
92 }
93
94 final byte[] out = new byte[len >> 1];
95
96 // two characters form the hex value.
97 for (int i = 0, j = 0; j < len; i++) {
98 int f = toDigit(data[j], j) << 4;
99 j++;
100 f = f | toDigit(data[j], j);
101 j++;
102 out[i] = (byte) (f & 0xFF);
103 }
104
105 return out;
106 }
107
108 /**
109 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
110 * The returned array will be double the length of the passed array, as it takes two characters to represent any
111 * given byte.
112 *
113 * @param data
114 * a byte[] to convert to Hex characters
115 * @return A char[] containing lower-case hexadecimal characters
116 */
117 public static char[] encodeHex(final byte[] data) {
118 return encodeHex(data, true);
119 }
120
121 /**
122 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order.
123 * The returned array will be double the length of the passed array, as it takes two characters to represent any
124 * given byte.
125 *
126 * @param data
127 * a byte buffer to convert to Hex characters
128 * @return A char[] containing lower-case hexadecimal characters
129 * @since 1.11
130 */
131 public static char[] encodeHex(final ByteBuffer data) {
132 return encodeHex(data, true);
133 }
134
135 /**
136 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
137 * The returned array will be double the length of the passed array, as it takes two characters to represent any
138 * given byte.
139 *
140 * @param data
141 * a byte[] to convert to Hex characters
142 * @param toLowerCase
143 * <code>true</code> converts to lowercase, <code>false</code> to uppercase
144 * @return A char[] containing hexadecimal characters in the selected case
145 * @since 1.4
146 */
147 public static char[] encodeHex(final byte[] data, final boolean toLowerCase) {
148 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
149 }
150
151 /**
152 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order.
153 * The returned array will be double the length of the passed array, as it takes two characters to represent any
154 * given byte.
155 *
156 * @param data
157 * a byte buffer to convert to Hex characters
158 * @param toLowerCase
159 * <code>true</code> converts to lowercase, <code>false</code> to uppercase
160 * @return A char[] containing hexadecimal characters in the selected case
161 * @since 1.11
162 */
163 public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) {
164 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
165 }
166
167 /**
168 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
169 * The returned array will be double the length of the passed array, as it takes two characters to represent any
170 * given byte.
171 *
172 * @param data
173 * a byte[] to convert to Hex characters
174 * @param toDigits
175 * the output alphabet (must contain at least 16 chars)
176 * @return A char[] containing the appropriate characters from the alphabet
177 * For best results, this should be either upper- or lower-case hex.
178 * @since 1.4
179 */
180 protected static char[] encodeHex(final byte[] data, final char[] toDigits) {
181 final int l = data.length;
182 final char[] out = new char[l << 1];
183 // two characters form the hex value.
184 for (int i = 0, j = 0; i < l; i++) {
185 out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
186 out[j++] = toDigits[0x0F & data[i]];
187 }
188 return out;
189 }
190
191 /**
192 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order.
193 * The returned array will be double the length of the passed array, as it takes two characters to represent any
194 * given byte.
195 *
196 * @param data
197 * a byte buffer to convert to Hex characters
198 * @param toDigits
199 * the output alphabet (must be at least 16 characters)
200 * @return A char[] containing the appropriate characters from the alphabet
201 * For best results, this should be either upper- or lower-case hex.
202 * @since 1.11
203 */
204 protected static char[] encodeHex(final ByteBuffer data, final char[] toDigits) {
205 return encodeHex(data.array(), toDigits);
206 }
207
208 /**
209 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
210 * String will be double the length of the passed array, as it takes two characters to represent any given byte.
211 *
212 * @param data
213 * a byte[] to convert to Hex characters
214 * @return A String containing lower-case hexadecimal characters
215 * @since 1.4
216 */
217 public static String encodeHexString(final byte[] data) {
218 return new String(encodeHex(data));
219 }
220
221 /**
222 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
223 * String will be double the length of the passed array, as it takes two characters to represent any given byte.
224 *
225 * @param data
226 * a byte[] to convert to Hex characters
227 * @param toLowerCase
228 * <code>true</code> converts to lowercase, <code>false</code> to uppercase
229 * @return A String containing lower-case hexadecimal characters
230 * @since 1.11
231 */
232 public static String encodeHexString(final byte[] data, boolean toLowerCase) {
233 return new String(encodeHex(data, toLowerCase));
234 }
235
236 /**
237 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned
238 * String will be double the length of the passed array, as it takes two characters to represent any given byte.
239 *
240 * @param data
241 * a byte buffer to convert to Hex characters
242 * @return A String containing lower-case hexadecimal characters
243 * @since 1.11
244 */
245 public static String encodeHexString(final ByteBuffer data) {
246 return new String(encodeHex(data));
247 }
248
249 /**
250 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned
251 * String will be double the length of the passed array, as it takes two characters to represent any given byte.
252 *
253 * @param data
254 * a byte buffer to convert to Hex characters
255 * @param toLowerCase
256 * <code>true</code> converts to lowercase, <code>false</code> to uppercase
257 * @return A String containing lower-case hexadecimal characters
258 * @since 1.11
259 */
260 public static String encodeHexString(final ByteBuffer data, boolean toLowerCase) {
261 return new String(encodeHex(data, toLowerCase));
262 }
263
264 /**
265 * Converts a hexadecimal character to an integer.
266 *
267 * @param ch
268 * A character to convert to an integer digit
269 * @param index
270 * The index of the character in the source
271 * @return An integer
272 * @throws DecoderException
273 * Thrown if ch is an illegal hex character
274 */
275 protected static int toDigit(final char ch, final int index) throws DecoderException {
276 final int digit = Character.digit(ch, 16);
277 if (digit == -1) {
278 throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index);
279 }
280 return digit;
281 }
282
283 private final Charset charset;
284
285 /**
286 * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET}
287 */
288 public Hex() {
289 // use default encoding
290 this.charset = DEFAULT_CHARSET;
291 }
292
293 /**
294 * Creates a new codec with the given Charset.
295 *
296 * @param charset
297 * the charset.
298 * @since 1.7
299 */
300 public Hex(final Charset charset) {
301 this.charset = charset;
302 }
303
304 /**
305 * Creates a new codec with the given charset name.
306 *
307 * @param charsetName
308 * the charset name.
309 * @throws java.nio.charset.UnsupportedCharsetException
310 * If the named charset is unavailable
311 * @since 1.4
312 * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable
313 */
314 public Hex(final String charsetName) {
315 this(Charset.forName(charsetName));
316 }
317
318 /**
319 * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values.
320 * The returned array will be half the length of the passed array, as it takes two characters to represent any given
321 * byte. An exception is thrown if the passed char array has an odd number of elements.
322 *
323 * @param array
324 * An array of character bytes containing hexadecimal digits
325 * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
326 * @throws DecoderException
327 * Thrown if an odd number of characters is supplied to this function
328 * @see #decodeHex(char[])
329 */
330 @Override
331 public byte[] decode(final byte[] array) throws DecoderException {
332 return decodeHex(new String(array, getCharset()).toCharArray());
333 }
334
335 /**
336 * Converts a buffer of character bytes representing hexadecimal values into an array of bytes of those same values.
337 * The returned array will be half the length of the passed array, as it takes two characters to represent any given
338 * byte. An exception is thrown if the passed char array has an odd number of elements.
339 *
340 * @param buffer
341 * An array of character bytes containing hexadecimal digits
342 * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
343 * @throws DecoderException
344 * Thrown if an odd number of characters is supplied to this function
345 * @see #decodeHex(char[])
346 * @since 1.11
347 */
348 public byte[] decode(final ByteBuffer buffer) throws DecoderException {
349 return decodeHex(new String(buffer.array(), getCharset()).toCharArray());
350 }
351
352 /**
353 * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those
354 * same values. The returned array will be half the length of the passed String or array, as it takes two characters
355 * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements.
356 *
357 * @param object
358 * A String, ByteBuffer, byte[], or an array of character bytes containing hexadecimal digits
359 * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
360 * @throws DecoderException
361 * Thrown if an odd number of characters is supplied to this function or the object is not a String or
362 * char[]
363 * @see #decodeHex(char[])
364 */
365 @Override
366 public Object decode(final Object object) throws DecoderException {
367 if (object instanceof String) {
368 return decode(((String) object).toCharArray());
369 } else if (object instanceof byte[]) {
370 return decode((byte[]) object);
371 } else if (object instanceof ByteBuffer) {
372 return decode((ByteBuffer) object);
373 } else {
374 try {
375 return decodeHex((char[]) object);
376 } catch (final ClassCastException e) {
377 throw new DecoderException(e.getMessage(), e);
378 }
379 }
380 }
381
382 /**
383 * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each
384 * byte in order. The returned array will be double the length of the passed array, as it takes two characters to
385 * represent any given byte.
386 * <p>
387 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by
388 * {@link #getCharset()}.
389 * </p>
390 *
391 * @param array
392 * a byte[] to convert to Hex characters
393 * @return A byte[] containing the bytes of the lower-case hexadecimal characters
394 * @since 1.7 No longer throws IllegalStateException if the charsetName is invalid.
395 * @see #encodeHex(byte[])
396 */
397 @Override
398 public byte[] encode(final byte[] array) {
399 return encodeHexString(array).getBytes(this.getCharset());
400 }
401
402 /**
403 * Converts byte buffer into an array of bytes for the characters representing the hexadecimal values of each
404 * byte in order. The returned array will be double the length of the passed array, as it takes two characters to
405 * represent any given byte.
406 * <p>
407 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by
408 * {@link #getCharset()}.
409 * </p>
410 *
411 * @param array
412 * a byte buffer to convert to Hex characters
413 * @return A byte[] containing the bytes of the lower-case hexadecimal characters
414 * @see #encodeHex(byte[])
415 * @since 1.11
416 */
417 public byte[] encode(final ByteBuffer array) {
418 return encodeHexString(array).getBytes(this.getCharset());
419 }
420
421 /**
422 * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each
423 * byte in order. The returned array will be double the length of the passed String or array, as it takes two
424 * characters to represent any given byte.
425 * <p>
426 * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by
427 * {@link #getCharset()}.
428 * </p>
429 *
430 * @param object
431 * a String, ByteBuffer, or byte[] to convert to Hex characters
432 * @return A char[] containing lower-case hexadecimal characters
433 * @throws EncoderException
434 * Thrown if the given object is not a String or byte[]
435 * @see #encodeHex(byte[])
436 */
437 @Override
438 public Object encode(final Object object) throws EncoderException {
439 byte[] byteArray;
440 if (object instanceof String) {
441 byteArray = ((String) object).getBytes(this.getCharset());
442 } else if (object instanceof ByteBuffer) {
443 byteArray = ((ByteBuffer) object).array();
444 } else {
445 try {
446 byteArray = (byte[]) object;
447 } catch (final ClassCastException e) {
448 throw new EncoderException(e.getMessage(), e);
449 }
450 }
451 return encodeHex(byteArray);
452 }
453
454 /**
455 * Gets the charset.
456 *
457 * @return the charset.
458 * @since 1.7
459 */
460 public Charset getCharset() {
461 return this.charset;
462 }
463
464 /**
465 * Gets the charset name.
466 *
467 * @return the charset name.
468 * @since 1.4
469 */
470 public String getCharsetName() {
471 return this.charset.name();
472 }
473
474 /**
475 * Returns a string representation of the object, which includes the charset name.
476 *
477 * @return a string representation of the object.
478 */
479 @Override
480 public String toString() {
481 return super.toString() + "[charsetName=" + this.charset + "]";
482 }
483 }