    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 * @version $Id$
    038 */
    039public class Hex implements BinaryEncoder, BinaryDecoder {
    040
    041    /**
    042     * Default charset is {@link Charsets#UTF_8}
    043     *
    044     * @since 1.7
    045     */
    046    public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
    047
    048    /**
    049     * Default charset name is {@link CharEncoding#UTF_8}
    050     *
    051     * @since 1.4
    052     */
    053    public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8;
    054
    055    /**
    056     * Used to build output as Hex
    057     */
    058    private static final char[] DIGITS_LOWER =
    059        {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    060
    061    /**
    062     * Used to build output as Hex
    063     */
    064    private static final char[] DIGITS_UPPER =
    065        {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    066
    067    /**
    068     * Converts a String representing hexadecimal values into an array of bytes of those same values. The
    069     * returned array will be half the length of the passed String, as it takes two characters to represent any given
    070     * byte. An exception is thrown if the passed String has an odd number of elements.
    071     *
    072     * @param data
    073     *            A String containing hexadecimal digits
    074     * @return A byte array containing binary data decoded from the supplied char array.
    075     * @throws DecoderException
    076     *             Thrown if an odd number or illegal of characters is supplied
    077     * @since 1.11
    078     */
    079    public static byte[] decodeHex(final String data) throws DecoderException {
    080        return decodeHex(data.toCharArray());
    081    }
    082
    083    /**
    084     * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
    085     * returned array will be half the length of the passed array, as it takes two characters to represent any given
    086     * byte. An exception is thrown if the passed char array has an odd number of elements.
    087     *
    088     * @param data
    089     *            An array of characters containing hexadecimal digits
    090     * @return A byte array containing binary data decoded from the supplied char array.
    091     * @throws DecoderException
    092     *             Thrown if an odd number or illegal of characters is supplied
    093     */
    094    public static byte[] decodeHex(final char[] data) throws DecoderException {
    095
    096        final int len = data.length;
    097
    098        if ((len & 0x01) != 0) {
    099            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}