View Javadoc

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.apache.commons.codec.binary;
19  
20  import java.io.UnsupportedEncodingException;
21  
22  import org.apache.commons.codec.BinaryDecoder;
23  import org.apache.commons.codec.BinaryEncoder;
24  import org.apache.commons.codec.CharEncoding;
25  import org.apache.commons.codec.DecoderException;
26  import org.apache.commons.codec.EncoderException;
27  
28  /**
29   * Converts hexadecimal Strings. The charset used for certain operation can be set, the default is set in
30   * {@link #DEFAULT_CHARSET_NAME}
31   * 
32   * @since 1.1
33   * @author Apache Software Foundation
34   * @version $Id: Hex.java 1157192 2011-08-12 17:27:38Z ggregory $
35   */
36  public class Hex implements BinaryEncoder, BinaryDecoder {
37  
38      /**
39       * Default charset name is {@link CharEncoding#UTF_8}
40       * 
41       * @since 1.4
42       */
43      public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8;
44  
45      /**
46       * Used to build output as Hex
47       */
48      private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
49  
50      /**
51       * Used to build output as Hex
52       */
53      private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
54  
55      /**
56       * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
57       * returned array will be half the length of the passed array, as it takes two characters to represent any given
58       * byte. An exception is thrown if the passed char array has an odd number of elements.
59       * 
60       * @param data
61       *            An array of characters containing hexadecimal digits
62       * @return A byte array containing binary data decoded from the supplied char array.
63       * @throws DecoderException
64       *             Thrown if an odd number or illegal of characters is supplied
65       */
66      public static byte[] decodeHex(char[] data) throws DecoderException {
67  
68          int len = data.length;
69  
70          if ((len & 0x01) != 0) {
71              throw new DecoderException("Odd number of characters.");
72          }
73  
74          byte[] out = new byte[len >> 1];
75  
76          // two characters form the hex value.
77          for (int i = 0, j = 0; j < len; i++) {
78              int f = toDigit(data[j], j) << 4;
79              j++;
80              f = f | toDigit(data[j], j);
81              j++;
82              out[i] = (byte) (f & 0xFF);
83          }
84  
85          return out;
86      }
87  
88      /**
89       * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
90       * The returned array will be double the length of the passed array, as it takes two characters to represent any
91       * given byte.
92       * 
93       * @param data
94       *            a byte[] to convert to Hex characters
95       * @return A char[] containing hexadecimal characters
96       */
97      public static char[] encodeHex(byte[] data) {
98          return encodeHex(data, true);
99      }
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     @Override
299     public String toString() {
300         return super.toString() + "[charsetName=" + this.charsetName + "]";
301     }
302 }