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.nio.ByteBuffer;
21  import java.nio.charset.Charset;
22  
23  import org.apache.commons.codec.BinaryDecoder;
24  import org.apache.commons.codec.BinaryEncoder;
25  import org.apache.commons.codec.CharEncoding;
26  import org.apache.commons.codec.Charsets;
27  import org.apache.commons.codec.DecoderException;
28  import org.apache.commons.codec.EncoderException;
29  
30  /**
31   * Converts hexadecimal Strings. The charset used for certain operation can be set, the default is set in
32   * {@link #DEFAULT_CHARSET_NAME}
33   *
34   * This class is thread-safe.
35   *
36   * @since 1.1
37   * @version $Id: Hex.java 1811344 2017-10-06 15:19:57Z ggregory $
38   */
39  public class Hex implements BinaryEncoder, BinaryDecoder {
40  
41      /**
42       * Default charset is {@link Charsets#UTF_8}
43       *
44       * @since 1.7
45       */
46      public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
47  
48      /**
49       * Default charset name is {@link CharEncoding#UTF_8}
50       *
51       * @since 1.4
52       */
53      public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8;
54  
55      /**
56       * Used to build output as Hex
57       */
58      private static final char[] DIGITS_LOWER =
59          {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
60  
61      /**
62       * Used to build output as Hex
63       */
64      private static final char[] DIGITS_UPPER =
65          {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
66  
67      /**
68       * Converts a String representing hexadecimal values into an array of bytes of those same values. The
69       * returned array will be half the length of the passed String, as it takes two characters to represent any given
70       * byte. An exception is thrown if the passed String has an odd number of elements.
71       *
72       * @param data
73       *            A String containing hexadecimal digits
74       * @return A byte array containing binary data decoded from the supplied char array.
75       * @throws DecoderException
76       *             Thrown if an odd number or illegal of characters is supplied
77       * @since 1.11
78       */
79      public static byte[] decodeHex(final String data) throws DecoderException {
80          return decodeHex(data.toCharArray());
81      }
82  
83      /**
84       * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
85       * returned array will be half the length of the passed array, as it takes two characters to represent any given
86       * byte. An exception is thrown if the passed char array has an odd number of elements.
87       *
88       * @param data
89       *            An array of characters containing hexadecimal digits
90       * @return A byte array containing binary data decoded from the supplied char array.
91       * @throws DecoderException
92       *             Thrown if an odd number or illegal of characters is supplied
93       */
94      public static byte[] decodeHex(final char[] data) throws DecoderException {
95  
96          final int len = data.length;
97  
98          if ((len & 0x01) != 0) {
99              throw new DecoderException("Odd number of characters.");
100         }
101 
102         final byte[] out = new byte[len >> 1];
103 
104         // two characters form the hex value.
105         for (int i = 0, j = 0; j < len; i++) {
106             int f = toDigit(data[j], j) << 4;
107             j++;
108             f = f | toDigit(data[j], j);
109             j++;
110             out[i] = (byte) (f & 0xFF);
111         }
112 
113         return out;
114     }
115 
116     /**
117      * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
118      * The returned array will be double the length of the passed array, as it takes two characters to represent any
119      * given byte.
120      *
121      * @param data
122      *            a byte[] to convert to Hex characters
123      * @return A char[] containing lower-case hexadecimal characters
124      */
125     public static char[] encodeHex(final byte[] data) {
126         return encodeHex(data, true);
127     }
128 
129     /**
130      * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order.
131      * The returned array will be double the length of the passed array, as it takes two characters to represent any
132      * given byte.
133      *
134      * @param data
135      *            a byte buffer to convert to Hex characters
136      * @return A char[] containing lower-case hexadecimal characters
137      * @since 1.11
138      */
139     public static char[] encodeHex(final ByteBuffer data) {
140         return encodeHex(data, true);
141     }
142 
143     /**
144      * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
145      * The returned array will be double the length of the passed array, as it takes two characters to represent any
146      * given byte.
147      *
148      * @param data
149      *            a byte[] to convert to Hex characters
150      * @param toLowerCase
151      *            <code>true</code> converts to lowercase, <code>false</code> to uppercase
152      * @return A char[] containing hexadecimal characters in the selected case
153      * @since 1.4
154      */
155     public static char[] encodeHex(final byte[] data, final boolean toLowerCase) {
156         return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
157     }
158 
159     /**
160      * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order.
161      * The returned array will be double the length of the passed array, as it takes two characters to represent any
162      * given byte.
163      *
164      * @param data
165      *            a byte buffer to convert to Hex characters
166      * @param toLowerCase
167      *            <code>true</code> converts to lowercase, <code>false</code> to uppercase
168      * @return A char[] containing hexadecimal characters in the selected case
169      * @since 1.11
170      */
171     public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) {
172         return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
173     }
174 
175     /**
176      * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
177      * The returned array will be double the length of the passed array, as it takes two characters to represent any
178      * given byte.
179      *
180      * @param data
181      *            a byte[] to convert to Hex characters
182      * @param toDigits
183      *            the output alphabet (must contain at least 16 chars)
184      * @return A char[] containing the appropriate characters from the alphabet
185      *         For best results, this should be either upper- or lower-case hex.
186      * @since 1.4
187      */
188     protected static char[] encodeHex(final byte[] data, final char[] toDigits) {
189         final int l = data.length;
190         final char[] out = new char[l << 1];
191         // two characters form the hex value.
192         for (int i = 0, j = 0; i < l; i++) {
193             out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
194             out[j++] = toDigits[0x0F & data[i]];
195         }
196         return out;
197     }
198 
199     /**
200      * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order.
201      * The returned array will be double the length of the passed array, as it takes two characters to represent any
202      * given byte.
203      *
204      * @param data
205      *            a byte buffer to convert to Hex characters
206      * @param toDigits
207      *            the output alphabet (must be at least 16 characters)
208      * @return A char[] containing the appropriate characters from the alphabet
209      *         For best results, this should be either upper- or lower-case hex.
210      * @since 1.11
211      */
212     protected static char[] encodeHex(final ByteBuffer data, final char[] toDigits) {
213         return encodeHex(data.array(), toDigits);
214     }
215 
216     /**
217      * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
218      * String will be double the length of the passed array, as it takes two characters to represent any given byte.
219      *
220      * @param data
221      *            a byte[] to convert to Hex characters
222      * @return A String containing lower-case hexadecimal characters
223      * @since 1.4
224      */
225     public static String encodeHexString(final byte[] data) {
226         return new String(encodeHex(data));
227     }
228 
229     /**
230      * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
231      * String will be double the length of the passed array, as it takes two characters to represent any given byte.
232      *
233      * @param data
234      *            a byte[] to convert to Hex characters
235      * @param toLowerCase
236      *            <code>true</code> converts to lowercase, <code>false</code> to uppercase
237      * @return A String containing lower-case hexadecimal characters
238      * @since 1.11
239      */
240     public static String encodeHexString(final byte[] data, final boolean toLowerCase) {
241         return new String(encodeHex(data, toLowerCase));
242     }
243 
244     /**
245      * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned
246      * String will be double the length of the passed array, as it takes two characters to represent any given byte.
247      *
248      * @param data
249      *            a byte buffer to convert to Hex characters
250      * @return A String containing lower-case hexadecimal characters
251      * @since 1.11
252      */
253     public static String encodeHexString(final ByteBuffer data) {
254         return new String(encodeHex(data));
255     }
256 
257     /**
258      * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned
259      * String will be double the length of the passed array, as it takes two characters to represent any given byte.
260      *
261      * @param data
262      *            a byte buffer to convert to Hex characters
263      * @param toLowerCase
264      *            <code>true</code> converts to lowercase, <code>false</code> to uppercase
265      * @return A String containing lower-case hexadecimal characters
266      * @since 1.11
267      */
268     public static String encodeHexString(final ByteBuffer data, final boolean toLowerCase) {
269         return new String(encodeHex(data, toLowerCase));
270     }
271 
272     /**
273      * Converts a hexadecimal character to an integer.
274      *
275      * @param ch
276      *            A character to convert to an integer digit
277      * @param index
278      *            The index of the character in the source
279      * @return An integer
280      * @throws DecoderException
281      *             Thrown if ch is an illegal hex character
282      */
283     protected static int toDigit(final char ch, final int index) throws DecoderException {
284         final int digit = Character.digit(ch, 16);
285         if (digit == -1) {
286             throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index);
287         }
288         return digit;
289     }
290 
291     private final Charset charset;
292 
293     /**
294      * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET}
295      */
296     public Hex() {
297         // use default encoding
298         this.charset = DEFAULT_CHARSET;
299     }
300 
301     /**
302      * Creates a new codec with the given Charset.
303      *
304      * @param charset
305      *            the charset.
306      * @since 1.7
307      */
308     public Hex(final Charset charset) {
309         this.charset = charset;
310     }
311 
312     /**
313      * Creates a new codec with the given charset name.
314      *
315      * @param charsetName
316      *            the charset name.
317      * @throws java.nio.charset.UnsupportedCharsetException
318      *             If the named charset is unavailable
319      * @since 1.4
320      * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable
321      */
322     public Hex(final String charsetName) {
323         this(Charset.forName(charsetName));
324     }
325 
326     /**
327      * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values.
328      * The returned array will be half the length of the passed array, as it takes two characters to represent any given
329      * byte. An exception is thrown if the passed char array has an odd number of elements.
330      *
331      * @param array
332      *            An array of character bytes containing hexadecimal digits
333      * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
334      * @throws DecoderException
335      *             Thrown if an odd number of characters is supplied to this function
336      * @see #decodeHex(char[])
337      */
338     @Override
339     public byte[] decode(final byte[] array) throws DecoderException {
340         return decodeHex(new String(array, getCharset()).toCharArray());
341     }
342 
343     /**
344      * Converts a buffer of character bytes representing hexadecimal values into an array of bytes of those same values.
345      * The returned array will be half the length of the passed array, as it takes two characters to represent any given
346      * byte. An exception is thrown if the passed char array has an odd number of elements.
347      *
348      * @param buffer
349      *            An array of character bytes containing hexadecimal digits
350      * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
351      * @throws DecoderException
352      *             Thrown if an odd number of characters is supplied to this function
353      * @see #decodeHex(char[])
354      * @since 1.11
355      */
356     public byte[] decode(final ByteBuffer buffer) throws DecoderException {
357         return decodeHex(new String(buffer.array(), getCharset()).toCharArray());
358     }
359 
360     /**
361      * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those
362      * same values. The returned array will be half the length of the passed String or array, as it takes two characters
363      * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements.
364      *
365      * @param object
366      *            A String, ByteBuffer, byte[], or an array of character bytes containing hexadecimal digits
367      * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
368      * @throws DecoderException
369      *             Thrown if an odd number of characters is supplied to this function or the object is not a String or
370      *             char[]
371      * @see #decodeHex(char[])
372      */
373     @Override
374     public Object decode(final Object object) throws DecoderException {
375         if (object instanceof String) {
376             return decode(((String) object).toCharArray());
377         } else if (object instanceof byte[]) {
378             return decode((byte[]) object);
379         } else if (object instanceof ByteBuffer) {
380             return decode((ByteBuffer) object);
381         } else {
382             try {
383                 return decodeHex((char[]) object);
384             } catch (final ClassCastException e) {
385                 throw new DecoderException(e.getMessage(), e);
386             }
387         }
388     }
389 
390     /**
391      * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each
392      * byte in order. The returned array will be double the length of the passed array, as it takes two characters to
393      * represent any given byte.
394      * <p>
395      * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by
396      * {@link #getCharset()}.
397      * </p>
398      *
399      * @param array
400      *            a byte[] to convert to Hex characters
401      * @return A byte[] containing the bytes of the lower-case hexadecimal characters
402      * @since 1.7 No longer throws IllegalStateException if the charsetName is invalid.
403      * @see #encodeHex(byte[])
404      */
405     @Override
406     public byte[] encode(final byte[] array) {
407         return encodeHexString(array).getBytes(this.getCharset());
408     }
409 
410     /**
411      * Converts byte buffer into an array of bytes for the characters representing the hexadecimal values of each
412      * byte in order. The returned array will be double the length of the passed array, as it takes two characters to
413      * represent any given byte.
414      * <p>
415      * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by
416      * {@link #getCharset()}.
417      * </p>
418      *
419      * @param array
420      *            a byte buffer to convert to Hex characters
421      * @return A byte[] containing the bytes of the lower-case hexadecimal characters
422      * @see #encodeHex(byte[])
423      * @since 1.11
424      */
425     public byte[] encode(final ByteBuffer array) {
426         return encodeHexString(array).getBytes(this.getCharset());
427     }
428 
429     /**
430      * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each
431      * byte in order. The returned array will be double the length of the passed String or array, as it takes two
432      * characters to represent any given byte.
433      * <p>
434      * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by
435      * {@link #getCharset()}.
436      * </p>
437      *
438      * @param object
439      *            a String, ByteBuffer, or byte[] to convert to Hex characters
440      * @return A char[] containing lower-case hexadecimal characters
441      * @throws EncoderException
442      *             Thrown if the given object is not a String or byte[]
443      * @see #encodeHex(byte[])
444      */
445     @Override
446     public Object encode(final Object object) throws EncoderException {
447         byte[] byteArray;
448         if (object instanceof String) {
449             byteArray = ((String) object).getBytes(this.getCharset());
450         } else if (object instanceof ByteBuffer) {
451             byteArray = ((ByteBuffer) object).array();
452         } else {
453             try {
454                 byteArray = (byte[]) object;
455             } catch (final ClassCastException e) {
456                 throw new EncoderException(e.getMessage(), e);
457             }
458         }
459         return encodeHex(byteArray);
460     }
461 
462     /**
463      * Gets the charset.
464      *
465      * @return the charset.
466      * @since 1.7
467      */
468     public Charset getCharset() {
469         return this.charset;
470     }
471 
472     /**
473      * Gets the charset name.
474      *
475      * @return the charset name.
476      * @since 1.4
477      */
478     public String getCharsetName() {
479         return this.charset.name();
480     }
481 
482     /**
483      * Returns a string representation of the object, which includes the charset name.
484      *
485      * @return a string representation of the object.
486      */
487     @Override
488     public String toString() {
489         return super.toString() + "[charsetName=" + this.charset + "]";
490     }
491 }