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 }