001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.codec.binary; 019 020 import java.io.UnsupportedEncodingException; 021 022 import org.apache.commons.codec.BinaryDecoder; 023 import org.apache.commons.codec.BinaryEncoder; 024 import org.apache.commons.codec.CharEncoding; 025 import org.apache.commons.codec.DecoderException; 026 import org.apache.commons.codec.EncoderException; 027 028 /** 029 * Converts hexadecimal Strings. The charset used for certain operation can be set, the default is set in 030 * {@link #DEFAULT_CHARSET_NAME} 031 * 032 * @since 1.1 033 * @author Apache Software Foundation 034 * @version $Id: Hex.java 1080701 2011-03-11 17:52:27Z ggregory $ 035 */ 036 public class Hex implements BinaryEncoder, BinaryDecoder { 037 038 /** 039 * Default charset name is {@link CharEncoding#UTF_8} 040 * 041 * @since 1.4 042 */ 043 public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8; 044 045 /** 046 * Used to build output as Hex 047 */ 048 private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 049 050 /** 051 * Used to build output as Hex 052 */ 053 private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 054 055 /** 056 * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The 057 * returned array will be half the length of the passed array, as it takes two characters to represent any given 058 * byte. An exception is thrown if the passed char array has an odd number of elements. 059 * 060 * @param data 061 * An array of characters containing hexadecimal digits 062 * @return A byte array containing binary data decoded from the supplied char array. 063 * @throws DecoderException 064 * Thrown if an odd number or illegal of characters is supplied 065 */ 066 public static byte[] decodeHex(char[] data) throws DecoderException { 067 068 int len = data.length; 069 070 if ((len & 0x01) != 0) { 071 throw new DecoderException("Odd number of characters."); 072 } 073 074 byte[] out = new byte[len >> 1]; 075 076 // two characters form the hex value. 077 for (int i = 0, j = 0; j < len; i++) { 078 int f = toDigit(data[j], j) << 4; 079 j++; 080 f = f | toDigit(data[j], j); 081 j++; 082 out[i] = (byte) (f & 0xFF); 083 } 084 085 return out; 086 } 087 088 /** 089 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 090 * The returned array will be double the length of the passed array, as it takes two characters to represent any 091 * given byte. 092 * 093 * @param data 094 * a byte[] to convert to Hex characters 095 * @return A char[] containing hexadecimal characters 096 */ 097 public static char[] encodeHex(byte[] data) { 098 return encodeHex(data, true); 099 } 100 101 /** 102 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 103 * The returned array will be double the length of the passed array, as it takes two characters to represent any 104 * given byte. 105 * 106 * @param data 107 * a byte[] to convert to Hex characters 108 * @param toLowerCase 109 * <code>true</code> converts to lowercase, <code>false</code> to uppercase 110 * @return A char[] containing hexadecimal characters 111 * @since 1.4 112 */ 113 public static char[] encodeHex(byte[] data, boolean toLowerCase) { 114 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 115 } 116 117 /** 118 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 119 * The returned array will be double the length of the passed array, as it takes two characters to represent any 120 * given byte. 121 * 122 * @param data 123 * a byte[] to convert to Hex characters 124 * @param toDigits 125 * the output alphabet 126 * @return A char[] containing hexadecimal characters 127 * @since 1.4 128 */ 129 protected static char[] encodeHex(byte[] data, char[] toDigits) { 130 int l = data.length; 131 char[] out = new char[l << 1]; 132 // two characters form the hex value. 133 for (int i = 0, j = 0; i < l; i++) { 134 out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; 135 out[j++] = toDigits[0x0F & data[i]]; 136 } 137 return out; 138 } 139 140 /** 141 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned 142 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 143 * 144 * @param data 145 * a byte[] to convert to Hex characters 146 * @return A String containing hexadecimal characters 147 * @since 1.4 148 */ 149 public static String encodeHexString(byte[] data) { 150 return new String(encodeHex(data)); 151 } 152 153 /** 154 * Converts a hexadecimal character to an integer. 155 * 156 * @param ch 157 * A character to convert to an integer digit 158 * @param index 159 * The index of the character in the source 160 * @return An integer 161 * @throws DecoderException 162 * Thrown if ch is an illegal hex character 163 */ 164 protected static int toDigit(char ch, int index) throws DecoderException { 165 int digit = Character.digit(ch, 16); 166 if (digit == -1) { 167 throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index); 168 } 169 return digit; 170 } 171 172 private final String charsetName; 173 174 /** 175 * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET_NAME} 176 */ 177 public Hex() { 178 // use default encoding 179 this.charsetName = DEFAULT_CHARSET_NAME; 180 } 181 182 /** 183 * Creates a new codec with the given charset name. 184 * 185 * @param csName 186 * the charset name. 187 * @since 1.4 188 */ 189 public Hex(String csName) { 190 this.charsetName = csName; 191 } 192 193 /** 194 * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values. 195 * The returned array will be half the length of the passed array, as it takes two characters to represent any given 196 * byte. An exception is thrown if the passed char array has an odd number of elements. 197 * 198 * @param array 199 * An array of character bytes containing hexadecimal digits 200 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 201 * @throws DecoderException 202 * Thrown if an odd number of characters is supplied to this function 203 * @see #decodeHex(char[]) 204 */ 205 public byte[] decode(byte[] array) throws DecoderException { 206 try { 207 return decodeHex(new String(array, getCharsetName()).toCharArray()); 208 } catch (UnsupportedEncodingException e) { 209 throw new DecoderException(e.getMessage(), e); 210 } 211 } 212 213 /** 214 * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those 215 * same values. The returned array will be half the length of the passed String or array, as it takes two characters 216 * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements. 217 * 218 * @param object 219 * A String or, an array of character bytes containing hexadecimal digits 220 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 221 * @throws DecoderException 222 * Thrown if an odd number of characters is supplied to this function or the object is not a String or 223 * char[] 224 * @see #decodeHex(char[]) 225 */ 226 public Object decode(Object object) throws DecoderException { 227 try { 228 char[] charArray = object instanceof String ? ((String) object).toCharArray() : (char[]) object; 229 return decodeHex(charArray); 230 } catch (ClassCastException e) { 231 throw new DecoderException(e.getMessage(), e); 232 } 233 } 234 235 /** 236 * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each 237 * byte in order. The returned array will be double the length of the passed array, as it takes two characters to 238 * represent any given byte. 239 * <p> 240 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by 241 * {@link #getCharsetName()}. 242 * </p> 243 * 244 * @param array 245 * a byte[] to convert to Hex characters 246 * @return A byte[] containing the bytes of the hexadecimal characters 247 * @throws IllegalStateException 248 * if the charsetName is invalid. This API throws {@link IllegalStateException} instead of 249 * {@link UnsupportedEncodingException} for backward compatibility. 250 * @see #encodeHex(byte[]) 251 */ 252 public byte[] encode(byte[] array) { 253 return StringUtils.getBytesUnchecked(encodeHexString(array), getCharsetName()); 254 } 255 256 /** 257 * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each 258 * byte in order. The returned array will be double the length of the passed String or array, as it takes two 259 * characters to represent any given byte. 260 * <p> 261 * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by 262 * {@link #getCharsetName()}. 263 * </p> 264 * 265 * @param object 266 * a String, or byte[] to convert to Hex characters 267 * @return A char[] containing hexadecimal characters 268 * @throws EncoderException 269 * Thrown if the given object is not a String or byte[] 270 * @see #encodeHex(byte[]) 271 */ 272 public Object encode(Object object) throws EncoderException { 273 try { 274 byte[] byteArray = object instanceof String ? ((String) object).getBytes(getCharsetName()) : (byte[]) object; 275 return encodeHex(byteArray); 276 } catch (ClassCastException e) { 277 throw new EncoderException(e.getMessage(), e); 278 } catch (UnsupportedEncodingException e) { 279 throw new EncoderException(e.getMessage(), e); 280 } 281 } 282 283 /** 284 * Gets the charset name. 285 * 286 * @return the charset name. 287 * @since 1.4 288 */ 289 public String getCharsetName() { 290 return this.charsetName; 291 } 292 293 /** 294 * Returns a string representation of the object, which includes the charset name. 295 * 296 * @return a string representation of the object. 297 */ 298 public String toString() { 299 return super.toString() + "[charsetName=" + this.charsetName + "]"; 300 } 301 }