    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
    018package org.apache.commons.codec.binary;
    019
    020import java.nio.ByteBuffer;
    021import java.nio.charset.Charset;
    022
    023import org.apache.commons.codec.BinaryDecoder;
    024import org.apache.commons.codec.BinaryEncoder;
    025import org.apache.commons.codec.CharEncoding;
    026import org.apache.commons.codec.Charsets;
    027import org.apache.commons.codec.DecoderException;
    028import org.apache.commons.codec.EncoderException;
    029
    030/**
    031 * Converts hexadecimal Strings. The charset used for certain operation can be set, the default is set in
    032 * {@link #DEFAULT_CHARSET_NAME}
    033 *
    034 * This class is thread-safe.
    035 *
    036 * @since 1.1
    037 */
    038public class Hex implements BinaryEncoder, BinaryDecoder {
    039
    040    /**
    041     * Default charset is {@link Charsets#UTF_8}
    042     *
    043     * @since 1.7
    044     */
    045    public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
    046
    047    /**
    048     * Default charset name is {@link CharEncoding#UTF_8}
    049     *
    050     * @since 1.4
    051     */
    052    public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8;
    053
    054    /**
    055     * Used to build output as Hex
    056     */
    057    private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
    058            'e', 'f' };
    059
    060    /**
    061     * Used to build output as Hex
    062     */
    063    private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
    064            'E', 'F' };
    065
    066    /**
    067     * Converts a String representing hexadecimal values into an array of bytes of those same values. The returned array
    068     * will be half the length of the passed String, as it takes two characters to represent any given byte. An
    069     * exception is thrown if the passed String has an odd number of elements.
    070     *
    071     * @param data A String containing hexadecimal digits
    072     * @return A byte array containing binary data decoded from the supplied char array.
    073     * @throws DecoderException Thrown if an odd number or illegal of characters is supplied
    074     * @since 1.11
    075     */
    076    public static byte[] decodeHex(final String data) throws DecoderException {
    077        return decodeHex(data.toCharArray());
    078    }
    079
    080    /**
    081     * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
    082     * returned array will be half the length of the passed array, as it takes two characters to represent any given
    083     * byte. An exception is thrown if the passed char array has an odd number of elements.
    084     *
    085     * @param data An array of characters containing hexadecimal digits
    086     * @return A byte array containing binary data decoded from the supplied char array.
    087     * @throws DecoderException Thrown if an odd number or illegal of characters is supplied
    088     */
    089    public static byte[] decodeHex(final char[] data) throws DecoderException {
    090
    091        final int len = data.length;
    092
    093        if ((len & 0x01) != 0) {
    094            throw new DecoderException("Odd number of characters.");
    095        }
    096
    097        final byte[] out = new byte[len >> 1];
    098
    099        // two characters form the hex value.
    100        for (int i = 0, j = 0; j < len; i++) {
    101            int f = toDigit(data[j], j) << 4;
    102            j++;
    103            f = f | toDigit(data[j], j);
    104            j++;
    105            out[i] = (byte) (f & 0xFF);
    106        }
    107
    108        return out;
    109    }
    110
    111    /**
    112     * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
    113     * The returned array will be double the length of the passed array, as it takes two characters to represent any
    114     * given byte.
    115     *
    116     * @param data a byte[] to convert to Hex characters
    117     * @return A char[] containing lower-case hexadecimal characters
    118     */
    119    public static char[] encodeHex(final byte[] data) {
    120        return encodeHex(data, true);
    121    }
    122
    123    /**
    124     * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
    125     * returned array will be double the length of the passed array, as it takes two characters to represent any given
    126     * byte.
    127     *
    128     * @param data a byte buffer to convert to Hex characters
    129     * @return A char[] containing lower-case hexadecimal characters
    130     * @since 1.11
    131     */
    132    public static char[] encodeHex(final ByteBuffer data) {
    133        return encodeHex(data, true);
    134    }
    135
    136    /**
    137     * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
    138     * The returned array will be double the length of the passed array, as it takes two characters to represent any
    139     * given byte.
    140     *
    141     * @param data        a byte[] to convert to Hex characters
    142     * @param toLowerCase <code>true</code> converts to lowercase, <code>false</code> to uppercase
    143     * @return A char[] containing hexadecimal characters in the selected case
    144     * @since 1.4
    145     */
    146    public static char[] encodeHex(final byte[] data, final boolean toLowerCase) {
    147        return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
    148    }
    149
    150    /**
    151     * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
    152     * returned array will be double the length of the passed array, as it takes two characters to represent any given
    153     * byte.
    154     *
    155     * @param data        a byte buffer to convert to Hex characters
    156     * @param toLowerCase <code>true</code> converts to lowercase, <code>false</code> to uppercase
    157     * @return A char[] containing hexadecimal characters in the selected case
    158     * @since 1.11
    159     */
    160    public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) {
    161        return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
    162    }
    163
    164    /**
    165     * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
    166     * The returned array will be double the length of the passed array, as it takes two characters to represent any
    167     * given byte.
    168     *
    169     * @param data     a byte[] to convert to Hex characters
    170     * @param toDigits the output alphabet (must contain at least 16 chars)
    171     * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either
    172     *         upper- or lower-case hex.
    173     * @since 1.4
    174     */
    175    protected static char[] encodeHex(final byte[] data, final char[] toDigits) {
    176        final int l = data.length;
    177        final char[] out = new char[l << 1];
    178        // two characters form the hex value.
    179        for (int i = 0, j = 0; i < l; i++) {
    180            out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
    181            out[j++] = toDigits[0x0F & data[i]];
    182        }
    183        return out;
    184    }
    185
    186    /**
    187     * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
    188     * returned array will be double the length of the passed array, as it takes two characters to represent any given
    189     * byte.
    190     *
    191     * @param byteBuffer a byte buffer to convert to Hex characters
    192     * @param toDigits   the output alphabet (must be at least 16 characters)
    193     * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either
    194     *         upper- or lower-case hex.
    195     * @since 1.11
    196     */
    197    protected static char[] encodeHex(final ByteBuffer byteBuffer, final char[] toDigits) {
    198        return encodeHex(toByteArray(byteBuffer), toDigits);
    199    }
    200
    201    /**
    202     * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
    203     * String will be double the length of the passed array, as it takes two characters to represent any given byte.
    204     *
    205     * @param data a byte[] to convert to Hex characters
    206     * @return A String containing lower-case hexadecimal characters
    207     * @since 1.4
    208     */
    209    public static String encodeHexString(final byte[] data) {
    210        return new String(encodeHex(data));
    211    }
    212
    213    /**
    214     * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
    215     * String will be double the length of the passed array, as it takes two characters to represent any given byte.
    216     *
    217     * @param data        a byte[] to convert to Hex characters
    218     * @param toLowerCase <code>true</code> converts to lowercase, <code>false</code> to uppercase
    219     * @return A String containing lower-case hexadecimal characters
    220     * @since 1.11
    221     */
    222    public static String encodeHexString(final byte[] data, final boolean toLowerCase) {
    223        return new String(encodeHex(data, toLowerCase));
    224    }
    225
    226    /**
    227     * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned
    228     * String will be double the length of the passed array, as it takes two characters to represent any given byte.
    229     *
    230     * @param data a byte buffer to convert to Hex characters
    231     * @return A String containing lower-case hexadecimal characters
    232     * @since 1.11
    233     */
    234    public static String encodeHexString(final ByteBuffer data) {
    235        return new String(encodeHex(data));
    236    }
    237
    238    /**
    239     * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned
    240     * String will be double the length of the passed array, as it takes two characters to represent any given byte.
    241     *
    242     * @param data        a byte buffer to convert to Hex characters
    243     * @param toLowerCase <code>true</code> converts to lowercase, <code>false</code> to uppercase
    244     * @return A String containing lower-case hexadecimal characters
    245     * @since 1.11
    246     */
    247    public static String encodeHexString(final ByteBuffer data, final boolean toLowerCase) {
    248        return new String(encodeHex(data, toLowerCase));
    249    }
    250
    251    private static byte[] toByteArray(final ByteBuffer byteBuffer) {
    252        if (byteBuffer.hasArray()) {
    253            return byteBuffer.array();
    254        }
    255        final byte[] byteArray = new byte[byteBuffer.remaining()];
    256        byteBuffer.get(byteArray);
    257        return byteArray;
    258    }
    259
    260    /**
    261     * Converts a hexadecimal character to an integer.
    262     *
    263     * @param ch    A character to convert to an integer digit
    264     * @param index The index of the character in the source
    265     * @return An integer
    266     * @throws DecoderException Thrown if ch is an illegal hex character
    267     */
    268    protected static int toDigit(final char ch, final int index) throws DecoderException {
    269        final int digit = Character.digit(ch, 16);
    270        if (digit == -1) {
    271            throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index);
    272        }
    273        return digit;
    274    }
    275
    276    private final Charset charset;
    277
    278    /**
    279     * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET}
    280     */
    281    public Hex() {
    282        // use default encoding
    283        this.charset = DEFAULT_CHARSET;
    284    }
    285
    286    /**
    287     * Creates a new codec with the given Charset.
    288     *
    289     * @param charset the charset.
    290     * @since 1.7
    291     */
    292    public Hex(final Charset charset) {
    293        this.charset = charset;
    294    }
    295
    296    /**
    297     * Creates a new codec with the given charset name.
    298     *
    299     * @param charsetName the charset name.
    300     * @throws java.nio.charset.UnsupportedCharsetException If the named charset is unavailable
    301     * @since 1.4
    302     * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable
    303     */
    304    public Hex(final String charsetName) {
    305        this(Charset.forName(charsetName));
    306    }
    307
    308    /**
    309     * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values.
    310     * The returned array will be half the length of the passed array, as it takes two characters to represent any given
    311     * byte. An exception is thrown if the passed char array has an odd number of elements.
    312     *
    313     * @param array An array of character bytes containing hexadecimal digits
    314     * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
    315     * @throws DecoderException Thrown if an odd number of characters is supplied to this function
    316     * @see #decodeHex(char[])
    317     */
    318    @Override
    319    public byte[] decode(final byte[] array) throws DecoderException {
    320        return decodeHex(new String(array, getCharset()).toCharArray());
    321    }
    322
    323    /**
    324     * Converts a buffer of character bytes representing hexadecimal values into an array of bytes of those same values.
    325     * The returned array will be half the length of the passed array, as it takes two characters to represent any given
    326     * byte. An exception is thrown if the passed char array has an odd number of elements.
    327     *
    328     * @param buffer An array of character bytes containing hexadecimal digits
    329     * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
    330     * @throws DecoderException Thrown if an odd number of characters is supplied to this function
    331     * @see #decodeHex(char[])
    332     * @since 1.11
    333     */
    334    public byte[] decode(final ByteBuffer buffer) throws DecoderException {
    335        return decodeHex(new String(toByteArray(buffer), getCharset()).toCharArray());
    336    }
    337
    338    /**
    339     * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those
    340     * same values. The returned array will be half the length of the passed String or array, as it takes two characters
    341     * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements.
    342     *
    343     * @param object A String, ByteBuffer, byte[], or an array of character bytes containing hexadecimal digits
    344     * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
    345     * @throws DecoderException Thrown if an odd number of characters is supplied to this function or the object is not
    346     *                          a String or char[]
    347     * @see #decodeHex(char[])
    348     */
    349    @Override
    350    public Object decode(final Object object) throws DecoderException {
    351        if (object instanceof String) {
    352            return decode(((String) object).toCharArray());
    353        } else if (object instanceof byte[]) {
    354            return decode((byte[]) object);
    355        } else if (object instanceof ByteBuffer) {
    356            return decode((ByteBuffer) object);
    357        } else {
    358            try {
    359                return decodeHex((char[]) object);
    360            } catch (final ClassCastException e) {
    361                throw new DecoderException(e.getMessage(), e);
    362            }
    363        }
    364    }
    365
    366    /**
    367     * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each
    368     * byte in order. The returned array will be double the length of the passed array, as it takes two characters to
    369     * represent any given byte.
    370     * <p>
    371     * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by
    372     * {@link #getCharset()}.
    373     * </p>
    374     *
    375     * @param array a byte[] to convert to Hex characters
    376     * @return A byte[] containing the bytes of the lower-case hexadecimal characters
    377     * @since 1.7 No longer throws IllegalStateException if the charsetName is invalid.
    378     * @see #encodeHex(byte[])
    379     */
    380    @Override
    381    public byte[] encode(final byte[] array) {
    382        return encodeHexString(array).getBytes(this.getCharset());
    383    }
    384
    385    /**
    386     * Converts byte buffer into an array of bytes for the characters representing the hexadecimal values of each byte
    387     * in order. The returned array will be double the length of the passed array, as it takes two characters to
    388     * represent any given byte.
    389     * <p>
    390     * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by
    391     * {@link #getCharset()}.
    392     * </p>
    393     *
    394     * @param array a byte buffer to convert to Hex characters
    395     * @return A byte[] containing the bytes of the lower-case hexadecimal characters
    396     * @see #encodeHex(byte[])
    397     * @since 1.11
    398     */
    399    public byte[] encode(final ByteBuffer array) {
    400        return encodeHexString(array).getBytes(this.getCharset());
    401    }
    402
    403    /**
    404     * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each
    405     * byte in order. The returned array will be double the length of the passed String or array, as it takes two
    406     * characters to represent any given byte.
    407     * <p>
    408     * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by
    409     * {@link #getCharset()}.
    410     * </p>
    411     *
    412     * @param object a String, ByteBuffer, or byte[] to convert to Hex characters
    413     * @return A char[] containing lower-case hexadecimal characters
    414     * @throws EncoderException Thrown if the given object is not a String or byte[]
    415     * @see #encodeHex(byte[])
    416     */
    417    @Override
    418    public Object encode(final Object object) throws EncoderException {
    419        byte[] byteArray;
    420        if (object instanceof String) {
    421            byteArray = ((String) object).getBytes(this.getCharset());
    422        } else if (object instanceof ByteBuffer) {
    423            byteArray = toByteArray((ByteBuffer) object);
    424        } else {
    425            try {
    426                byteArray = (byte[]) object;
    427            } catch (final ClassCastException e) {
    428                throw new EncoderException(e.getMessage(), e);
    429            }
    430        }
    431        return encodeHex(byteArray);
    432    }
    433
    434    /**
    435     * Gets the charset.
    436     *
    437     * @return the charset.
    438     * @since 1.7
    439     */
    440    public Charset getCharset() {
    441        return this.charset;
    442    }
    443
    444    /**
    445     * Gets the charset name.
    446     *
    447     * @return the charset name.
    448     * @since 1.4
    449     */
    450    public String getCharsetName() {
    451        return this.charset.name();
    452    }
    453
    454    /**
    455     * Returns a string representation of the object, which includes the charset name.
    456     *
    457     * @return a string representation of the object.
    458     */
    459    @Override
    460    public String toString() {
    461        return super.toString() + "[charsetName=" + this.charset + "]";
    462    }
    463}