
/*---------------------------------------------------------------------------
 * 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.
 *---------------------------------------------------------------------------
 */

import java.lang.*;
import java.util.*;
import java.io.*;

import com.dalsemi.onewire.*;
import com.dalsemi.onewire.adapter.*;
import com.dalsemi.onewire.container.*;


/**     A Console Application for testing OneWireContainer16. */
public class OWC16Test
{

   // constant for determining platform
   static final int          PC_PLATFORM     = 0x01;
   static final int          TINI_PLATFORM   = 0x02;
   static int                platform        = 0;

   static final int          COMMAND_SUCCESS = 0x9000;

   // maximum run time for process()
   static final int          MAX_RUN_TIME    = 15;

   static DSPortAdapter      adapter         = null;
   static OneWireContainer16 owc16           = null;
   static CommandAPDU        capdu           = null;
   static ResponseAPDU       rapdu           = null;
   static boolean            fError          = false;

   // user menu options
   static Hashtable hashMainMenu         = new Hashtable();
   static Hashtable hashGetCommandMenu   = new Hashtable();
   static Hashtable hashSetCommandMenu   = new Hashtable();
   static Hashtable hashOtherCommandMenu = new Hashtable();

   // user menu options counts
   static int mainMenuItemCount;
   static int getCommandItemCount;
   static int setCommandItemCount;
   static int otherCommandItemCount;

   /**
    * Method main
    *
    *
    * @param args
    *
    */
   static public void main (String args [])
   {
      OneWireContainer owc = null;

      // find and initialize the first OneWireContainer16
      owc = initContainer();

      if (!(owc instanceof OneWireContainer16))
      {
         cleanup();
         System.out.println(
            "*************************************************************************");
         System.out.println("No Suitable Container found. Exit program.");
         System.out.println();
         System.exit(0);
      }
      else
         owc16 = ( OneWireContainer16 ) owc;

      int       curMenuChoice     = 0;
      int       curMenuItemChoice = 0;
      int       curMenuItemCount  = 0;
      Hashtable curMenu           = new Hashtable();

      initMenu();

      curMenuChoice = getMenuChoice(hashMainMenu, mainMenuItemCount);

      while (true)
      {
         switch (curMenuChoice)
         {

            case 0 :
               curMenu          = hashGetCommandMenu;
               curMenuItemCount = getCommandItemCount;
               break;
            case 1 :
               curMenu          = hashSetCommandMenu;
               curMenuItemCount = setCommandItemCount;
               break;
            case 2 :
               curMenu          = hashOtherCommandMenu;
               curMenuItemCount = otherCommandItemCount;
               break;
            case 3 :
               cleanup();
               System.exit(0);
               break;
         }

         curMenuItemChoice = getMenuChoice(curMenu, curMenuItemCount);

         if (curMenuItemChoice == 0)
         {
            curMenuChoice = getMenuChoice(hashMainMenu, mainMenuItemCount);
         }
         else
         {
            sendCommand(
               ( String ) curMenu.get(new Integer(curMenuItemChoice)));

            if (fError == true)
               break;
            else
            {
              System.out.println("  Hit ENTER to continue...");
              getString();
            }
         }
      }   // while
   }

   // find and initialize the first OneWireContainer16
   static OneWireContainer initContainer ()
   {
      OneWireContainer owc = null;

      try
      {
         if (System.getProperty("os.arch").indexOf("TINI") != -1)
         {
            platform = TINI_PLATFORM;

            //        System.out.println("in TINI");
         }
         else
         {
            platform = PC_PLATFORM;

            //        System.out.println("in PC");
         }

         adapter = OneWireAccessProvider.getDefaultAdapter();

         if (adapter != null)
         {

            // get exclusive use of adapter
            adapter.beginExclusive(true);
            adapter.setSearchAllDevices();

            // Include Java iButtons Versions 1 and 2.
            byte[] buttonsToInclude = { ( byte ) 0x16, ( byte ) 0x96 };

            adapter.targetFamily(buttonsToInclude);

            if (adapter.findFirstDevice())
            {
               owc = adapter.getFirstDeviceContainer();

               if (owc instanceof OneWireContainer16)
               {

                  owc.setupContainer(adapter, owc.getAddressAsString());

                  // print device information
                  System.out.println();
                  System.out.println(
                     "*************************************************************************");
                  System.out.println("* 1-Wire Device Name: "
                                     + owc.getName());
                  System.out.println("* 1-Wire Device Other Names: "
                                     + owc.getAlternateNames());
                  System.out.println("* 1-Wire Device Address: "
                                     + owc.getAddressAsString());
                  System.out.println(
                     "* 1-Wire Device Max speed: "
                     + ((owc.getMaxSpeed() == DSPortAdapter.SPEED_OVERDRIVE)
                        ? "Overdrive"
                        : "Normal"));
                  System.out.println("* 1-Wire Device Description: "
                                     + owc.getDescription());
                  System.out.println(
                     "*************************************************************************");
                  System.out.println("  Hit ENTER to continue...");
                  getString();
               }
            }   // if findFirstDevice()
            else
            {
               System.out.print("Please insert an Java iButton of Family ");

               for (int i = 0; i < buttonsToInclude.length; i++)
                  System.out.print(printByteToHex(buttonsToInclude [i]));

               System.out.println();
            }
         }
         else
         {
            System.out.println(
               "No adapter found.  Please check hardware setup");
         }
      }
      catch (Exception e)
      {
         printException(e);
      }

      return owc;
   }   // initContainer

   /** initialize menu choices  */
   static void initMenu ()
   {
      hashMainMenu.put(new Integer(0), "Get Command");
      hashMainMenu.put(new Integer(1), "Set Command");
      hashMainMenu.put(new Integer(2), "Other Command");
      hashMainMenu.put(new Integer(3), "Quit");

      mainMenuItemCount = 4;

      hashGetCommandMenu.put(new Integer(0), "Return to Main Menu");
      hashGetCommandMenu.put(new Integer(1), "getAIDByNumber");
      hashGetCommandMenu.put(new Integer(2), "getAppletGCMode");
      hashGetCommandMenu.put(new Integer(3), "getATR");
      hashGetCommandMenu.put(new Integer(4), "getCommandPINMode");
      hashGetCommandMenu.put(new Integer(5), "getCommitBufferSize");
      hashGetCommandMenu.put(new Integer(6), "getEphemeralGCMode");
      hashGetCommandMenu.put(new Integer(7), "getErrorReportingMode");
      hashGetCommandMenu.put(new Integer(8), "getExceptionMode");
      hashGetCommandMenu.put(new Integer(9), "getFirmwareVersionString");
      hashGetCommandMenu.put(new Integer(10), "getFreeRAM");
      hashGetCommandMenu.put(new Integer(11), "getLastError");
      hashGetCommandMenu.put(new Integer(12), "getLoadPINMode");
      hashGetCommandMenu.put(new Integer(13), "getRandomBytes");
      hashGetCommandMenu.put(new Integer(14), "getRealTimeClock");
      hashGetCommandMenu.put(new Integer(15), "getRestoreMode");
      hashGetCommandMenu.put(new Integer(16), "getPORCount");

      getCommandItemCount = 17;

      hashSetCommandMenu.put(new Integer(0), "Return to Main Menu");
      hashSetCommandMenu.put(new Integer(1), "setAppletGCMode");
      hashSetCommandMenu.put(new Integer(2), "setCommandPINMode");
      hashSetCommandMenu.put(new Integer(3), "setCommitBufferSize");
      hashSetCommandMenu.put(new Integer(4), "setCommonPIN");
      hashSetCommandMenu.put(new Integer(5), "setEphemeralGCMode");
      hashSetCommandMenu.put(new Integer(6), "setErrorReportingMode");
      hashSetCommandMenu.put(new Integer(7), "setExceptionMode");
      hashSetCommandMenu.put(new Integer(8), "setLoadPINMode");
      hashSetCommandMenu.put(new Integer(9), "setPIN");
      hashSetCommandMenu.put(new Integer(10), "setRestoreMode");

      setCommandItemCount = 11;

      hashOtherCommandMenu.put(new Integer(0), "Return to Main Menu");
      hashOtherCommandMenu.put(new Integer(1), "masterErase");
      hashOtherCommandMenu.put(new Integer(2), "loadApplet");
      hashOtherCommandMenu.put(new Integer(3), "process");
      hashOtherCommandMenu.put(new Integer(4), "select");
      hashOtherCommandMenu.put(new Integer(5), "deleteAppletByAID");
      hashOtherCommandMenu.put(new Integer(6), "deleteAppletByNumber");
      hashOtherCommandMenu.put(new Integer(7), "deleteSelectedApplet");

      otherCommandItemCount = 8;

      return;
   }

   /** getMenuChoice - retrieve menu choice from the user  */
   static int getMenuChoice (Hashtable menu, int count)
   {
      int     choice      = 0;

      while (true)
      {
         System.out.println("********************************************");

         for (int i = 0; i < count; i++)
            System.out.println(i + ". " + menu.get(new Integer(i)));

         System.out.print("Please enter your choice: ");

         // change input into integer number
         choice = getNumber();

         if (menu.get(new Integer(choice)) == null)
         {
             System.out.println("Invalid menu choice");
         }
         else
             break;
       }

      return choice;
   }

   /** send command to Java iButton */
   static void sendCommand (String cmdStr)
   {
      boolean displayASCII  = false;
      boolean displayNumber = false;

      capdu  = null;
      rapdu  = null;
      fError = false;

      String  inputString  = null;
      int     inputValue   = 0;
      int     numRepeat    = 1;
      int     savedRunTime = 0;
      int     newRunTime   = 0;
      byte[]  apduHeader   = new byte [4];
      byte[]  apduData     = null;

      // get inputs from user
      if (cmdStr.equals("setPIN"))
      {
         while (true)
         {
            System.out.print("Please enter PIN:");

            inputString = getString();

            System.out.print("Please enter PIN again:");

            if (!inputString.equals(getString().trim()))
               System.out.println(" PIN does not match.  Try again.");
            else
               break;
         }

         String password;

         password = inputString;

         System.out.println("password = " + password);
         owc16.setPIN(password);

         return;
      }

      if (cmdStr.equals("process"))
      {
         savedRunTime = owc16.getRunTime();

         while (true)
         {
            System.out.print("Please enter Process Run Time (0-15): ");

            newRunTime = getNumber();

            if ((newRunTime < 0) || (newRunTime > MAX_RUN_TIME))
               System.out.println("Invalid Run Time.  ");
            else
               break;
         }

         while (true)
         {
            System.out.print(
               "Please enter process CommandAPDU header byte array: ");

            apduHeader = parseByteString(getString());

            if (apduHeader.length != 4)
               System.out.println(
                  "Invalid CommandAPDU header. Header should be of 4 bytes");
            else
               break;
         }

         while (true)
         {
            System.out.print(
               "Please enter process CommandAPDU body byte array (enter NONE if not applicable): ");

            inputString = getString();

            if (!inputString.trim().toUpperCase().equals("NONE"))
            {
               apduData   = parseByteString(inputString);
               break;
            }
         }
      }
      else
      {
         if (cmdStr.equals("loadApplet") || cmdStr.equals("select")
                 || cmdStr.equals("deleteAppletByAID")
                 || cmdStr.equals("setCommonPIN"))
         {
            System.out.print("Please enter a string parameter: ");

            inputString = getString();
         }
         else if (cmdStr.equals("deleteAppletByNumber")
                  || cmdStr.equals("getAIDByNumber")
                  || cmdStr.equals("getRandomBytes")
                  || cmdStr.equals("setAppletGCMode")
                  || cmdStr.equals("setCommandPINMode")
                  || cmdStr.equals("setCommitBufferSize")
                  || cmdStr.equals("setEphemeralGCMode")
                  || cmdStr.equals("setErrorReportingMode")
                  || cmdStr.equals("setExceptionMode")
                  || cmdStr.equals("setLoadPINMode")
                  || cmdStr.equals("setRestoreMode"))
         {
            System.out.print("Please enter a Numerical Parameter: ");

            inputValue = getNumber();
         }
      }

      System.out.print("Please enter number of times to repeat : ");

      numRepeat = getNumber();

      if (numRepeat < 1)
         numRepeat = 1;

      System.out.print("Repeat count: ");

      for (int i = 0; i < numRepeat; i++)
      {
         rapdu = null;

         System.out.print(i + " ");

         // execute command
         try
         {
            if (cmdStr.equals("masterErase"))
            {
               rapdu = owc16.masterErase();
            }
            else if (cmdStr.equals("loadApplet"))
            {

               // turn Garbage Collector on first to reclaim unused memory
               rapdu = owc16.setAppletGCMode(1);

               File appletFile = new File(inputString);

               if (appletFile.exists())
               {
                  rapdu = null;
                  rapdu = owc16.loadApplet(appletFile.getName(),
                                           appletFile.getParent(),
                                           appletFile.getName());
               }
               else
               {
                  fError = true;

                  System.out.println("Applet file " + inputString
                                     + " does not exist.");
               }
            }
            else if (cmdStr.equals("process"))
            {

               // turn Garbage Collector on first to reclaim unused memory
               rapdu = owc16.setAppletGCMode(1);
               rapdu = null;

               owc16.setRunTime(newRunTime);

               capdu = new CommandAPDU(apduHeader [0], apduHeader [1],
                                       apduHeader [2], apduHeader [3],
                                       apduData);
               rapdu = owc16.process(capdu);
            }
            else if (cmdStr.equals("select"))
            {
               rapdu = owc16.select(inputString);
            }
            else if (cmdStr.equals("deleteAppletByAID"))
            {
               rapdu = owc16.deleteAppletByAID(inputString);
            }
            else if (cmdStr.equals("deleteAppletByNumber"))
            {
               rapdu = owc16.deleteAppletByNumber(inputValue);
            }
            else if (cmdStr.equals("deleteSelectedApplet"))
            {
               rapdu = owc16.deleteSelectedApplet();
            }
            else if (cmdStr.equals("getAIDByNumber"))
            {
               displayASCII = true;
               rapdu        = owc16.getAIDByNumber(inputValue);
            }
            else if (cmdStr.equals("getAppletGCMode"))
            {
               rapdu = owc16.getAppletGCMode();
            }
            else if (cmdStr.equals("getATR"))
            {
               rapdu = owc16.getATR();
            }
            else if (cmdStr.equals("getCommandPINMode"))
            {
               rapdu = owc16.getCommandPINMode();
            }
            else if (cmdStr.equals("getCommitBufferSize"))
            {
               displayNumber = true;
               rapdu         = owc16.getCommitBufferSize();
            }
            else if (cmdStr.equals("getEphemeralGCMode"))
            {
               rapdu = owc16.getEphemeralGCMode();
            }
            else if (cmdStr.equals("getErrorReportingMode"))
            {
               rapdu = owc16.getErrorReportingMode();
            }
            else if (cmdStr.equals("getExceptionMode"))
            {
               rapdu = owc16.getExceptionMode();
            }
            else if (cmdStr.equals("getFirmwareVersionString"))
            {
               displayASCII = true;
               rapdu        = owc16.getFirmwareVersionString();
            }
            else if (cmdStr.equals("getFreeRAM"))
            {
               rapdu = owc16.getFreeRAM();
            }
            else if (cmdStr.equals("getLastError"))
            {
               rapdu = owc16.getLastError();
            }
            else if (cmdStr.equals("getLoadPINMode"))
            {
               rapdu = owc16.getLoadPINMode();
            }
            else if (cmdStr.equals("getRandomBytes"))
            {
               rapdu = owc16.getRandomBytes(inputValue);
            }
            else if (cmdStr.equals("getRealTimeClock"))
            {
               rapdu = owc16.getRealTimeClock();
            }
            else if (cmdStr.equals("getRestoreMode"))
            {
               rapdu = owc16.getRestoreMode();
            }
            else if (cmdStr.equals("getPORCount"))
            {
               rapdu = owc16.getPORCount();
            }
            else if (cmdStr.equals("setAppletGCMode"))
            {
               rapdu = owc16.setAppletGCMode(inputValue);
            }
            else if (cmdStr.equals("setCommandPINMode"))
            {
               rapdu = owc16.setCommandPINMode(inputValue);
            }
            else if (cmdStr.equals("setCommitBufferSize"))
            {
               rapdu = owc16.setCommitBufferSize(inputValue);
            }
            else if (cmdStr.equals("setCommonPIN"))
            {
               rapdu = owc16.setCommonPIN(inputString);

               if (rapdu.getSW() == COMMAND_SUCCESS)
               {
                  owc16.setPIN(inputString);   // update owc16 copy of password

                  /*
                           // write password to a file
                                FileOutputStream out = new FileOutputStream("password.txt");
                                out.write(inputString.getBytes());
                                out.close();
                  */
               }
            }
            else if (cmdStr.equals("setEphemeralGCMode"))
            {
               rapdu = owc16.setEphemeralGCMode(inputValue);
            }
            else if (cmdStr.equals("setErrorReportingMode"))
            {
               rapdu = owc16.setErrorReportingMode(inputValue);
            }
            else if (cmdStr.equals("setExceptionMode"))
            {
               rapdu = owc16.setExceptionMode(inputValue);
            }
            else if (cmdStr.equals("setLoadPINMode"))
            {
               rapdu = owc16.setLoadPINMode(inputValue);
            }
            else if (cmdStr.equals("setRestoreMode"))
            {
               rapdu = owc16.setRestoreMode(inputValue);
            }
            else
            {
               fError = true;

               System.out.println("Invalid Command Type: " + cmdStr);
            }
         }
         catch (Exception e)
         {
            if (cmdStr.equals("process"))
               owc16.setRunTime(savedRunTime);   // restore run time value

            fError = true;

            printException(e);
         }

         if ((rapdu == null) || (rapdu.getSW() != COMMAND_SUCCESS) || fError)
         {
            System.out.println("Error occurs, abort loop.");

            break;
         }
      }   // for numRepeat

      System.out.println();   // write EOL

      byte[] data;

      // Output CommandAPDU and ResponseAPDU
      if (!fError)
      {
         if (rapdu != null)
         {
            data     = new byte [2];
            data [0] = rapdu.getSW1();
            data [1] = rapdu.getSW2();

            System.out.println("Status Word = " + printByteToHex(data)
                               + getSWString(rapdu.getSW()));

            if (rapdu.getData() != null)
            {
               data = rapdu.getData();

               if (displayASCII)
               {
                  System.out.println("Data in ASCII:");
                  System.out.println(printByteToASCII(data));
               }
               else if (displayNumber)
               {
                  System.out.println("Data in Numberic Value:");
                  System.out.println(printByteToInt(data));
               }
               else
               {
                  System.out.println("Data in bytes:");
                  System.out.println(printByteToHex(data));
               }
            }
         }
         else
         {
            System.out.println("NO Response APDU received.");
         }
      }

      return;
   }

   /**
    * Gets ResponseAPDU status word meaning.
    *
    * @return string representing meaning of status word.
    *
    */
   static String getSWString (int sw)
   {
      switch (sw)
      {

         case COMMAND_SUCCESS :
            return " Success.";
         case 0x6100 :
            return " Failure. Response Bytes Remaining";
         case 0x6301 :
            return "Success Packet";
         case 0x6400 :
            return " Failure. Insufficient Memory.";
         case 0x6681 :
            return " Failure. Bad Master PIN";
         case 0x6700 :
            return " Failure. Wrong Length";
         case 0x6901 :
            return " Failure. Invalid AID Length.";
         case 0x6902 :
            return " Failure. Invalid API Version.";
         case 0x6903 :
            return " Failure. Invalid Password.";
         case 0x6904 :
            return " Failure. Invalid Signature Length.";
         case 0x6905 :
            return " Failure. Hash Corruption.";
         case 0x6906 :
            return " Failure. Hash Failure.";
         case 0x6982 :
            return " Failure. Invalid Signature.";
         case 0x6984 :
            return " Failure. Data Invalid";
         case 0x6985 :
            return " Failure. Conditions Of Use Not Satisfied";
         case 0x6A80 :
            return " Failure. Wrong Data";
         case 0x6A81 :
            return " Failure. Function Not Supported";
         case 0x6A82 :
            return " Failure. Unable to Select Applet";
         case 0x6A84 :
            return " Failure. Class Length Overrun.";
         case 0x6A86 :
            return " Failure. Invalid Loader Command.";
         case 0x6A87 :
            return " Failure. Invalid Packet.";
         case 0x6B00 :
            return " Failure. Incorrect Parameters (P1,P2)";
         case 0x6C00 :
            return " Failure. Invalid Expected Length";
         case 0x6D00 :
            return " Failure. INS Value Not Supported";
         case 0x6F00 :
            return " Failure. Uncaught Exception";
         case 0x8450 :
            return " Failure. Unable to Find Applet";
         case 0x8453 :
            return " Failure.  Applet not found.";
         default :
            return " UNKNOWN STATUS WORD";
      }
   }

   /**
    * Retrieve user input from the console.
    *
    * @return numberic value entered from the console.
    *
    */
   static int getNumber ()
   {
      int     value   = -1;

      while (true)
      {
         try
         {
            String str = getString();

            value   = Integer.parseInt(str);
            break;
         }
         catch (NumberFormatException e)
         {
            System.out.println("Invalid Numeric Value: " + e.toString());
            System.out.print("Please enter value again: ");
         }
         catch (Exception e)
         {
            printException(e);
         }
      }

      return value;
   }

   private static DataInputStream dis = new DataInputStream(System.in);

   /**
    * Retrieve user input from the console.
    *
    * @return string entered from the console.
    *
    */
   private static String getString ()
   {
      try
      {
        return dis.readLine();
      }
      catch (java.io.IOException e)
      {
         System.out.println("Error in reading from console: " + e);
      }

      return "";
   }

   static String printByteToASCII (byte data)
   {
      byte[] newData = new byte [1];

      newData [0] = data;

      return printByteToASCII(newData);
   }   // print ByteToASCII()

   static String printByteToASCII (byte[] data)
   {
      String printStr = "";

      for (int i = 0; i < data.length; i++)
         printStr += ( char ) data [i];

      return printStr;
   }   // print ByteToASCII()

   static long printByteToInt (byte data)
   {
      byte[] newData = new byte [1];

      newData [0] = data;

      return printByteToInt(newData);
   }   // print ByteToInt()

   /**
   data[0] contains the least significant byte
   data[data.length-1] contains the most significant byte
   */
   static long printByteToInt (byte[] data)
   {
      long val = 0;

      for (int i = 0; i < data.length; i++)
         val = (data [i] << i * 8) + val;

      return val;
   }   // print ByteToInt()

   private static String printByteToHex (byte data)
   {
      byte[] newData = new byte [1];

      newData [0] = data;

      return printByteToHex(newData);
   }   // printByteToHex

   private static String printByteToHex (byte[] data)
   {
      String printStr = "";

      for (int i = 0; i < data.length; i++)
      {
         if ((data [i] & 0xFF) < 0x10)
            printStr += "0";

         printStr +=
            Integer.toHexString(( int ) (data [i] & 0xFF)).toUpperCase()
            + " ";
      }

      return printStr;
   }   // printByteToHex

   /**  convert input to hexidecimal value*/
   static int hexDigitValue (char c)
   {
      int value = Character.digit(c, 16);

      if (value == -1)
      {
         throw new StringIndexOutOfBoundsException("Invalid Hex value: " + c);
      }

      return value;
   }

   /** parse byte string into a byte array */
   static byte[] parseByteString (String str)
   {

      // data are entered in "xx xx xx xx" format
      String dataStr  = str.trim();
      int    dataLen  = dataStr.length();
      byte[] buf      = new byte [dataLen];
      int    bufLen   = 0;
      int    curPos   = 0;
      int    savedPos = 0;
      int    count    = 0;
      char   c;

      while (curPos < dataLen)
      {
         c = dataStr.charAt(curPos);

         if (!Character.isWhitespace(c))
         {
            savedPos = curPos;
            count    = 1;

            while ((curPos < dataLen - 1)
                   && (!Character.isWhitespace(dataStr.charAt(++curPos))))
            {
               count++;
            }

            if (count > 2)
               throw new StringIndexOutOfBoundsException(
                  "Invalid Byte String: " + str);

            if (curPos != dataLen - 1)
               curPos--;

            if (count == 1)   // only 1 digit entered
               buf [bufLen++] = ( byte ) hexDigitValue(c);
            else
               buf [bufLen++] =
                  ( byte ) ((hexDigitValue(c) << 4)
                            | ( byte ) hexDigitValue(dataStr.charAt(curPos)));
         }                    // if

         curPos++;
      }                       // while

      byte[] data = new byte [bufLen];

      System.arraycopy(buf, 0, data, 0, bufLen);

      return data;
   }

   /** print out Exception stack trace */
   static void printException (Exception e)
   {
      System.out.println("***** EXCEPTION *****");
      e.printStackTrace();
   }

   /** clean up before exiting program */
   static void cleanup ()
   {
      try
      {
         if (adapter != null)
         {
            adapter.endExclusive(); // end exclusive use of adapter
            adapter.freePort();     // free port used by adapter
         }
      }
      catch (Exception e)
      {
         printException(e);
      }

      return;
   }
}
