content: /*******************************************************************************
   * Copyright (c) 2011, J.W. Janssen
   * 
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *     J.W. Janssen - Cleanup and make API more OO-oriented.
   *******************************************************************************/
  package org.flashtool.binutils.hex.util;
  
  
  import java.io.*;
  import java.nio.*;
  
  
  /**
   * 
   */
  public final class HexUtils
  {
    // CONSTRUCTORS
  
    /**
     * Creates a new HexUtils instance.
     */
    private HexUtils()
    {
      // NO-op
    }
  
    // METHODS
  
    /**
     * Parses the hex-byte in the given character sequence at the given offset.
     * 
     * @param aInput
     *          the characters to parse as hex-bytes.
     * @return a byte value.
     * @throws IllegalArgumentException
     *           in case the given char sequence was <code>null</code>, in case
     *           the given input did not yield a hex-byte, or the requested offset
     *           is outside the boundaries of the given char sequence.
     */
    public static int parseHexByte(final char[] aInput) throws IllegalArgumentException
    {
      if (aInput == null)
      {
        throw new IllegalArgumentException("Input cannot be null!");
      }
      return parseHexByte(new String(aInput), 0);
    }
  
    /**
     * Parses the hex-byte in the given character sequence at the given offset.
     * 
     * @param aInput
     *          the char sequence to parse as hex-bytes;
     * @param aOffset
     *          the offset in the char sequence to start parsing.
     * @return a byte value.
     * @throws IllegalArgumentException
     *           in case the given char sequence was <code>null</code>, in case
     *           the given input did not yield a hex-byte, or the requested offset
     *           is outside the boundaries of the given char sequence.
     */
    public static int parseHexByte(final CharSequence aInput, final int aOffset) throws IllegalArgumentException
    {
      if (aInput == null)
      {
        throw new IllegalArgumentException("Input cannot be null!");
      }
  
      try
      {
        return (Integer.parseInt(aInput.subSequence(aOffset, aOffset + 2).toString(), 16));
      }
      catch (IndexOutOfBoundsException exception)
      {
        throw new IllegalArgumentException("No such offset: " + aOffset + "; length = " + aInput.length());
      }
      catch (NumberFormatException exception)
      {
        throw new IllegalArgumentException("Not a hex-digit string: " + aInput);
      }
    }
  
    /**
     * Reads two characters from the given reader and parses them as a single
     * hex-value byte.
     * 
     * @param aReader
     * @return
     * @throws IllegalArgumentException
     * @throws IOException
     */
    public static int parseHexByte(final Reader aReader) throws IllegalArgumentException, IOException
    {
      return parseHexNumber(aReader, 1);
    }
  
    /**
     * Parses the hex-byte in the given character sequence at the given offset.
     * 
     * @param aInput
     *          the char sequence to parse as hex-bytes;
     * @param aOffset
     *          the offset in the char sequence to start parsing.
     * @return a byte value.
     * @throws IllegalArgumentException
     *           in case the given char sequence was <code>null</code>, in case
     *           the given input did not yield a hex-byte, or the requested offset
     *           is outside the boundaries of the given char sequence.
     */
    public static int[] parseHexBytes(final CharSequence aInput, final int aOffset, final int aByteCount)
        throws IllegalArgumentException
    {
      if (aInput == null)
      {
        throw new IllegalArgumentException("Input cannot be null!");
      }
      if (aByteCount < 0)
      {
        throw new IllegalArgumentException("Byte count cannot be less than one!");
      }
  
      try
      {
        final int[] result = new int[aByteCount];
        int offset = aOffset;
        for (int i = 0; i < aByteCount; i++, offset += 2)
        {
          result[i] = Integer.parseInt(aInput.subSequence(offset, offset + 2).toString(), 16);
        }
        return result;
      }
      catch (IndexOutOfBoundsException exception)
      {
        throw new IllegalArgumentException("No such offset: " + aOffset + "; length = " + aInput.length());
      }
      catch (NumberFormatException exception)
      {
        throw new IllegalArgumentException("Not a hex-digit string!");
      }
    }
  
    /**
     * @param aInput
     * @param aOffset
     * @param aByteCount
     * @return
     * @throws IllegalArgumentException
     */
    public static int parseHexNumber(final CharSequence aInput, final int aOffset, final int aByteCount)
        throws IllegalArgumentException
    {
      if (aInput == null)
      {
        throw new IllegalArgumentException("Input cannot be null!");
      }
      if (aByteCount < 1)
      {
        throw new IllegalArgumentException("Byte count cannot be less than one!");
      }
  
      final int[] addressBytes = parseHexBytes(aInput, aOffset, aByteCount);
  
      int address = 0;
      for (int i = 0; i < aByteCount; i++)
      {
        address |= addressBytes[i];
        if (i < aByteCount - 1)
        {
          address <<= 8;
        }
      }
  
      return address;
    }
  
    /**
     * Reads a number of characters from the given reader and parses them as a
     * hex-value.
     * 
     * @param aReader
     *          the reader to read the data from;
     * @param aByteCount
     *          the number of bytes to read (= 2 * amount of actual characters
     *          read).
     * @return the parsed number.
     * @throws IllegalArgumentException
     *           in case the given reader was <code>null</code> or the given byte
     *           count was <= 0.
     * @throws IOException
     *           in case of I/O problems.
     */
    public static int parseHexNumber(final Reader aReader, final int aByteCount) throws IllegalArgumentException,
        IOException
    {
      if (aReader == null)
      {
        throw new IllegalArgumentException("Input cannot be null!");
      }
      if (aByteCount <= 0)
      {
        throw new IllegalArgumentException("Byte count cannot be less or equal to zero!");
      }
  
      final char[] buf = new char[2 * aByteCount];
      if (aReader.read(buf) != buf.length)
      {
        throw new IOException("Unexpected end-of-stream?!");
      }
  
      int result = 0;
      for (char element : buf)
      {
        int hexdigit = Character.digit(element, 16);
        if (hexdigit < 0)
        {
          throw new IOException("Unexpected character: " + element);
        }
        result *= 16;
        result |= hexdigit;
      }
  
      return result;
    }
  
    /**
     * Parses the hex-word in the given character sequence at the given offset
     * assuming it is in big endian byte order.
     * 
     * @param aInput
     *          the char sequence to parse as hex-bytes;
     * @param aOffset
     *          the offset in the char sequence to start parsing.
     * @return a word value, in big endian byte order.
     * @throws IllegalArgumentException
     *           in case the given char sequence was <code>null</code>, in case
     *           the given input did not yield a hex-byte, or the requested offset
     *           is outside the boundaries of the given char sequence.
     */
    public static int parseHexWord(final char[] aInput) throws IllegalArgumentException
    {
      if (aInput == null)
      {
        throw new IllegalArgumentException("Input cannot be null!");
      }
      return parseHexWord(new String(aInput), 0);
    }
  
    /**
     * Parses the hex-word in the given character sequence at the given offset
     * assuming it is in big endian byte order.
     * 
     * @param aInput
     *          the char sequence to parse as hex-bytes;
     * @param aOffset
     *          the offset in the char sequence to start parsing.
     * @return a word value, in big endian byte order.
     * @throws IllegalArgumentException
     *           in case the given char sequence was <code>null</code>, in case
     *           the given input did not yield a hex-byte, or the requested offset
     *           is outside the boundaries of the given char sequence.
     */
    public static int parseHexWord(final CharSequence aInput, final int aOffset) throws IllegalArgumentException
    {
      if (aInput == null)
      {
        throw new IllegalArgumentException("Input cannot be null!");
      }
  
      try
      {
        int msb = Integer.parseInt(aInput.subSequence(aOffset + 0, aOffset + 2).toString(), 16);
        int lsb = Integer.parseInt(aInput.subSequence(aOffset + 2, aOffset + 4).toString(), 16);
        return ByteOrderUtils.createWord(ByteOrder.BIG_ENDIAN, msb, lsb);
      }
      catch (IndexOutOfBoundsException exception)
      {
        throw new IllegalArgumentException("No such offset: " + aOffset + "; length = " + aInput.length());
      }
      catch (NumberFormatException exception)
      {
        throw new IllegalArgumentException("Not a hex-digit string!");
      }
    }
  
    /**
     * Reads four characters from the given reader and parses them as a single
     * hex-value word.
     * 
     * @param aReader
     * @return
     * @throws IllegalArgumentException
     * @throws IOException
     */
    public static int parseHexWord(final Reader aReader) throws IllegalArgumentException, IOException
    {
      return parseHexNumber(aReader, 2);
    }
  
  }
  