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 * Hex encoder and decoder. 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 801639 2009-08-06 13:15:10Z niallp $ 035 */ 036 public class Hex implements BinaryEncoder, BinaryDecoder { 037 038 /** 039 * Default charset name is {@link CharEncoding#UTF_8} 040 */ 041 public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8; 042 043 /** 044 * Used to build output as Hex 045 */ 046 private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 047 048 /** 049 * Used to build output as Hex 050 */ 051 private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 052 053 /** 054 * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The 055 * returned array will be half the length of the passed array, as it takes two characters to represent any given 056 * byte. An exception is thrown if the passed char array has an odd number of elements. 057 * 058 * @param data 059 * An array of characters containing hexadecimal digits 060 * @return A byte array containing binary data decoded from the supplied char array. 061 * @throws DecoderException 062 * Thrown if an odd number or illegal of characters is supplied 063 */ 064 public static byte[] decodeHex(char[] data) throws DecoderException { 065 066 int len = data.length; 067 068 if ((len & 0x01) != 0) { 069 throw new DecoderException("Odd number of characters."); 070 } 071 072 byte[] out = new byte[len >> 1]; 073 074 // two characters form the hex value. 075 for (int i = 0, j = 0; j < len; i++) { 076 int f = toDigit(data[j], j) << 4; 077 j++; 078 f = f | toDigit(data[j], j); 079 j++; 080 out[i] = (byte) (f & 0xFF); 081 } 082 083 return out; 084 } 085 086 /** 087 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 088 * The returned array will be double the length of the passed array, as it takes two characters to represent any 089 * given byte. 090 * 091 * @param data 092 * a byte[] to convert to Hex characters 093 * @return A char[] containing hexadecimal characters 094 */ 095 public static char[] encodeHex(byte[] data) { 096 return encodeHex(data, true); 097 } 098 099 /** 100 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 101 * The returned array will be double the length of the passed array, as it takes two characters to represent any 102 * given byte. 103 * 104 * @param data 105 * a byte[] to convert to Hex characters 106 * @param toLowerCase 107 * <code>true</code> converts to lowercase, <code>false</code> to uppercase 108 * @return A char[] containing hexadecimal characters 109 * @since 1.4 110 */ 111 public static char[] encodeHex(byte[] data, boolean toLowerCase) { 112 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 113 } 114 115 /** 116 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 117 * The returned array will be double the length of the passed array, as it takes two characters to represent any 118 * given byte. 119 * 120 * @param data 121 * a byte[] to convert to Hex characters 122 * @param toDigits 123 * the output alphabet 124 * @return A char[] containing hexadecimal characters 125 * @since 1.4 126 */ 127 protected static char[] encodeHex(byte[] data, char[] toDigits) { 128 int l = data.length; 129 char[] out = new char[l << 1]; 130 // two characters form the hex value. 131 for (int i = 0, j = 0; i < l; i++) { 132 out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; 133 out[j++] = toDigits[0x0F & data[i]]; 134 } 135 return out; 136 } 137 138 /** 139 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned 140 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 141 * 142 * @param data 143 * a byte[] to convert to Hex characters 144 * @return A String containing hexadecimal characters 145 * @since 1.4 146 */ 147 public static String encodeHexString(byte[] data) { 148 return new String(encodeHex(data)); 149 } 150 151 /** 152 * Converts a hexadecimal character to an integer. 153 * 154 * @param ch 155 * A character to convert to an integer digit 156 * @param index 157 * The index of the character in the source 158 * @return An integer 159 * @throws DecoderException 160 * Thrown if ch is an illegal hex character 161 */ 162 protected static int toDigit(char ch, int index) throws DecoderException { 163 int digit = Character.digit(ch, 16); 164 if (digit == -1) { 165 throw new DecoderException("Illegal hexadecimal charcter " + ch + " at index " + index); 166 } 167 return digit; 168 } 169 170 private final String charsetName; 171 172 /** 173 * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET_NAME} 174 */ 175 public Hex() { 176 // use default encoding 177 this.charsetName = DEFAULT_CHARSET_NAME; 178 } 179 180 /** 181 * Creates a new codec with the given charset name. 182 * 183 * @param csName 184 * the charset name. 185 * @since 1.4 186 */ 187 public Hex(String csName) { 188 this.charsetName = csName; 189 } 190 191 /** 192 * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values. 193 * The returned array will be half the length of the passed array, as it takes two characters to represent any given 194 * byte. An exception is thrown if the passed char array has an odd number of elements. 195 * 196 * @param array 197 * An array of character bytes containing hexadecimal digits 198 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 199 * @throws DecoderException 200 * Thrown if an odd number of characters is supplied to this function 201 * @see #decodeHex(char[]) 202 */ 203 public byte[] decode(byte[] array) throws DecoderException { 204 try { 205 return decodeHex(new String(array, getCharsetName()).toCharArray()); 206 } catch (UnsupportedEncodingException e) { 207 throw new DecoderException(e.getMessage(), e); 208 } 209 } 210 211 /** 212 * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those 213 * same values. The returned array will be half the length of the passed String or array, as it takes two characters 214 * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements. 215 * 216 * @param object 217 * A String or, an array of character bytes containing hexadecimal digits 218 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 219 * @throws DecoderException 220 * Thrown if an odd number of characters is supplied to this function or the object is not a String or 221 * char[] 222 * @see #decodeHex(char[]) 223 */ 224 public Object decode(Object object) throws DecoderException { 225 try { 226 char[] charArray = object instanceof String ? ((String) object).toCharArray() : (char[]) object; 227 return decodeHex(charArray); 228 } catch (ClassCastException e) { 229 throw new DecoderException(e.getMessage(), e); 230 } 231 } 232 233 /** 234 * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each 235 * byte in order. The returned array will be double the length of the passed array, as it takes two characters to 236 * represent any given byte. 237 * <p> 238 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by 239 * {@link #getCharsetName()}. 240 * </p> 241 * 242 * @param array 243 * a byte[] to convert to Hex characters 244 * @return A byte[] containing the bytes of the hexadecimal characters 245 * @throws IllegalStateException 246 * if the charsetName is invalid. This API throws {@link IllegalStateException} instead of 247 * {@link UnsupportedEncodingException} for backward compatibility. 248 * @see #encodeHex(byte[]) 249 */ 250 public byte[] encode(byte[] array) { 251 return StringUtils.getBytesUnchecked(encodeHexString(array), getCharsetName()); 252 } 253 254 /** 255 * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each 256 * byte in order. The returned array will be double the length of the passed String or array, as it takes two 257 * characters to represent any given byte. 258 * <p> 259 * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by 260 * {@link #getCharsetName()}. 261 * </p> 262 * 263 * @param object 264 * a String, or byte[] to convert to Hex characters 265 * @return A char[] containing hexadecimal characters 266 * @throws EncoderException 267 * Thrown if the given object is not a String or byte[] 268 * @see #encodeHex(byte[]) 269 */ 270 public Object encode(Object object) throws EncoderException { 271 try { 272 byte[] byteArray = object instanceof String ? ((String) object).getBytes(getCharsetName()) : (byte[]) object; 273 return encodeHex(byteArray); 274 } catch (ClassCastException e) { 275 throw new EncoderException(e.getMessage(), e); 276 } catch (UnsupportedEncodingException e) { 277 throw new EncoderException(e.getMessage(), e); 278 } 279 } 280 281 /** 282 * Gets the charset name. 283 * 284 * @return the charset name. 285 * @since 1.4 286 */ 287 public String getCharsetName() { 288 return this.charsetName; 289 } 290 291 /** 292 * Returns a string representation of the object, which includes the charset name. 293 * 294 * @return a string representation of the object. 295 */ 296 public String toString() { 297 return super.toString() + "[charsetName=" + this.charsetName + "]"; 298 } 299 }