
/*---------------------------------------------------------------------------
 * Copyright (C) 1999,2000 Dallas Semiconductor Corporation, All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Dallas Semiconductor
 * shall not be used except as stated in the Dallas Semiconductor
 * Branding Policy.
 *---------------------------------------------------------------------------
 */

// CommandAPDU.java
package com.dalsemi.onewire.container;

/**
 * A CommandAPDU represents an ISO 7816-4 specified
 * Application Protocol Data Unit (APDU) sent to a
 * smart card. A response from the smart card is in turn represented
 * by a ResponseAPDU.<BR><BR>
 *
 * According to ISO 7816-4, a CommandAPDU has the following
 * format:
 * <pre>
 *                  HEADER         |           BODY
 *         CLA    INS    P1    P2  |  [LC]    [DATA]    [LE]
 * </pre>
 * where
 * <ul>
 * <li>CLA  is the class byte
 * <li>INS  is the instruction byte
 * <li>P1   is the first parameter byte
 * <li>P2   is the second parameter byte
 * <li>LC   is the number of bytes present in the data block
 * <li>DATA is an byte array of data to be sent
 * <li>LE   is the maximum number of bytes expected in the ResponseAPDU
 * <li>[ ]  denotes optional fields
 * </ul>
 *
 * @version    0.00, 28 Aug 2000
 * @author     YL
 *
 */
public class CommandAPDU
{

   /** Constant for addressing CLA in the CommandAPDU buffer. */
   public final static int CLA = 0;

   /** Constant for addressing INS in the CommandAPDU buffer. */
   public final static int INS = 1;

   /** Constant for addressing P1  in the CommandAPDU buffer. */
   public final static int P1 = 2;

   /** Constant for addressing P2  in the CommandAPDU buffer. */
   public final static int P2 = 3;

   /** Constant for addressing LC  in the CommandAPDU buffer. */
   public final static int LC = 4;

   /** byte array to hold the entire CommandAPDU. */
   protected byte[] apduBuffer = null;

   /** length of the CommandAPDU currently in the buffer. */
   protected int apduLength;

   /**
    * Constructs a new ISO 7816-4 CommandAPDU.
    *
    * @param     buffer  the entire CommandAPDU as a byte array .
    */
   public CommandAPDU (byte[] buffer)
   {
      apduLength = buffer.length;
      apduBuffer = new byte [apduLength];

      System.arraycopy(buffer, 0, apduBuffer, 0, apduLength);
   }   // CommandAPDU

   /**
    * Constructs a new ISO 7816-4 CASE 1 CommandAPDU.
    *
    * @param     cla  CLA byte.
    * @param     ins  INS byte.
    * @param     p1   Parameter byte P1.
    * @param     p2   Parameter byte P2.
    */
   public CommandAPDU (byte cla, byte ins, byte p1, byte p2)
   {
      this(cla, ins, p1, p2, null, -1);
   }   // CommandAPDU

   /**
    * Constructs a new ISO 7816-4 CASE 2 CommandAPDU.
    *
    * @param     cla  CLA byte.
    * @param     ins  INS byte.
    * @param     p1   Parameter byte P1.
    * @param     p2   Parameter byte P2.
    * @param     le   Length of expected ResponseAPDU.
    *                 This value can be in the range of -1 to 255,
    *                 where -1 means no length is expected and 0
    *                 means the maximum length supported is expected.
    */
   public CommandAPDU (byte cla, byte ins, byte p1, byte p2, int le)
   {
      this(cla, ins, p1, p2, null, le);
   }   // CommandAPDU

   /**
    * Constructs a new ISO 7816-4 CASE 3 CommandAPDU.
    *
    * @param     cla  CLA byte.
    * @param     ins  INS byte.
    * @param     p1   Parameter byte P1.
    * @param     p2   Parameter byte P2.
    * @param     data The CommandAPDU data as a byte array.
    *                 The length LC is derived from the array length.
    */
   public CommandAPDU (byte cla, byte ins, byte p1, byte p2, byte[] data)
   {
      this(cla, ins, p1, p2, data, -1);
   }   // CommandAPDU

   /**
    * Constructs a new ISO 7816-4 CASE 4 CommandAPDU.
    *
    * @param     cla  CLA byte.
    * @param     ins  INS byte.
    * @param     p1   Parameter byte  P1.
    * @param     p2   Parameter byte  P2.
    * @param     data The CommandAPDU data as a byte array.
    *                 The length LC is derived from the array length.
    * @param     le   Length of expected ResponseAPDU.
    *                 This value can be in the range of -1 to 255,
    *                 where -1 means no length is expected and 0
    *                 means the maximum length supported is expected.
    */
   public CommandAPDU (byte cla, byte ins, byte p1, byte p2, byte[] data,
                       int le)
   {

      /* all CommandAPDU has at least 4 bytes of header */
      apduLength = 4;

      if (data != null)
      {
         apduLength++;                // add one byte for LC

         apduLength += data.length;   // add data length
      }

      if (le >= 0)
      {
         apduLength++;   // add one byte for LE
      }

      apduBuffer = new byte [apduLength];

      // fill CommandAPDU buffer body           
      apduBuffer [CLA] = cla;
      apduBuffer [INS] = ins;
      apduBuffer [P1]  = p1;
      apduBuffer [P2]  = p2;

      if (data != null)
      {
         apduBuffer [LC] = ( byte ) data.length;

         System.arraycopy(data, 0, apduBuffer, LC + 1, data.length);
      }

      if (le >= 0)
         apduBuffer [apduLength - 1] = ( byte ) le;
   }   // CommandAPDU

   /**
    * Gets the CLA byte value.
    *
    * @return CLA byte of the CommandAPDU.
    */
   public byte getCLA ()
   {
      return apduBuffer [CLA];
   }   // getCLA

   /**
    * Gets the INS byte value.
    *
    * @return INS byte of the CommandAPDU
    */
   public byte getINS ()
   {
      return apduBuffer [INS];
   }   // getINS

   /**
    * Gets the first parameter (P1) byte value.
    *
    * @return P1 byte of the CommandAPDU
    */
   public byte getP1 ()
   {
      return apduBuffer [P1];
   }   //getP1

   /**
    * Gets the second parameter (P2) byte value.
    *
    * @return P2 byte of the CommandAPDU
    */
   public byte getP2 ()
   {
      return apduBuffer [P2];
   }   // getP2

   /**
    * Gets the length of data field (LC).
    *
    * @return The number of bytes present in the data field of
    * the CommandAPDU. The value 0 indicates that there is no body.
    */
   public int getLC ()
   {
      if (apduLength >= 6)
         return apduBuffer [LC];
      else
         return 0;
   }   // getLC

   /**
    * Gets the expected length of ResponseAPDU (LE).
    *
    * @return The maximum number of bytes expected in the data field
    * of the ResponseAPDU. The value -1 indicates that no value
    * is specified.
    */
   public int getLE ()
   {
      if ((apduLength == 5) || (apduLength == (6 + getLC())))
         return apduBuffer [apduLength - 1];
      else
         return -1;
   }   // getLE

   /**
    * Gets the internal CommandAPDU buffer.
    * This method allows user to manipulate the buffered CommandAPDU.
    *
    * @return  buffer that holds the current CommandAPDU
    *
    * @see #getBytes
    *
    */
   final public byte[] getBuffer ()
   {
      return apduBuffer;
   }   // getBuffer

   /**
    * Gets the byte at the specified position in the buffer.
    * This method can only be used to access the CommandAPDU
    * currently stored.  It is not possible to read beyond the
    * end of the CommandAPDU.
    *
    * @param index   the position in the buffer
    *
    * @return        the value at the given position,
    *                or -1 if the position is invalid
    *
    * @see #setByte
    * @see #getLength
    */
   final public byte getByte (int index)
   {
      if (index >= apduLength)
         return ( byte ) -1;   // read beyond end of CommandAPDU
      else
         return (apduBuffer [index]);
   }                           // getByte

   /**
    * Gets a byte array of the buffered CommandAPDU.
    * The byte array returned gets allocated with the exact size of the
    * buffered CommandAPDU. To get direct access to the internal buffer, use
    * getBuffer().
    *
    * @return  the buffered CommandAPDU copied into a new array
    *
    * @see #getBuffer
    */
   final public byte[] getBytes ()
   {
      byte[] apdu = new byte [apduLength];

      System.arraycopy(apduBuffer, 0, apdu, 0, apduLength);

      return apdu;
   }   // getBytes

   /**
    * Gets the length of the buffered CommandAPDU.
    *
    * @return  the length of the CommandAPDU currently stored
    */
   final public int getLength ()
   {
      return apduLength;
   }   // getLength

   /**
    * Sets the byte value at the specified position in the buffer.
    * This method can only be used to modify an CommandAPDU
    * already stored. It is not possible to set bytes beyond
    * the end of the current CommandAPDU.
    *
    * @param index   the position in the buffer
    * @param value   the new byte value to store
    *
    * @see #getByte
    * @see #getLength
    *
    */
   final public void setByte (int index, byte value)
   {
      if (index < apduLength)
         apduBuffer [index] = value;
   }   // setByte

   /**
    * Gets a string representation of this CommandAPDU.
    *
    * @return a string describing this CommandAPDU.
    */
   public String toString ()
   {
      String apduString = "";

      apduString += "CLA = " + Integer.toHexString(apduBuffer [CLA] & 0xFF);
      apduString += " INS = " + Integer.toHexString(apduBuffer [INS] & 0xFF);
      apduString += " P1 = " + Integer.toHexString(apduBuffer [P1] & 0xFF);
      apduString += " P2 = " + Integer.toHexString(apduBuffer [P2] & 0xFF);
      apduString += " LC = " + Integer.toHexString(getLC() & 0xFF);

      if (getLE() == -1)
         apduString += " LE = " + getLE();
      else
         apduString += " LE = " + Integer.toHexString(getLE() & 0xFF);

      if (apduLength > 5)
      {
         apduString += "\nDATA = ";

         for (int i = 5; i < getLC() + 5; i++)
         {
            if ((apduBuffer [i] & 0xFF) < 0x10)
               apduString += '0';

            apduString += Integer.toHexString(( int ) (apduBuffer [i] & 0xFF))
                          + " ";
         }
      }

      // make hex String representation of byte array     
      return (apduString.toUpperCase());
   }   // toString
}
