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 }