image
 
image
XFile.java


/*::.
==================================================================================================================================
=================================================¦ Copyright © 2002 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       XFile.java
Originator: Allen Baker (2002.12.19 23:05)
LayoutRev:  5
================================================================================================================================== */



/*.
==========================================================================================
Package
------------------------------------------------------------------------------------------ */
package cosmicabyss.com.lib;



/*.
==========================================================================================
Imports
------------------------------------------------------------------------------------------ */
import java.io.*;
import java.net.*;
import java.util.*;
import java.nio.channels.FileChannel;
import java.security.*;
import java.lang.reflect.*;



/*::
======================================================================================================================== *//**
This class provides methods to supplement the capabilities provided by a class that is part of the JDK, J2EE, or some
other 3rd party library. The extensions are provided by way of this subclass of the 3rd party class.<P>

In particular, this class extends the capabilities of the File class. Like the File class, this class provides methods
that manipulate and get parts of a file's name or path and retrieve other information about a file that the File class
does not. Unlike the File class, this class also provides methods for modifying the content of the file; among others,
this includes methods that encrypt, decrypt, copy, and append the file. The class provides a number of other
miscellaneous file management methods as well.

<P>
   <DL>
      <DT>
         <B>
            Example usage:
         </B>
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  no example provided
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="XFile.java.html">
               xFile.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class XFile extends File
   {



   /*:                                    :METHOD:000:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the XFile class.

   <P><B>Implementation: </B><A HREF="XFile.java.html#000">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> XFile(File parent, Type child)
      {
      super(parent, UString.toString(child));
      }



   /*:                                    :METHOD:001:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the XFile class.

   <P><B>Implementation: </B><A HREF="XFile.java.html#001">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> XFile(Type pathname)
      {
      super(UString.toString(pathname));
      }



   /*:                                    :METHOD:002:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the XFile class.

   <P><B>Implementation: </B><A HREF="XFile.java.html#002">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> XFile(Type1 parent, Type2 child)
      {
      super(UString.toString(parent),UString.toString(child));
      }



   /*:                                    :METHOD:003:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the XFile class.

   <P><B>Implementation: </B><A HREF="XFile.java.html#003">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XFile(URI uri)
      {
      super(uri);
      }



   /*:                                    :METHOD:004:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the XFile class.

   <P><B>Implementation: </B><A HREF="XFile.java.html#004">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XFile(File file) throws Exception
      {
      super(file.getCanonicalPath());
      }



   /*:                                    :METHOD:005:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method encrypts the file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#005">View source</A>

   @param
      pPassword is the password that will be used to encrypt and/or decrypt the file.
   @param
      pIterations is the number of times the file will be run through the encryption and/or decryption
      algorithm when the encrypt and/or decrypt method is called. If this value is 1 or less, the file
      will be run through 1 time.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void encrypt(Password pPassword, int pIterations) throws Exception
      {
      /*.
      ==========================================================================================
      This method makes no sense on directory files, this must be a normal file
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory()) return;
      /*.
      ==========================================================================================
      Set up the FileCipher object to encrypt this file.
      ------------------------------------------------------------------------------------------ */
      if (iCipher == null)
         {
         iCipher = new FileCipher
            (
            this.getCanonicalPath(),
            pPassword,
            pIterations
            );
         }
      /*.
      ==========================================================================================
      Encrypt the file
      ------------------------------------------------------------------------------------------ */
      iCipher.encrypt();
      }



   /*:                                    :METHOD:006:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method encrypts the file the FileCipher's default number of times.

   <P><B>Implementation: </B><A HREF="XFile.java.html#006">View source</A>

   @param
      pPassword is the password that will be used to encrypt and/or decrypt the file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void encrypt(Password pPassword) throws Exception
      {
      /*.
      ==========================================================================================
      This method makes no sense on directory files, this must be a normal file
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory()) return;
      /*.
      ==========================================================================================
      Set up the FileCipher object to encrypt this file.
      ------------------------------------------------------------------------------------------ */
      if (iCipher == null)
         {
         iCipher = new FileCipher
            (
            this.getCanonicalPath(),
            pPassword
            );
         }
      /*.
      ==========================================================================================
      Encrypt the file
      ------------------------------------------------------------------------------------------ */
      iCipher.encrypt();
      }



   /*:                                    :METHOD:007:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method encrypts the file using the FileCipher's default password.

   <P><B>Implementation: </B><A HREF="XFile.java.html#007">View source</A>

   @param
      pIterations is the number of times the file will be run through the encryption and/or decryption
      algorithm when the encrypt and/or decrypt method is called. If this value is 1 or less, the file
      will be run through 1 time.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void encrypt(int pIterations) throws Exception
      {
      /*.
      ==========================================================================================
      Since no password was specified, use the default password
      ------------------------------------------------------------------------------------------ */
      Password  password = new Password();
      /*.
      ==========================================================================================
      Encrypt the file
      ------------------------------------------------------------------------------------------ */
      this.encrypt(password,pIterations);
      }



   /*:                                    :METHOD:008:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method encrypts the file the FileCipher's default number of times using the FileCipher's
   default password.

   <P><B>Implementation: </B><A HREF="XFile.java.html#008">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void encrypt() throws Exception
      {
      /*.
      ==========================================================================================
      Since no password was specified, use the default password
      ------------------------------------------------------------------------------------------ */
      Password  password = new Password();
      /*.
      ==========================================================================================
      Encrypt the file
      ------------------------------------------------------------------------------------------ */
      this.encrypt(password);
      }



   /*:                                    :METHOD:009:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method encrypts the file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#009">View source</A>

   @param
      pPwd is the password XString that will be used to encrypt and/or decrypt the file.
   @param
      pPwdCount is the number of times the password XString is hashed before it is used to encrypt
      and/or decrypt the file.
   @param
      pIterations is the number of times the file will be run through the encryption and/or decryption
      algorithm when the encrypt and/or decrypt method is called. If this value is 1 or less, the file
      will be run through 1 time.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> void encrypt(Type pPwd, int pPwdCount, int pIterations) throws Exception
      {
      /*.
      ==========================================================================================
      Create the password
      ------------------------------------------------------------------------------------------ */
      Password  password = (new Password(UString.toString(pPwd))).iterate(pPwdCount);
      /*.
      ==========================================================================================
      Encrypt the file
      ------------------------------------------------------------------------------------------ */
      this.encrypt(password,pIterations);
      }



   /*:                                    :METHOD:010:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method encrypts the file after hashing the password XString the Password object's default
   number of times.

   <P><B>Implementation: </B><A HREF="XFile.java.html#010">View source</A>

   @param
      pPwd is the password XString that will be used to encrypt and/or decrypt the file.
   @param
      pIterations is the number of times the file will be run through the encryption and/or decryption
      algorithm when the encrypt and/or decrypt method is called. If this value is 1 or less, the file
      will be run through 1 time.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> void encrypt(Type pPwd, int pIterations) throws Exception
      {
      /*.
      ==========================================================================================
      Create the password
      ------------------------------------------------------------------------------------------ */
      Password  password = new Password(UString.toString(pPwd));
      /*.
      ==========================================================================================
      Encrypt the file
      ------------------------------------------------------------------------------------------ */
      this.encrypt(password,pIterations);
      }



   /*:                                    :METHOD:011:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method encrypts the file the FileCipher's default number of times after hashing the password
   xString the Password object's default number of times.

   <P><B>Implementation: </B><A HREF="XFile.java.html#011">View source</A>

   @param
      pPwd is the password XString that will be used to encrypt and/or decrypt the file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> void encrypt(Type pPwd) throws Exception
      {
      /*.
      ==========================================================================================
      Create the password
      ------------------------------------------------------------------------------------------ */
      Password  password = new Password(UString.toString(pPwd));
      /*.
      ==========================================================================================
      Encrypt the file
      ------------------------------------------------------------------------------------------ */
      this.encrypt(password);
      }



   /*:                                    :METHOD:012:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method encrypts the file using the Password object's default password XString.

   <P><B>Implementation: </B><A HREF="XFile.java.html#012">View source</A>

   @param
      pPwdCount is the number of times the password XString is hashed before it is used to encrypt
      and/or decrypt the file.
   @param
      pIterations is the number of times the file will be run through the encryption and/or decryption
      algorithm when the encrypt and/or decrypt method is called. If this value is 1 or less, the file
      will be run through 1 time.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void encrypt(int pPwdCount, int pIterations) throws Exception
      {
      /*.
      ==========================================================================================
      Create the password
      ------------------------------------------------------------------------------------------ */
      Password  password = (new Password()).iterate(pPwdCount);
      /*.
      ==========================================================================================
      Encrypt the file
      ------------------------------------------------------------------------------------------ */
      this.encrypt(password,pIterations);
      }



   /*:                                    :METHOD:013:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method decrypts the file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#013">View source</A>

   @param
      pPassword is the password that will be used to encrypt and/or decrypt the file.
   @param
      pIterations is the number of times the file will be run through the encryption and/or decryption
      algorithm when the decrypt and/or decrypt method is called. If this value is 1 or less, the file
      will be run through 1 time.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void decrypt(Password pPassword, int pIterations) throws Exception
      {
      /*.
      ==========================================================================================
      This method makes no sense on directory files, this must be a normal file
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory()) return;
      /*.
      ==========================================================================================
      Set up the FileCipher object to decrypt this file.
      ------------------------------------------------------------------------------------------ */
      if (iCipher == null)
         {
         iCipher = new FileCipher
            (
            this.getCanonicalPath(),
            pPassword,
            pIterations
            );
         }
      /*.
      ==========================================================================================
      Decrypt the file
      ------------------------------------------------------------------------------------------ */
      iCipher.decrypt();
      }



   /*:                                    :METHOD:014:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method decrypts the file the FileCipher's default number of times.

   <P><B>Implementation: </B><A HREF="XFile.java.html#014">View source</A>

   @param
      pPassword is the password that will be used to encrypt and/or decrypt the file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void decrypt(Password pPassword) throws Exception
      {
      /*.
      ==========================================================================================
      This method makes no sense on directory files, this must be a normal file
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory()) return;
      /*.
      ==========================================================================================
      Set up the FileCipher object to decrypt this file.
      ------------------------------------------------------------------------------------------ */
      if (iCipher == null)
         {
         iCipher = new FileCipher
            (
            this.getCanonicalPath(),
            pPassword
            );
         }
      /*.
      ==========================================================================================
      Decrypt the file
      ------------------------------------------------------------------------------------------ */
      iCipher.decrypt();
      }



   /*:                                    :METHOD:015:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method decrypts the file using the FileCipher's default password.

   <P><B>Implementation: </B><A HREF="XFile.java.html#015">View source</A>

   @param
      pIterations is the number of times the file will be run through the encryption and/or decryption
      algorithm when the encrypt and/or decrypt method is called. If this value is 1 or less, the file
      will be run through 1 time.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void decrypt(int pIterations) throws Exception
      {
      /*.
      ==========================================================================================
      Since no password was specified, use the default password
      ------------------------------------------------------------------------------------------ */
      Password  password = new Password();
      /*.
      ==========================================================================================
      Decrypt the file
      ------------------------------------------------------------------------------------------ */
      this.decrypt(password,pIterations);
      }



   /*:                                    :METHOD:016:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method decrypts the file the FileCipher's default number of times using the FileCipher's
   default password.

   <P><B>Implementation: </B><A HREF="XFile.java.html#016">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void decrypt() throws Exception
      {
      /*.
      ==========================================================================================
      Since no password was specified, use the default password
      ------------------------------------------------------------------------------------------ */
      Password  password = new Password();
      /*.
      ==========================================================================================
      Decrypt the file
      ------------------------------------------------------------------------------------------ */
      this.decrypt(password);
      }



   /*:                                    :METHOD:017:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method decrypts the file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#017">View source</A>

   @param
      pPwd is the password XString that will be used to encrypt and/or decrypt the file.
   @param
      pPwdCount is the number of times the password XString is hashed before it is used to decrypt
      and/or decrypt the file.
   @param
      pIterations is the number of times the file will be run through the encryption and/or decryption
      algorithm when the encrypt and/or decrypt method is called. If this value is 1 or less, the file
      will be run through 1 time.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> void decrypt(Type pPwd, int pPwdCount, int pIterations) throws Exception
      {
      /*.
      ==========================================================================================
      Create the password
      ------------------------------------------------------------------------------------------ */
      Password  password = (new Password(UString.toString(pPwd))).iterate(pPwdCount);
      /*.
      ==========================================================================================
      Decrypt the file
      ------------------------------------------------------------------------------------------ */
      this.decrypt(password,pIterations);
      }



   /*:                                    :METHOD:018:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method decrypts the file after hashing the password XString the Password object's default
   number of times.

   <P><B>Implementation: </B><A HREF="XFile.java.html#018">View source</A>

   @param
      pPwd is the password XString that will be used to encrypt and/or decrypt the file.
   @param
      pIterations is the number of times the file will be run through the encryption and/or decryption
      algorithm when the encrypt and/or decrypt method is called. If this value is 1 or less, the file
      will be run through 1 time.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> void decrypt(Type pPwd, int pIterations) throws Exception
      {
      /*.
      ==========================================================================================
      Create the password
      ------------------------------------------------------------------------------------------ */
      Password  password = new Password(UString.toString(pPwd));
      /*.
      ==========================================================================================
      Decrypt the file
      ------------------------------------------------------------------------------------------ */
      this.decrypt(password,pIterations);
      }



   /*:                                    :METHOD:019:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method decrypts the file the FileCipher's default number of times after hashing the password
   xString the Password object's default number of times.

   <P><B>Implementation: </B><A HREF="XFile.java.html#019">View source</A>

   @param
      pPwd is the password XString that will be used to encrypt and/or decrypt the file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> void decrypt(Type pPwd) throws Exception
      {
      /*.
      ==========================================================================================
      Create the password
      ------------------------------------------------------------------------------------------ */
      Password  password = new Password(UString.toString(pPwd));
      /*.
      ==========================================================================================
      Decrypt the file
      ------------------------------------------------------------------------------------------ */
      this.decrypt(password);
      }



   /*:                                    :METHOD:020:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method decrypts the file using the Password object's default password XString.

   <P><B>Implementation: </B><A HREF="XFile.java.html#020">View source</A>

   @param
      pPwdCount is the number of times the password XString is hashed before it is used to decrypt
      and/or decrypt the file.
   @param
      pIterations is the number of times the file will be run through the encryption and/or decryption
      algorithm when the encrypt and/or decrypt method is called. If this value is 1 or less, the file
      will be run through 1 time.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void decrypt(int pPwdCount, int pIterations) throws Exception
      {
      /*.
      ==========================================================================================
      Create the password
      ------------------------------------------------------------------------------------------ */
      Password  password = (new Password()).iterate(pPwdCount);
      /*.
      ==========================================================================================
      Decrypt the file
      ------------------------------------------------------------------------------------------ */
      this.decrypt(password,pIterations);
      }



   /*:                                    :METHOD:021:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the canonical path name of the directory that this file is in - always without a
   trailing '\'.

   If the file is in the root directory, then on Windows this method returns a string containing the
   drive specifier such as "C:". On Unix, it would probably return the empty string.

   This method is needed because Java's getParent*() methods work from the name of the file given to
   the constructor and do not necessarily return a complete parent path. This code shows the problem
   with Java's standard methods:

      <BLOCKQUOTE>
         <PRE id="unindent">
            *   // Get the parent of a relative filename path
            *   File file = new File("Ex1.java");
            *   XString parentPath = file.getParent();     // null
            *   File parentDir = file.getParentFile();     // null
            *
            *   // Get the parents of an absolute filename path
            *   file = new File("D:/almanac/Ex1.java");
            *   parentPath = file.getParent();             // D:\almanac
            *   parentDir = file.getParentFile();          // D:\almanac
         </PRE>
      </BLOCKQUOTE>

   <P><B>Implementation: </B><A HREF="XFile.java.html#021">View source</A>

   @return
      The canonical path name of the directory that this file is in - always without a trailing '\'.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString getCanonicalParent() throws Exception
      {
      XString  result = new XString((new File(this.getCanonicalPath())).getParent());

      result = result.trimRight("\\");
      return result;
      }



   /*:                                    :METHOD:022:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the extension part of the file name (the part of the file name that follows the
   last '.' character in the file name.

   <P><B>Implementation: </B><A HREF="XFile.java.html#022">View source</A>

   @return
      The extension part of the file name (the part of the file name that follows the last '.'
      character in the file name, or the empty string if this file's extension is empty
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString getExtension()
      {
      XString  fileName = new XString(this.getName());
      if (fileName.equals(""))
         {
         return XString.EMPTY;
         }
      int  lastIndex = fileName.lastIndexOf('.');
      if (lastIndex == (-1))
         {
         return XString.EMPTY;
         }
      return fileName.substring(lastIndex+1);
      }



   /*:                                    :METHOD:023:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the name of the file or directory denoted by this abstract pathname, minus its
   extension. This is just the last name in the pathname's name sequence with the extension and it's
   delimiting '.' removed. If the pathname's name sequence is empty, then the empty string is returned.

   <P><B>Implementation: </B><A HREF="XFile.java.html#023">View source</A>

   @return
      The name of the file or directory denoted by this abstract pathname, minus its extension. This is
      just the last name in the pathname's name sequence with the extension and it's delimiting '.'
      removed. If the pathname's name sequence is empty, then the empty string is returned.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString getExtensionlessName()
      {
      XString  fileName = new XString(this.getName());

      if (fileName.equals(""))
         {
         return XString.EMPTY;
         }

      if (fileName.endsWith("."))
         {
         fileName = fileName.trimRight(".");
         return fileName;
         }

      XString  extension = this.getExtension();
      if (extension.equals(""))
         {
         return fileName;
         }

      fileName = fileName.trimRight("." + extension);
      return fileName;
      }



   /*:                                    :METHOD:024:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the canonical name of the file or directory denoted by this abstract pathname,
   minus its extension. This is the canonical path with the extension and it's delimiting '.' removed.
   If the pathname's name sequence is empty, then the empty string is returned.

   <P><B>Implementation: </B><A HREF="XFile.java.html#024">View source</A>

   @return
      This method returns the canonical name of the file or directory denoted by this abstract
      pathname, minus its extension. This is the canonical path with the extension and it's delimiting
      '.' removed. If the pathname's name sequence is empty, then the empty string is returned.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString getExtensionlessCanonicalPath() throws Exception
      {
      XString  pathName = new XString(this.getCanonicalPath());

      if (pathName.equals(""))
         {
         return XString.EMPTY;
         }

      if (pathName.endsWith("."))
         {
         pathName = pathName.trimRight(".");
         return pathName;
         }

      XString  extension = this.getExtension();
      if (extension.equals(""))
         {
         return pathName;
         }

      pathName = pathName.trimRight("." + extension);
      return pathName;
      }



   /*:                                    :METHOD:025:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the canonical name of the file or directory denoted by this abstract pathname,
   minus its drive specifier. This is the canonical path with the drive and it's delimiting ':'
   removed. If the pathname's name sequence is empty, then the empty string is returned.

   <P><B>Implementation: </B><A HREF="XFile.java.html#025">View source</A>

   @return
      This method returns the canonical name of the file or directory denoted by this abstract
      pathname, minus its drive specifier. This is the canonical path with the drive and it's
      delimiting ':' removed. If the pathname's name sequence is empty, then the empty string is
      returned.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString getDrivelessCanonicalPath() throws Exception
      {
      XString  pathName = new XString(this.getCanonicalPath());

      if (pathName.equals(""))
         {
         return XString.EMPTY;
         }

      ArrayList<XString>  tokens = pathName.tokenizedString(":");
      if (tokens.size() < 1)
         {
         return XString.EMPTY;
         }

      return tokens.get(tokens.size()-1);
      }



   /*:                                    :METHOD:026:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the canonical name of the parent of the file or directory denoted by this
   abstract pathname, minus its drive specifier. This is the canonical path with the drive and it's
   delimiting ':' removed. If the pathname's name sequence is empty, then the empty string is returned.

   <P><B>Implementation: </B><A HREF="XFile.java.html#026">View source</A>

   @return
      This method returns the canonical name of the parent of the file or directory denoted by this
      abstract pathname, minus its drive specifier. This is the canonical path with the drive and it's
      delimiting ':' removed. If the pathname's name sequence is empty, then the empty string is
      returned.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString getDrivelessParent() throws Exception
      {
      /*.
      ==========================================================================================
      Get the parent (or directory) for this file.
         For some reason, this.getParent() and this.getParentFile() do not work in here -- they
         both return null. So this kludge is to get around the problem until I can troubleshoot
         and fix it.
            File parent = this.getParentFile();
      ------------------------------------------------------------------------------------------ */
      XString  path = new XString(this.getCanonicalPath());
      path = path.trimRight(File.separator + this.getName());
      File  parent = new File(path.toString());
      /*.
      ==========================================================================================
      Return it without its drive specifier.
      ------------------------------------------------------------------------------------------ */
      if (parent == null)
         {
         return XString.EMPTY;
         }
      return (new XFile(parent)).getDrivelessCanonicalPath();
      }



   /*:                                    :METHOD:027:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method renames the file denoted by this abstract pathname.

   <P><B>Implementation: </B><A HREF="XFile.java.html#027">View source</A>

   @return
      True if and only if the renaming succeeded; false otherwise

   @param
      pNewName is the new name for the file
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> boolean renameTo(Type pNewName)
      {
      XFile  newFile = new XFile(pNewName);

      return renameTo(newFile);
      }



   /*:                                    :METHOD:028:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method renames the file denoted by this abstract pathname.

   <P><B>Implementation: </B><A HREF="XFile.java.html#028">View source</A>

   @return
      True if and only if the renaming succeeded; false otherwise

   @param
      pNewFileExt is an XFile object containing the new name for the file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean renameTo(XFile pNewFileExt)
      {
      return renameTo((File)pNewFileExt);
      }



   /*:                                    :METHOD:029:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method is a cover for File.renameTo(). The motivation for this cover method is that with this
   new version of Java (1.5.0), rename (and other file methods) sometimes don't work on the first try.
   This is because file objects that have been closed are hung onto, pending garbage collection.

   <P><B>Implementation: </B><A HREF="XFile.java.html#029">View source</A>

   @return
      True if and only if the renaming succeeded; false otherwise

   @param
      pNewFile is a File object containing the new name for the file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean renameTo(File pNewFile)
      {
      /*.
      ==========================================================================================
      HACK - I should just be able to call renameTo() here and return its result. In fact, I
      used to do just that and this method always worked fine. Now with this new version of Java
      (1.5.0), rename (and other file methods) sometimes don't work on the first try. This is
      because file objects that have been closed are hung onto, pending garbage collection. By
      suggesting garbage collection, the next time, the renameTo() usually (but not always)
      works.
      ------------------------------------------------------------------------------------------ */
      for (int i=0; i<20; i++)
         {
         try
            {
            if (super.renameTo(pNewFile))
               {
               return true;
               }
            System.gc();
            Util.sleep(200);
            }
         catch (Exception e)
            {
            XString topBanner = new XString("=========================| " + i + " |=========================");
            XString botBanner = XString.EMPTY;
            botBanner.fill(topBanner.length(), '=');

            System.out.println(topBanner);
            System.out.println(e.getMessage());
            e.printStackTrace();
            System.out.println(botBanner);
            }
         }
      return false;
      }



   /*:                                    :METHOD:030:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces a file's extension.
      <BLOCKQUOTE>
         <PRE id="unindent">
            Example: if the file's name is:

               \tuna\test.ex1

            and this method is called like this:

               XFile myFile = new XFile("\tuna\test.ex1");
               myFile.changeExtension("newExt");

            then the file would be renamed to:

               \tuna\test.newExt
         </PRE>
      </BLOCKQUOTE>

   <P><B>Implementation: </B><A HREF="XFile.java.html#030">View source</A>

   @return
      True if and only if the renaming succeeded; false otherwise

   @param
      pNewExt is the new extension for the file
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> boolean changeExtension(Type pNewExt) throws Exception
      {
      XString  fileName  = new XString(this.getCanonicalPath());
      XString  extension = this.getExtension();
      if ( ! extension.equals(""))
         {
         fileName = fileName.trimRight("." + extension);
         }
      fileName = new XString(fileName.toString() + "." + UString.toString(pNewExt));
      return renameTo(fileName.toString());
      }



   /*:                                    :METHOD:031:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the last-modified time and last-accessed times of the file to the current system
   time.

   <P><B>Implementation: </B><A HREF="XFile.java.html#031">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void touch()
      {
      long  newTime = (new java.util.Date()).getTime();
      this.setLastModified(newTime);
      this.setLastAccessed(newTime);
      }



   /*:                                    :METHOD:032:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method increments the file's last-modified time and last-accessed time by a very small amount
   that is just enough for the "xcopy /d" command to recognize that the file has a newer date-time
   stamp. This allows backup operations that are based upon xcopy to work without changing the
   date-time stamp of the file significantly enough to be noticable in a "dir" command display.

   <P><B>Implementation: </B><A HREF="XFile.java.html#032">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void lightlyTouch()
      {
      long  newTime = this.lastModified() + 1;
      this.setLastModified(newTime);
      this.setLastAccessed(newTime);
      }



   /*:                                    :METHOD:033:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   Compares two lastModified dates.
      <BLOCKQUOTE>
         <PRE id="unindent">
            => If this method returns 0, it means this XFile is THE SAME AGE as the argument XFile
            => If this method returns -1, it means this XFile is OLDER than the argument XFile
            => If this method returns 1, it means this XFile is NEWER than the argument XFile
         </PRE>
      </BLOCKQUOTE>

   <P><B>Implementation: </B><A HREF="XFile.java.html#033">View source</A>

   @return
      This method returns:
         <BLOCKQUOTE>
            <PRE id="unindent">
               =>  0, if this XFile is THE SAME AGE as the argument XFile
               => -1, if this XFile is OLDER than the argument XFile
               =>  1, if this XFile is NEWER than the argument XFile
            </PRE>
         </BLOCKQUOTE>

   @param
      pOther the XFile whose lastModified date is to be compared to this XFile's lastModified date.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public int compareLastModified(XFile pOther)
      {
      long  thisDate  = this.lastModified();
      long  otherDate = pOther.lastModified();

      if (thisDate < otherDate) return (-1);
      if (thisDate > otherDate) return ( 1);
      return (0);
      }



   /*:                                    :METHOD:034:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method changes the year, month, and day components of this file's date-time stamp. It leaves
   the remaining date-time stamp components unchanged.

   <P><B>Implementation: </B><A HREF="XFile.java.html#034">View source</A>

   @param
      Yyyy is the year to set the last modified time of the file to.
   @param
      MM is the month to set the last modified time of the file to.
   @param
      Dd is the day of the month to set the last modified time of the file to.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setLastModified(int yyyy, int MM, int dd)
      {
      /*.
      ==========================================================================================
      Get a calendar object the represents the existing date-time stamp on this file
      ------------------------------------------------------------------------------------------ */
      GregorianCalendar calendar = new GregorianCalendar();
      calendar.setTimeInMillis(this.lastModified());
      /*.
      ==========================================================================================
      Pick out the elements of the existing date-time stamp that are going to stay the same
      after the arguments to this method are appiled;
      ------------------------------------------------------------------------------------------ */
      int  HH = calendar.get(Calendar.HOUR_OF_DAY);
      int  mm = calendar.get(Calendar.MINUTE);
      int  ss = calendar.get(Calendar.SECOND);
      int  ms = calendar.get(Calendar.MILLISECOND);
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.setLastModified(yyyy,MM,dd,HH,mm,ss,ms);
      }



   /*:                                    :METHOD:035:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method changes the year, month, day, hour, minute, and second components of this file's
   date-time stamp. It leaves the remaining date-time stamp components unchanged.

   <P><B>Implementation: </B><A HREF="XFile.java.html#035">View source</A>

   @param
      Yyyy is the year to set the last modified time of the file to.
   @param
      MM is the month to set the last modified time of the file to.
   @param
      Dd is the day of the month to set the last modified time of the file to.
   @param
      HH is the hour in 24hr format to set the last modified time of the file to.
   @param
      Mm is the minute to set the last modified time of the file to.
   @param
      Ss is the second to set the last modified time of the file to.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setLastModified
      (
      int yyyy,
      int MM,
      int dd,
      int HH,
      int mm,
      int ss
      )
      {
      /*.
      ==========================================================================================
      Get a calendar object the represents the existing date-time stamp on this file
      ------------------------------------------------------------------------------------------ */
      GregorianCalendar calendar = new GregorianCalendar();
      calendar.setTimeInMillis(this.lastModified());
      /*.
      ==========================================================================================
      Pick out the elements of the existing date-time stamp that are going to stay the same
      after the arguments to this method are appiled;
      ------------------------------------------------------------------------------------------ */
      int  ms = calendar.get(Calendar.MILLISECOND);
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.setLastModified(yyyy,MM,dd,HH,mm,ss,ms);
      }



   /*:                                    :METHOD:036:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the last modified time of the file to the time represented by the year, month, day,
   hour, minute, second, and millisecond specified.

   <P><B>Implementation: </B><A HREF="XFile.java.html#036">View source</A>

   @param
      Yyyy is the year to set the last modified time of the file to.
   @param
      MM is the month to set the last modified time of the file to.
   @param
      Dd is the day of the month to set the last modified time of the file to.
   @param
      HH is the hour in 24hr format to set the last modified time of the file to.
   @param
      Mm is the minute to set the last modified time of the file to.
   @param
      Ss is the second to set the last modified time of the file to.
   @param
      Ms is the millisecond to set the last modified time of the file to.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setLastModified
      (
      int yyyy,
      int MM,
      int dd,
      int HH,
      int mm,
      int ss,
      int ms
      )
      {
      /*.
      ==========================================================================================
      Normal people number months 1..12. The numb-nuts at Sun number them 0..11. Make an
      adjustment from normal numbering to numb-nuts numbering.
      ------------------------------------------------------------------------------------------ */
      MM = MM - 1;
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      java.util.Date  date = Util.getDate(yyyy,MM,dd,HH,mm,ss);
      this.setLastModified(date.getTime() + ms);
      }



   /*:                                    :METHOD:037:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   Returns the time that the file denoted by this abstract pathname was created.

   <P><B>Implementation: </B><A HREF="XFile.java.html#037">View source</A>

   @return
      A long value representing the time the file was created, measured in milliseconds since the epoch
      (00:00:00 GMT, January 1, 1970), or 0L if the file does not exist or if an I/O error occurs.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public long created()
      {
      try
         {
         /*.
         ==========================================================================================
         Call a native method to get a string representation of the created time in this format:
         yyyy:MM:dd:HH:mm:ss:ms
         ------------------------------------------------------------------------------------------ */
         NativeMethods nm      = new NativeMethods();
         XString       created = new XString(nm.getCreated(this.getCanonicalPath()));
         /*.
         ==========================================================================================
         Break the string representation down into its individual parts
         ------------------------------------------------------------------------------------------ */
         ArrayList<XString>  tokens = created.tokenizedString(":");
         /*.
         ==========================================================================================
         Convert the individual parts from string values into integer values.
         ------------------------------------------------------------------------------------------ */
         int  yyyy = UMath.decodeDecimalInt(tokens.get(0));
         int  MM   = UMath.decodeDecimalInt(tokens.get(1));
         int  dd   = UMath.decodeDecimalInt(tokens.get(2));
         int  HH   = UMath.decodeDecimalInt(tokens.get(3));
         int  mm   = UMath.decodeDecimalInt(tokens.get(4));
         int  ss   = UMath.decodeDecimalInt(tokens.get(5));
         int  ms   = UMath.decodeDecimalInt(tokens.get(6));
         /*.
         ==========================================================================================
         Normal people number months 1..12. The numb-nuts at Sun number them 0..11. Make an
         adjustment from normal numbering to numb-nuts numbering.
         ------------------------------------------------------------------------------------------ */
         MM = MM - 1;
         /*.
         ==========================================================================================
         Use a Date object to convert the individual parts into the long that is returned
         ------------------------------------------------------------------------------------------ */
         java.util.Date  date = Util.getDate(yyyy,MM,dd,HH,mm,ss);
         return date.getTime() + (long)ms;
         }
      catch (Exception e)
         {
         return 0;
         }
      }



   /*:                                    :METHOD:038:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the created time of the file or directory named by this abstract pathname. All
   platforms support created times to the nearest second, but some provide more precision. The argument
   will be truncated to fit the supported precision. If the operation succeeds and no intervening
   operations on the file take place, then the next invocation of the created() method will return the
   (possibly truncated) time argument that was passed to this method.

   <P><B>Implementation: </B><A HREF="XFile.java.html#038">View source</A>

   @return
      True if and only if the operation succeeded; false otherwise.

   @param
      pTime - The new created time, measured in milliseconds since the epoch (00:00:00 GMT, January 1,
      1970).
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean setCreated(long pTime)
      {
      /*.
      ==========================================================================================
      Get a calendar object that represents the new created time
      ------------------------------------------------------------------------------------------ */
      GregorianCalendar calendar = new GregorianCalendar();
      calendar.setTimeInMillis(pTime);
      /*.
      ==========================================================================================
      Pick out the elements of the calendar object that will be used to set the created time.
      ------------------------------------------------------------------------------------------ */
      int  yyyy = calendar.get(Calendar.YEAR);
      int  MM   = calendar.get(Calendar.MONTH);
      int  dd   = calendar.get(Calendar.DAY_OF_MONTH);
      int  HH   = calendar.get(Calendar.HOUR_OF_DAY);
      int  mm   = calendar.get(Calendar.MINUTE);
      int  ss   = calendar.get(Calendar.SECOND);
      int  ms   = calendar.get(Calendar.MILLISECOND);
      /*.
      ==========================================================================================
      Normal people number months 1..12. The numb-nuts at Sun number them 0..11. Make an
      adjustment from numb-nuts numbering to normal numbering.
      ------------------------------------------------------------------------------------------ */
      MM = MM + 1;
      /*.
      ==========================================================================================
      Set the created time
      ------------------------------------------------------------------------------------------ */
      this.setCreated(yyyy,MM,dd,HH,mm,ss,ms);
      /*.
      ==========================================================================================
      Assume all went well. This method only returns a boolean in order to keep it true to its
      sister-method, the Java-SDK-supplied setLastModified(long).
      ------------------------------------------------------------------------------------------ */
      return true;
      }



   /*:                                    :METHOD:039:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method changes the year, month, and day components of this file's date-time stamp. It leaves
   the remaining date-time stamp components unchanged.

   <P><B>Implementation: </B><A HREF="XFile.java.html#039">View source</A>

   @param
      Yyyy is the year to set the created time of the file to.
   @param
      MM is the month to set the created time of the file to.
   @param
      Dd is the day of the month to set the created time of the file to.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setCreated(int yyyy, int MM, int dd)
      {
      /*.
      ==========================================================================================
      Get a calendar object the represents the existing date-time stamp on this file
      ------------------------------------------------------------------------------------------ */
      GregorianCalendar calendar = new GregorianCalendar();
      calendar.setTimeInMillis(this.created());
      /*.
      ==========================================================================================
      Pick out the elements of the existing date-time stamp that are going to stay the same
      after the arguments to this method are appiled;
      ------------------------------------------------------------------------------------------ */
      int  HH = calendar.get(Calendar.HOUR_OF_DAY);
      int  mm = calendar.get(Calendar.MINUTE);
      int  ss = calendar.get(Calendar.SECOND);
      int  ms = calendar.get(Calendar.MILLISECOND);
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.setCreated(yyyy,MM,dd,HH,mm,ss,ms);
      }



   /*:                                    :METHOD:040:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method changes the year, month, day, hour, minute, and second components of this file's
   date-time stamp. It leaves the remaining date-time stamp components unchanged.

   <P><B>Implementation: </B><A HREF="XFile.java.html#040">View source</A>

   @param
      Yyyy is the year to set the created time of the file to.
   @param
      MM is the month to set the created time of the file to.
   @param
      Dd is the day of the month to set the created time of the file to.
   @param
      HH is the hour in 24hr format to set the created time of the file to.
   @param
      Mm is the minute to set the created time of the file to.
   @param
      Ss is the second to set the created time of the file to.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setCreated
      (
      int yyyy,
      int MM,
      int dd,
      int HH,
      int mm,
      int ss
      )
      {
      /*.
      ==========================================================================================
      Get a calendar object the represents the existing date-time stamp on this file
      ------------------------------------------------------------------------------------------ */
      GregorianCalendar calendar = new GregorianCalendar();
      calendar.setTimeInMillis(this.created());
      /*.
      ==========================================================================================
      Pick out the elements of the existing date-time stamp that are going to stay the same
      after the arguments to this method are appiled;
      ------------------------------------------------------------------------------------------ */
      int  ms = calendar.get(Calendar.MILLISECOND);
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.setCreated(yyyy,MM,dd,HH,mm,ss,ms);
      }



   /*:                                    :METHOD:041:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the created time of the file to the time represented by the year, month, day, hour,
   minute, second, and millisecond specified.

   <P><B>Implementation: </B><A HREF="XFile.java.html#041">View source</A>

   @param
      Yyyy is the year to set the created time of the file to.
   @param
      MM is the month to set the created time of the file to.
   @param
      Dd is the day of the month to set the created time of the file to.
   @param
      HH is the hour in 24hr format to set the created time of the file to.
   @param
      Mm is the minute to set the created time of the file to.
   @param
      Ss is the second to set the created time of the file to.
   @param
      Ms is the millisecond to set the created time of the file to.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setCreated
      (
      int yyyy,
      int MM,
      int dd,
      int HH,
      int mm,
      int ss,
      int ms
      )
      {
      try
         {
         /*.
         ==========================================================================================
         Change the file time
         ------------------------------------------------------------------------------------------ */
         NativeMethods  nm = new NativeMethods();
         nm.setCreated (this.getCanonicalPath(),yyyy,MM,dd,HH,mm,ss,ms);
         }
      catch (Exception e)
         {
         }
      }



   /*:                                    :METHOD:042:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   Returns the time that the file denoted by this abstract pathname was last accessed.

   <P><B>Implementation: </B><A HREF="XFile.java.html#042">View source</A>

   @return
      A long value representing the time the file was last accessed, measured in milliseconds since the
      epoch (00:00:00 GMT, January 1, 1970), or 0L if the file does not exist or if an I/O error
      occurs.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public long lastAccessed()
      {
      try
         {
         /*.
         ==========================================================================================
         Call a native method to get a string representation of the last accessed time in this
         format: yyyy:MM:dd:HH:mm:ss:ms
         ------------------------------------------------------------------------------------------ */
         NativeMethods nm           = new NativeMethods();
         XString       lastAccessed = new XString(nm.getLastAccessed(this.getCanonicalPath()));
         /*.
         ==========================================================================================
         Break the string representation down into its individual parts
         ------------------------------------------------------------------------------------------ */
         ArrayList<XString>  tokens = lastAccessed.tokenizedString(":");
         /*.
         ==========================================================================================
         Convert the individual parts from string values into integer values.
         ------------------------------------------------------------------------------------------ */
         int  yyyy = UMath.decodeDecimalInt(tokens.get(0));
         int  MM   = UMath.decodeDecimalInt(tokens.get(1));
         int  dd   = UMath.decodeDecimalInt(tokens.get(2));
         int  HH   = UMath.decodeDecimalInt(tokens.get(3));
         int  mm   = UMath.decodeDecimalInt(tokens.get(4));
         int  ss   = UMath.decodeDecimalInt(tokens.get(5));
         int  ms   = UMath.decodeDecimalInt(tokens.get(6));
         /*.
         ==========================================================================================
         Normal people number months 1..12. The numb-nuts at Sun number them 0..11. Make an
         adjustment from normal numbering to numb-nuts numbering.
         ------------------------------------------------------------------------------------------ */
         MM--;
         /*.
         ==========================================================================================
         Use a Date object to convert the individual parts into the long that is returned
         ------------------------------------------------------------------------------------------ */
         java.util.Date  date = Util.getDate(yyyy,MM,dd,HH,mm,ss);
         return date.getTime() + (long)ms;
         }
      catch (Exception e)
         {
         return 0;
         }
      }



   /*:                                    :METHOD:043:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the last-accessed time of the file or directory named by this abstract pathname.
   All platforms support file-access times to the nearest second, but some provide more precision. The
   argument will be truncated to fit the supported precision. If the operation succeeds and no
   intervening operations on the file take place, then the next invocation of the lastAccessed() method
   will return the (possibly truncated) time argument that was passed to this method.

   <P><B>Implementation: </B><A HREF="XFile.java.html#043">View source</A>

   @return
      True if and only if the operation succeeded; false otherwise.

   @param
      pTime - The new last-accessed time, measured in milliseconds since the epoch (00:00:00 GMT,
      January 1, 1970).
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean setLastAccessed(long pTime)
      {
      /*.
      ==========================================================================================
      Get a calendar object that represents the new last-accessed time
      ------------------------------------------------------------------------------------------ */
      GregorianCalendar calendar = new GregorianCalendar();
      calendar.setTimeInMillis(pTime);
      /*.
      ==========================================================================================
      Pick out the elements of the calendar object that will be used to set the last-accessed
      time.
      ------------------------------------------------------------------------------------------ */
      int  yyyy = calendar.get(Calendar.YEAR);
      int  MM   = calendar.get(Calendar.MONTH);
      int  dd   = calendar.get(Calendar.DAY_OF_MONTH);
      int  HH   = calendar.get(Calendar.HOUR_OF_DAY);
      int  mm   = calendar.get(Calendar.MINUTE);
      int  ss   = calendar.get(Calendar.SECOND);
      int  ms   = calendar.get(Calendar.MILLISECOND);
      /*.
      ==========================================================================================
      Normal people number months 1..12. The numb-nuts at Sun number them 0..11. Make an
      adjustment from numb-nuts numbering to normal numbering.
      ------------------------------------------------------------------------------------------ */
      MM = MM + 1;
      /*.
      ==========================================================================================
      Set the last-accessed time
      ------------------------------------------------------------------------------------------ */
      this.setLastAccessed(yyyy,MM,dd,HH,mm,ss,ms);
      /*.
      ==========================================================================================
      Assume all went well. This method only returns a boolean in order to keep it true to its
      sister-method, the Java-SDK-supplied setLastModified(long).
      ------------------------------------------------------------------------------------------ */
      return true;
      }



   /*:                                    :METHOD:044:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method changes the year, month, and day components of this file's date-time stamp. It leaves
   the remaining date-time stamp components unchanged.

   <P><B>Implementation: </B><A HREF="XFile.java.html#044">View source</A>

   @param
      Yyyy is the year to set the last Accessed time of the file to.
   @param
      MM is the month to set the last Accessed time of the file to.
   @param
      Dd is the day of the month to set the last Accessed time of the file to.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setLastAccessed(int yyyy, int MM, int dd)
      {
      /*.
      ==========================================================================================
      Get a calendar object the represents the existing date-time stamp on this file
      ------------------------------------------------------------------------------------------ */
      GregorianCalendar calendar = new GregorianCalendar();
      calendar.setTimeInMillis(this.lastAccessed());
      /*.
      ==========================================================================================
      Pick out the elements of the existing date-time stamp that are going to stay the same
      after the arguments to this method are appiled;
      ------------------------------------------------------------------------------------------ */
      int  HH = calendar.get(Calendar.HOUR_OF_DAY);
      int  mm = calendar.get(Calendar.MINUTE);
      int  ss = calendar.get(Calendar.SECOND);
      int  ms = calendar.get(Calendar.MILLISECOND);
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.setLastAccessed(yyyy,MM,dd,HH,mm,ss,ms);
      }



   /*:                                    :METHOD:045:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method changes the year, month, day, hour, minute, and second components of this file's
   date-time stamp. It leaves the remaining date-time stamp components unchanged.

   <P><B>Implementation: </B><A HREF="XFile.java.html#045">View source</A>

   @param
      Yyyy is the year to set the last Accessed time of the file to.
   @param
      MM is the month to set the last Accessed time of the file to.
   @param
      Dd is the day of the month to set the last Accessed time of the file to.
   @param
      HH is the hour in 24hr format to set the last Accessed time of the file to.
   @param
      Mm is the minute to set the last Accessed time of the file to.
   @param
      Ss is the second to set the last Accessed time of the file to.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setLastAccessed
      (
      int yyyy,
      int MM,
      int dd,
      int HH,
      int mm,
      int ss
      )
      {
      /*.
      ==========================================================================================
      Get a calendar object the represents the existing date-time stamp on this file
      ------------------------------------------------------------------------------------------ */
      GregorianCalendar calendar = new GregorianCalendar();
      calendar.setTimeInMillis(this.lastAccessed());
      /*.
      ==========================================================================================
      Pick out the elements of the existing date-time stamp that are going to stay the same
      after the arguments to this method are appiled;
      ------------------------------------------------------------------------------------------ */
      int  ms = calendar.get(Calendar.MILLISECOND);
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.setLastAccessed(yyyy,MM,dd,HH,mm,ss,ms);
      }



   /*:                                    :METHOD:046:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the last Accessed time of the file to the time represented by the year, month, day,
   hour, minute, second, and millisecond specified.

   <P><B>Implementation: </B><A HREF="XFile.java.html#046">View source</A>

   @param
      Yyyy is the year to set the last Accessed time of the file to.
   @param
      MM is the month to set the last Accessed time of the file to.
   @param
      Dd is the day of the month to set the last Accessed time of the file to.
   @param
      HH is the hour in 24hr format to set the last Accessed time of the file to.
   @param
      Mm is the minute to set the last Accessed time of the file to.
   @param
      Ss is the second to set the last Accessed time of the file to.
   @param
      Ms is the millisecond to set the last Accessed time of the file to.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setLastAccessed
      (
      int yyyy,
      int MM,
      int dd,
      int HH,
      int mm,
      int ss,
      int ms
      )
      {
      try
         {
         /*.
         ==========================================================================================
         Change the file time
         ------------------------------------------------------------------------------------------ */
         NativeMethods  nm = new NativeMethods();
         nm.setLastAccessed (this.getCanonicalPath(),yyyy,MM,dd,HH,mm,ss,ms);
         }
      catch (Exception e)
         {
         }
      }



   /*:                                    :METHOD:047:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method copies the dates of this file to another file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#047">View source</A>

   @param
      pToFile is the file to which the dates of this file is copied.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void copyDatesTo(XFile pToFile) throws Exception
      {
      pToFile.setCreated(this.created());
      pToFile.setLastModified(this.lastModified());
      pToFile.setLastAccessed(this.lastAccessed());
      }



   /*:                                    :METHOD:048:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**

   <P><B>Implementation: </B><A HREF="XFile.java.html#048">View source</A>

   *//* This method copies the dates of this file to another file.

   @param
      pToFile is the file to which the dates of this file is copied.
   ---------------------------------------------------------------------------------------------------- */
   public void copyDatesTo(File pToFile) throws Exception
      {
      XFile  toFile = new XFile(pToFile);
      this.copyDatesTo(toFile);
      }



   /*:                                    :METHOD:049:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method copies the dates of another file to this file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#049">View source</A>

   @param
      pFromFile is the file whose dates are copied to this file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void copyDatesFrom(XFile pFromFile) throws Exception
      {
      pFromFile.copyDatesTo(this);
      }



   /*:                                    :METHOD:050:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method copies the dates of another file to this file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#050">View source</A>

   @param
      pFromFile is the file whose dates are copied to this file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void copyDatesFrom(File pFromFile) throws Exception
      {
      XFile  fromFile = new XFile(pFromFile);
      this.copyDatesFrom(fromFile);
      }



   /*:                                    :METHOD:051:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method copies the contents of this file to another file. This method also copies the file dates
   from this file to its copy file.

                                    ** IMPLEMENTATION SUGGESTIION **
   Also worth noting - if youve got JDK 1.4, then FileChannel has transferTo() and transferFrom() which
   are generally much, much faster than copying bytes using streams. (They take better advantage of
   native code and hardware optimizations, and bypass the JVM for a lot of the work.)

   <P><B>Implementation: </B><A HREF="XFile.java.html#051">View source</A>

   @param
      pToFile is the file to which the content of this file is copied.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void copyTo(XFile pToFile) throws Exception
      {
      /*.
      ==========================================================================================
      This method makes no sense on directory files, this must be a normal file
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory())
         {
         throw new Exception("Attempt to copy a Directory from <" + this.getCanonicalPath() + "> to a file <" + pToFile.getCanonicalPath() + ">");
         }
      /*.
      ==========================================================================================
      Create channel on the source
      ------------------------------------------------------------------------------------------ */
      FileChannel srcChannel = new FileInputStream(this).getChannel();
      /*.
      ==========================================================================================
      Create channel on the destination
      ------------------------------------------------------------------------------------------ */
      FileChannel dstChannel = new FileOutputStream(pToFile).getChannel();
      /*.
      ==========================================================================================
      Copy file contents from source to destination
      ------------------------------------------------------------------------------------------ */
      dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
      /*.
      ==========================================================================================
      Close the channels
      ------------------------------------------------------------------------------------------ */
      srcChannel.close();
      dstChannel.force(true);
      dstChannel.close();
      /*.
      ==========================================================================================
      Copy the file dates
      ------------------------------------------------------------------------------------------ */
      this.copyDatesTo(pToFile);
      }



   /*:                                    :METHOD:052:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method copies the contents of this file to another file. This method also copies the file dates
   from this file to its copy file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#052">View source</A>

   @param
      pToFile is the file to which the content of this file is copied.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void copyTo(File pToFile) throws Exception
      {
      XFile  toFile = new XFile(pToFile);
      this.copyTo(toFile);
      }



   /*:                                    :METHOD:053:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method copies the contents of another file to this file. This method also copies the file dates
   from the other file to this file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#053">View source</A>

   @param
      pFromFile is the file whose contents are copied to this file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void copyFrom(XFile pFromFile) throws Exception
      {
      pFromFile.copyTo(this);
      }



   /*:                                    :METHOD:054:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method copies the contents of another file to this file. This method also copies the file dates
   from the other file to this file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#054">View source</A>

   @param
      pFromFile is the file whose contents are copied to this file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void copyFrom(File pFromFile) throws Exception
      {
      XFile  fromFile = new XFile(pFromFile);
      this.copyFrom(fromFile);
      }



   /*:                                    :METHOD:055:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method appends the contents of this file to another file.

                                    ** IMPLEMENTATION SUGGESTIION **
   Also worth noting - if youve got JDK 1.4, then FileChannel has transferTo() and transferFrom() which
   are generally much, much faster than copying bytes using streams. (They take better advantage of
   native code and hardware optimizations, and bypass the JVM for a lot of the work.)

   <P><B>Implementation: </B><A HREF="XFile.java.html#055">View source</A>

   @param
      pToFile is the file to which the content of this file is appended.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void appendTo(XFile pToFile) throws Exception
      {
      /*.
      ==========================================================================================
      This method makes no sense on directory files, this must be a normal file
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory()) return;
      /*.
      ==========================================================================================
      Create channel on the source
      ------------------------------------------------------------------------------------------ */
      FileChannel srcChannel = new FileInputStream(this).getChannel();
      /*.
      ==========================================================================================
      Create channel on the destination
      ------------------------------------------------------------------------------------------ */
      FileChannel dstChannel = new FileOutputStream(pToFile,true).getChannel();
      /*.
      ==========================================================================================
      Copy file contents from source to destination
      ------------------------------------------------------------------------------------------ */
      dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
      /*.
      ==========================================================================================
      Close the channels
      ------------------------------------------------------------------------------------------ */
      srcChannel.close();
      dstChannel.force(true);
      dstChannel.close();
      }



   /*:                                    :METHOD:056:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**

   <P><B>Implementation: </B><A HREF="XFile.java.html#056">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void appendTo(File pToFile) throws Exception
      {
      XFile  toFile = new XFile(pToFile);
      this.appendTo(toFile);
      }



   /*:                                    :METHOD:057:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method appends the contents of another file to this file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#057">View source</A>

   @param
      pFromFile is the file whose contents are appended to this file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void appendFrom(XFile pFromFile) throws Exception
      {
      pFromFile.appendTo(this);
      }



   /*:                                    :METHOD:058:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**

   <P><B>Implementation: </B><A HREF="XFile.java.html#058">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void appendFrom(File pFromFile) throws Exception
      {
      XFile  fromFile = new XFile(pFromFile);
      this.appendFrom(fromFile);
      }



   /*:                                    :METHOD:059:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method appends the contents of one or more other files to this file.

   <P><B>Implementation: </B><A HREF="XFile.java.html#059">View source</A>

   @param
      pArrayList is an array list of Files (or subclasses like FileExts).
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void appendFrom(ArrayList pArrayList) throws Exception
      {
      Iterator  iter = pArrayList.iterator();
      while (iter.hasNext())
         {
         File  fromFile = (File)iter.next();
         this.appendFrom(fromFile);
         }
      }



   /*:                                    :METHOD:060:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method computes and returns a string represntation of this XFile's digest

   <P><B>Implementation: </B><A HREF="XFile.java.html#060">View source</A>

   @return
      The digest of the file

   @param
      pAlgorithm is the name of the algorithm requested. See Appendix A in the Java Cryptography
      Architecture API Specification & Reference for information about standard algorithm names.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> XString digest(Type pAlgorithm) throws Exception
      {
      return (new Digest(UString.toString(pAlgorithm),this)).xstring();
      }



   /*:                                    :METHOD:061:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method computes and returns a string represntation of this XFile's MD5 digest

   <P><B>Implementation: </B><A HREF="XFile.java.html#061">View source</A>

   @return
      The MD5 digest of the file
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString digest() throws Exception
      {
      return this.digest("MD5");
      }



   /*:                                    :METHOD:062:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method compares the contents of this XFile with the contents of another XFile.

   <P><B>Implementation: </B><A HREF="XFile.java.html#062">View source</A>

   @return
      True if the contents of the FileExts are identical; false otherwise.

   @param
      pOtherFile is the XFile whose contents are compared to this XFile's contents.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean contentEquals(XFile pOtherFile) throws Exception
      {
      /*.
      ==========================================================================================
      Point of interest: here's the old, less efficient, but more elegant way to compare two
      files:
         Return
            ( (this.length() == pOtherFile.length()) &&
            this.digest("SHA").equalsIgnoreCase(pOtherFile.digest("SHA")) );
      ------------------------------------------------------------------------------------------ */
      return this.contentEquals((File)pOtherFile);
      }



   /*:                                    :METHOD:063:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method compares the contents of this XFile with the contents of another File.

   <P><B>Implementation: </B><A HREF="XFile.java.html#063">View source</A>

   @return
      True if the contents of the files are identical; false otherwise.

   @param
      pOtherFile is the File whose contents are compared to this XFile's contents.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean contentEquals(File pOtherFile) throws Exception
      {
      /*.
      ==========================================================================================
      If the two files are different sizes, then their contents are not equal.
      ------------------------------------------------------------------------------------------ */
      if (this.length() != pOtherFile.length())
         {
         return false;
         }
      /*.
      ==========================================================================================
      Otherwise, do a byte-by-byte comparison of the two files' content.
      ------------------------------------------------------------------------------------------ */
      boolean              result         = true;
      byte[]               thisByteArray  = new byte[2 * UMath.ONE_MEG];
      byte[]               otherByteArray = new byte[2 * UMath.ONE_MEG];
      BufferedInputStream  thisFile       = new BufferedInputStream
         (
         new FileInputStream(this)
         );
      BufferedInputStream  otherFile      = new BufferedInputStream
         (
         new FileInputStream(pOtherFile)
         );
      /*.
      ==========================================================================================
      Compare each byte in this file to the corresponding byte in the other file and if any
      difference is found, set the result to false and stop the comparing process.
      ------------------------------------------------------------------------------------------ */
      while (true)
         {
         /*.
         ==========================================================================================
         Read the next chunk of bytes from both files
         ------------------------------------------------------------------------------------------ */
         int  thisBytesRead  = thisFile.read(thisByteArray,0,2 * UMath.ONE_MEG);
         int  otherBytesRead = otherFile.read(otherByteArray,0,2 * UMath.ONE_MEG);
         /*.
         ==========================================================================================
         If the same number of bytes were not available in both files then one file is smaller than
         the other so they are not equal so set the result to false and break out of the loop.
         ------------------------------------------------------------------------------------------ */
         if (thisBytesRead != otherBytesRead)
            {
            result = false;
            break;
            }
         /*.
         ==========================================================================================
         If we've reached the end of file break out of the loop
         ------------------------------------------------------------------------------------------ */
         if (thisBytesRead == (-1)) break;
         /*.
         ==========================================================================================
         Compare each byte and if one is found to be different then set the result to false and
         break out of the loop.
         ------------------------------------------------------------------------------------------ */
         for (int i=0; i < thisBytesRead; i++)
            {
            if (thisByteArray[i] != otherByteArray[i])
               {
               result = false;
               break;
               }
            }
         }
      /*.
      ==========================================================================================
      Close the files and return the result
      ------------------------------------------------------------------------------------------ */
      thisFile.close();
      otherFile.close();
      return result;
      }



   /*:                                    :METHOD:064:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method overrides the File.delete() method and deletes the file or directory denoted by this
   abstract pathname. If this pathname denotes a directory, then the directory is emptied first and
   then deleted.

   <P><B>Implementation: </B><A HREF="XFile.java.html#064">View source</A>

   @return
      True if and only if the file or directory is successfully deleted; false otherwise
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean delete()
      {
      try
         {
         /*.
         ==========================================================================================
         If this is a directory, it must be empty before it can be deleted. This block empties it.
         ------------------------------------------------------------------------------------------ */
         if (this.isDirectory())
            {
            /*.
            ==========================================================================================
            Create a list of the files and sub directories that reside within this directory
            ------------------------------------------------------------------------------------------ */
            XString  fileList[] = XString.toXStringArray(this.list());
            /*.
            ==========================================================================================
            All the files and sub directories in this directory must be deleted before this directory
            can be deleted.
            ------------------------------------------------------------------------------------------ */
            XString  thisDirName = new XString(this.getCanonicalPath());
            for (int i=0; i < fileList.length; i++)
               {
               XFile  currXFile = new XFile(thisDirName, fileList[i]);
               if (currXFile.isDirectory())
                  {
                  /*.
                  ==========================================================================================
                  Sub-directory: remove it
                  ------------------------------------------------------------------------------------------ */
                  if ( ! currXFile.delete()) return false;
                  }
               else
                  {
                  /*.
                  ==========================================================================================
                  Regular file: delete it. (this upcasts the XFile to a File so that the overriden
                  File.delete() can be used to actually delete the file)
                  ------------------------------------------------------------------------------------------ */
                  if ( ! currXFile.deleteFile()) return false;
                  }
               }
            }
         /*.
         ==========================================================================================
         Now delete this file or directory (this upcasts the XFile to a File so that the overriden
         File.delete() can be used to actually delete the file)
         ------------------------------------------------------------------------------------------ */
         return this.deleteFile();
         }
      catch (Exception e)
         {
         return false;
         }
      }



   /*:                                    :METHOD:065:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method wipes the file or directory denoted by this abstract pathname. If this pathname denotes
   a directory, then the directory is emptied first and then wiped.

   Wipe is an advanced security tool, which allows you to completely remove sensitive data from your
   hard disk.

   Caveat: Caveat:
      On a journaling filesystem like NTFS there is actually no way to securely erase a single file
      without wiping all the free space on the drive. The problem is that the new blocks (which you've
      presumably overwritten with random data) are not guaranteed to be in the same place on disk as
      the old ones.

      In order to keep your data secure, the only real solution you have is to completely encrypt the
      drive

   <P><B>Implementation: </B><A HREF="XFile.java.html#065">View source</A>

   @return
      True if and only if the file or directory is successfully wiped; false otherwise
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean wipe()
      {
      try
         {
         /*.
         ==========================================================================================
         If this is a directory, it must be empty before it can be deleted. This block empties it.
         ------------------------------------------------------------------------------------------ */
         if (this.isDirectory())
            {
            /*.
            ==========================================================================================
            Create a list of the files and sub directories that reside within this directory
            ------------------------------------------------------------------------------------------ */
            XString  fileList[] = XString.toXStringArray(this.list());
            /*.
            ==========================================================================================
            All the files and sub directories in this directory must be deleted before this directory
            can be deleted.
            ------------------------------------------------------------------------------------------ */
            XString  thisDirName = new XString(this.getCanonicalPath());
            for (int i=0; i < fileList.length; i++)
               {
               XFile  currXFile = new XFile(thisDirName, fileList[i]);
               if (currXFile.isDirectory())
                  {
                  /*.
                  ==========================================================================================
                  Sub-directory: remove it
                  ------------------------------------------------------------------------------------------ */
                  if ( ! currXFile.wipe()) return false;
                  }
               else
                  {
                  /*.
                  ==========================================================================================
                  Regular file: delete it. (this upcasts the XFile to a File so that the overriden
                  File.delete() can be used to actually delete the file)
                  ------------------------------------------------------------------------------------------ */
                  if ( ! currXFile.wipeFile()) return false;
                  }
               }
            }
         /*.
         ==========================================================================================
         Now delete this file or directory (this upcasts the XFile to a File so that the overriden
         File.delete() can be used to actually delete the file)
         ------------------------------------------------------------------------------------------ */
         return this.wipeFile();
         }
      catch (Exception e)
         {
         return false;
         }
      }


   /*:                                    :METHOD:066:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method fixes up the "created", "lastModified", and "lastAccessed" dates on "this" file or
   directory.

   If "this" is a normal file:
      This method sets the created date for "this" file to 0 and sets the lastAccesse date to whatever
      the lastModified date is.

   If "this" is a directory:
      If there aren't any files or subdirectories in "this" directory then this method sets the
      lastModified and lastAccessed dates to match the created date.

      If there are files or subdirectories in "this" directory then this method changes the
      lastModified and lastAccessed dates on "this" directory to match the lastModified date of the
      most recently modified file or subdirectory in "this" directory.

   <P><B>Implementation: </B><A HREF="XFile.java.html#066">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void fixDates() throws Exception
      {
      /*.
      ==========================================================================================
      If "this" is a regular file ...
      ------------------------------------------------------------------------------------------ */
      if (this.isFile())
         {
         this.setCreated(0);
         this.setLastAccessed(this.lastModified());
         }
      /*.
      ==========================================================================================
      Else "this" is a directory ...
      ------------------------------------------------------------------------------------------ */
      else
         {
         /*.
         ==========================================================================================
         Create a list of the files and sub directories that reside within "this" directory
         ------------------------------------------------------------------------------------------ */
         XString  fileList[] = XString.toXStringArray(this.list());
         /*.
         ==========================================================================================
         If there aren't any files or subdirectories in "this" directory then just set the
         lastModified and lastAccessed dates to match the created date.
         ------------------------------------------------------------------------------------------ */
         if (fileList.length == 0)
            {
            long  date = this.created();
            this.setLastModified(date);
            this.setLastAccessed(date);
            }
         /*.
         ==========================================================================================
         Otherwise, change the lastModified and lastAccessed dates on "this" directory to match the
         lastModified date of the most recently modified file or subdirectory in "this" directory.
         ------------------------------------------------------------------------------------------ */
         else
            {
            long     newestDate  = 0;
            XString  thisDirName = new XString(this.getCanonicalPath());
            /*.
            ==========================================================================================
            Search each file and subdirectory for the most recent lastModified date
            ------------------------------------------------------------------------------------------ */
            for (int i=0; i < fileList.length; i++)
               {
               File  currentFile = new File(thisDirName.string(), fileList[i].string());
               newestDate = UMath.max(newestDate,currentFile.lastModified());
               }
            /*.
            ==========================================================================================
            Change this directory's dates
            ------------------------------------------------------------------------------------------ */
            this.setLastModified(newestDate);
            this.setLastAccessed(newestDate);
            }
         }
      }



   /*:                                    :METHOD:067:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method recursively fixes up the "created", "lastModified", and "lastAccessed" dates on "this"
   file or directory.

   If "this" is a normal file:
      This method sets the created date for "this" file to 0 and sets the lastAccesse date to whatever
      the lastModified date is.

   If "this" is a directory:
      If there aren't any files or subdirectories in "this" directory then this method sets the
      lastModified and lastAccessed dates to match the created date.

      If there are files or subdirectories in "this" directory then this method changes the
      lastModified and lastAccessed dates on "this" directory to match the lastModified date of the
      most recently modified file or subdirectory in "this" directory after recursively fixing the
      dates on any subdirectories in "this" directory.

   <P><B>Implementation: </B><A HREF="XFile.java.html#067">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void recursivelyFixDates() throws Exception
      {
      /*.
      ==========================================================================================
      If "this" is a regular file ...
      ------------------------------------------------------------------------------------------ */
      if (this.isFile())
         {
         this.setCreated(0);
         this.setLastAccessed(this.lastModified());
         }
      /*.
      ==========================================================================================
      Else "this" is a directory ...
      ------------------------------------------------------------------------------------------ */
      else
         {
         /*.
         ==========================================================================================
         Create a list of the files and sub directories that reside within "this" directory
         ------------------------------------------------------------------------------------------ */
         XString  fileList[] = XString.toXStringArray(this.list());
         /*.
         ==========================================================================================
         If there aren't any files or subdirectories in "this" directory then just set the
         lastModified and lastAccessed dates to match the created date.
         ------------------------------------------------------------------------------------------ */
         if (fileList.length == 0)
            {
            long  date = this.created();
            this.setLastModified(date);
            this.setLastAccessed(date);
            }
         /*.
         ==========================================================================================
         Otherwise, change the lastModified and lastAccessed dates on "this" directory to match the
         lastModified date of the most recently modified file or subdirectory in "this" directory.
         ------------------------------------------------------------------------------------------ */
         else
            {
            long     newestDate  = 0;
            XString  thisDirName = new XString(this.getCanonicalPath());
            /*.
            ==========================================================================================
            For each file and subdirectory ..
            ------------------------------------------------------------------------------------------ */
            for (int i=0; i < fileList.length; i++)
               {
               XFile  currentFileExt = new XFile(thisDirName, fileList[i]);
               /*.
               ==========================================================================================
               Fix this file or sudirectory's date
               ------------------------------------------------------------------------------------------ */
               currentFileExt.recursivelyFixDates();
               /*.
               ==========================================================================================
               Remember the lastModified date of the most recently modified file or subdirectory in
               "this" directory
               ------------------------------------------------------------------------------------------ */
               newestDate = UMath.max(newestDate,currentFileExt.lastModified());
               }
            /*.
            ==========================================================================================
            Change this directory's dates
            ------------------------------------------------------------------------------------------ */
            this.setLastModified(newestDate);
            this.setLastAccessed(newestDate);
            }
         }
      }



   /*:                                    :METHOD:068:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method fixes up and then displays the "created", "lastModified", and "lastAccessed" dates on
   "this" file or directory.

   If "this" is a normal file:
      This method sets the created date for "this" file to 0 and sets the lastAccesse date to whatever
      the lastModified date is.

   If "this" is a directory:
      If there aren't any files or subdirectories in "this" directory then this method sets the
      lastModified and lastAccessed dates to match the created date.

      If there are files or subdirectories in "this" directory then this method changes the
      lastModified and lastAccessed dates on "this" directory to match the lastModified date of the
      most recently modified file or subdirectory in "this" directory.

   <P><B>Implementation: </B><A HREF="XFile.java.html#068">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void fixAndDisplayDates() throws Exception
      {
      this.fixDates();
      this.displayDates();
      }



   /*:                                    :METHOD:069:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method recursively fixes up and then displays the "created", "lastModified", and "lastAccessed"
   dates on "this" file or directory.

   If "this" is a normal file:
      This method sets the created date for "this" file to 0 and sets the lastAccesse date to whatever
      the lastModified date is.

   If "this" is a directory:
      If there aren't any files or subdirectories in "this" directory then this method sets the
      lastModified and lastAccessed dates to match the created date.

      If there are files or subdirectories in "this" directory then this method changes the
      lastModified and lastAccessed dates on "this" directory to match the lastModified date of the
      most recently modified file or subdirectory in "this" directory after recursively fixing the
      dates on any subdirectories in "this" directory.

   <P><B>Implementation: </B><A HREF="XFile.java.html#069">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void recursivelyFixAndDisplayDates() throws Exception
      {
      /*.
      ==========================================================================================
      If "this" is a regular file ...
      ------------------------------------------------------------------------------------------ */
      if (this.isFile())
         {
         this.setCreated(0);
         this.setLastAccessed(this.lastModified());
         }
      /*.
      ==========================================================================================
      Else "this" is a directory ...
      ------------------------------------------------------------------------------------------ */
      else
         {
         /*.
         ==========================================================================================
         Create a list of the files and sub directories that reside within "this" directory
         ------------------------------------------------------------------------------------------ */
         XString  fileList[] = XString.toXStringArray(this.list());
         /*.
         ==========================================================================================
         If there aren't any files or subdirectories in "this" directory then just set the
         lastModified and lastAccessed dates to match the created date.
         ------------------------------------------------------------------------------------------ */
         if (fileList.length == 0)
            {
            long  date = this.created();
            this.setLastModified(date);
            this.setLastAccessed(date);
            }
         /*.
         ==========================================================================================
         Otherwise, change the lastModified and lastAccessed dates on "this" directory to match the
         lastModified date of the most recently modified file or subdirectory in "this" directory.
         ------------------------------------------------------------------------------------------ */
         else
            {
            long     newestDate  = 0;
            XString  thisDirName = new XString(this.getCanonicalPath());
            /*.
            ==========================================================================================
            For each file and subdirectory ..
            ------------------------------------------------------------------------------------------ */
            for (int i=0; i < fileList.length; i++)
               {
               XFile  currentFileExt = new XFile(thisDirName, fileList[i]);
               /*.
               ==========================================================================================
               Fix this file or sudirectory's date
               ------------------------------------------------------------------------------------------ */
               currentFileExt.recursivelyFixAndDisplayDates();
               /*.
               ==========================================================================================
               Remember the lastModified date of the most recently modified file or subdirectory in
               "this" directory
               ------------------------------------------------------------------------------------------ */
               newestDate = UMath.max(newestDate,currentFileExt.lastModified());
               }
            /*.
            ==========================================================================================
            Change this directory's dates
            ------------------------------------------------------------------------------------------ */
            this.setLastModified(newestDate);
            this.setLastAccessed(newestDate);
            }
         }
      /*.
      ==========================================================================================
      Display the dates for "this"
      ------------------------------------------------------------------------------------------ */
      this.displayDates();
      }



   /*:                                    :METHOD:070:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method displays the created, lastModified, and lastAccessed dates for "this" directory and
   recursively for all subdirectories under "this" directory.

   <P><B>Implementation: </B><A HREF="XFile.java.html#070">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void recursivelyDisplayDirDates() throws Exception
      {
      /*.
      ==========================================================================================
      If this is a directory ...
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory())
         {
         /*.
         ==========================================================================================
         Create a list of the files and sub directories that reside within this directory
         ------------------------------------------------------------------------------------------ */
         XString  fileList[] = XString.toXStringArray(this.list());
         /*.
         ==========================================================================================
         All the sub directories in this directory must have their dates changed
         ------------------------------------------------------------------------------------------ */
         XString  thisDirName = new XString(this.getCanonicalPath());
         for (int i=0; i < fileList.length; i++)
            {
            XFile  currentFileExt = new XFile(thisDirName, fileList[i]);
            if (currentFileExt.isDirectory())
               {
               /*.
               ==========================================================================================
               Sub-directory: change its dates
               ------------------------------------------------------------------------------------------ */
               currentFileExt.recursivelyDisplayDirDates();
               }
            }
         /*.
         ==========================================================================================
         Change this directory's dates
         ------------------------------------------------------------------------------------------ */
         this.displayDates();
         }
      }



   /*:                                    :METHOD:071:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method displays the created, lastModified, and lastAccessed dates for a file or directory.

   <P><B>Implementation: </B><A HREF="XFile.java.html#071">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void displayDates() throws Exception
      {
      /*.
      ==========================================================================================
      Display the dates for "this".
      ------------------------------------------------------------------------------------------ */
      cOut.println
         (
         Util.timeStamp("yyyy-MM-dd HH:mm:ss.SSS",new Date(this.created()))      +
         "  "                                                                    +
         Util.timeStamp("yyyy-MM-dd HH:mm:ss.SSS",new Date(this.lastModified())) +
         "  "                                                                    +
         Util.timeStamp("yyyy-MM-dd HH:mm:ss.SSS",new Date(this.lastAccessed())) +
         "  "                                                                    +
         this.getCanonicalPath()
         );
      }



   /*:                                    :METHOD:072:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method displays the column headers appropriate to subsequent calls to the displayDates()
   method.

   <P><B>Implementation: </B><A HREF="XFile.java.html#072">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void displayDatesColumnHeaders() throws Exception
      {
      /*.
      ==========================================================================================
      Display column headers
      ------------------------------------------------------------------------------------------ */
      cOut.println("");
      cOut.println
         (
         "      Create Date          Last Modified Date       Last Accessed Date          File Name"
         );
      cOut.println
         (
         "-----------------------  -----------------------  -----------------------  -------------------"
         );
      }



   /*:                                    :METHOD:073:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method makes a bakup of this file. If this file is a normal file, it also backs up any existing
   backups of this file, up to the number of backup files specified by the static value of MAX_BKUPS.
   For example:

      <BLOCKQUOTE>
         <PRE id="unindent">
            *  if
            *     this is a normal file named \temp\x.x
            *     and MAX_BKUPS is 3
            *     and the backup directory is \backup
            *
            *  then the first time backup() is called on \temp\x.x
            *     \temp\x.x is copied to \backup\temp\x.x.1
            *
            *  and the second time backup() is called on \temp\x.x
            *     first, \backup\temp\x.x.1 is copied to \backup\temp\x.x.2
            *     then,  \temp\x.x          is copied to \backup\temp\x.x.1
            *
            *  and the third and all subsequent times backup() is called on \temp\x.x
            *     first, \backup\temp\x.x.2 is copied to \backup\temp\x.x.3
            *     next,  \backup\temp\x.x.1 is copied to \backup\temp\x.x.2
            *     then,  \temp\x.x          is copied to \backup\temp\x.x.1
         </PRE>
      </BLOCKQUOTE>

   If this file is a directory, this method backs up all the normal files in this directory and
   recursively descends into all the subdirectories and backs them up too.

   This method preserves file dates. In other words, it sets the file dates on the backup file to match
   the dates on the file that the backup file backs up.

   <P><B>Implementation: </B><A HREF="XFile.java.html#073">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void backup() throws Exception
      {
      /*.
      ==========================================================================================
      If this is a directory ...
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory())
         {
         /*.
         ==========================================================================================
         Create a list of the files and sub directories that reside within this directory
         ------------------------------------------------------------------------------------------ */
         XString[]  fileList = XString.toXStringArray(this.list());
         /*.
         ==========================================================================================
         Back up all the normal files and recursively descend into the sub directories backing up
         everything in them.
         ------------------------------------------------------------------------------------------ */
         XString  thisDirName = new XString(this.getCanonicalPath());
         for (int i=0; i < fileList.length; i++)
            {
            XFile currentFileExt = new XFile(thisDirName.string(), fileList[i].string());
            currentFileExt.backup();
            }
         }
      /*.
      ==========================================================================================
      Else this is a normal file so make its backup(s)
      ------------------------------------------------------------------------------------------ */
      else
         {
         this.backupNormalFile(null,null,0);
         }
      }



   /*:                                    :METHOD:074:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tests to see if two files are normal (not directory) and distinct (distinguished as not
   being the same; separate) files.

   <P><B>Implementation: </B><A HREF="XFile.java.html#074">View source</A>

   @return
      This method returns true if the two files are normal and distinct. Otherwise, this method returns
      false.

   @param
      pFirstFile is any file
   @param
      pSecondFile is any file
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static boolean filesAreNormalAndDistinct(File pFirstFile, File pSecondFile) throws Exception
      {
      return filesAreNormal(pFirstFile, pSecondFile) && filesAreDistinct(pFirstFile, pSecondFile);
      }



   /*:                                    :METHOD:075:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tests to see if two files are normal (not directory) and distinct (distinguished as not
   being the same; separate) files.

   <P><B>Implementation: </B><A HREF="XFile.java.html#075">View source</A>

   @return
      This method returns true if the two files are normal and distinct. Otherwise, this method returns
      false.

   @param
      pFirstPathname is anything that can be converted to a String representing the name of any file
   @param
      pSecondPathname is anything that can be converted to a String representing the name of any file
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static <Type1, Type2> boolean filesAreNormalAndDistinct(Type1 pFirstPathname, Type2 pSecondPathname) throws Exception
      {
      if (pFirstPathname == null)
         {
         throw new Exception("pFirstPathname is null");
         }
      if (pSecondPathname == null)
         {
         throw new Exception("pSecondPathname is null");
         }
      /*.
      ==========================================================================================
      Get files for each pathname
      ------------------------------------------------------------------------------------------ */
      File  firstFile  = new File(UString.toString(pFirstPathname));
      File  secondFile = new File(UString.toString(pSecondPathname));
      /*.
      ==========================================================================================
      Test then release the files to the garbage collector and return result of test
      ------------------------------------------------------------------------------------------ */
      boolean  result = filesAreNormalAndDistinct(firstFile, secondFile);
      firstFile  = null;
      secondFile = null;
      return result;
      }



   /*:                                    :METHOD:076:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tests to see if two files are normal (not directory) files.

   <P><B>Implementation: </B><A HREF="XFile.java.html#076">View source</A>

   @return
      This method returns true if the two files are normal. If either is a directory, this method
      returns false.

   @param
      pFirstFile is any file
   @param
      pSecondFile is any file
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static boolean filesAreNormal(File pFirstFile, File pSecondFile) throws Exception
      {
      return pFirstFile.isFile() && pSecondFile.isFile();
      }



   /*:                                    :METHOD:077:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tests to see if two files are normal (not directory) files.

   <P><B>Implementation: </B><A HREF="XFile.java.html#077">View source</A>

   @return
      This method returns true if the two files are normal. If either is a directory, this method
      returns false.

   @param
      pFirstPathname is anything that can be converted to a String representing the name of any file
   @param
      pSecondPathname is anything that can be converted to a String representing the name of any file
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static <Type1, Type2> boolean filesAreNormal(Type1 pFirstPathname, Type2 pSecondPathname) throws Exception
      {
      if (pFirstPathname == null)
         {
         throw new Exception("pFirstPathname is null");
         }
      if (pSecondPathname == null)
         {
         throw new Exception("pSecondPathname is null");
         }
      /*.
      ==========================================================================================
      Get files for each pathname
      ------------------------------------------------------------------------------------------ */
      File  firstFile  = new File(UString.toString(pFirstPathname));
      File  secondFile = new File(UString.toString(pSecondPathname));
      /*.
      ==========================================================================================
      Test then release the files to the garbage collector and return result of test
      ------------------------------------------------------------------------------------------ */
      boolean  result = filesAreNormal(firstFile, secondFile);
      firstFile  = null;
      secondFile = null;
      return result;
      }



   /*:                                    :METHOD:078:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tests to see if two files are distinct (distinguished as not being the same; separate)
   files.


   <P><B>Implementation: </B><A HREF="XFile.java.html#078">View source</A>

   @return
      This method returns true if the two files are distinct. If they are the same file, this method
      returns false.

   @param
      pFirstFile is any file
   @param
      pSecondFile is any file
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static boolean filesAreDistinct(File pFirstFile, File pSecondFile) throws Exception
      {
      return (pFirstFile.getCanonicalPath() != pSecondFile.getCanonicalPath());
      }



   /*:                                    :METHOD:079:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tests to see if two files are distinct (distinguished as not being the same; separate)
   files.


   <P><B>Implementation: </B><A HREF="XFile.java.html#079">View source</A>

   @return
      This method returns true if the two files are distinct. If they are the same file, this method
      returns false.

   @param
      pFirstPathname is anything that can be converted to a String representing the name of any file
   @param
      pSecondPathname is anything that can be converted to a String representing the name of any file
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static <Type1, Type2> boolean filesAreDistinct(Type1 pFirstPathname, Type2 pSecondPathname) throws Exception
      {
      if (pFirstPathname == null)
         {
         throw new Exception("pFirstPathname is null");
         }
      if (pSecondPathname == null)
         {
         throw new Exception("pSecondPathname is null");
         }
      /*.
      ==========================================================================================
      Get files for each pathname
      ------------------------------------------------------------------------------------------ */
      File  firstFile  = new File(UString.toString(pFirstPathname));
      File  secondFile = new File(UString.toString(pSecondPathname));
      /*.
      ==========================================================================================
      Test then release the files to the garbage collector and return result of test
      ------------------------------------------------------------------------------------------ */
      boolean  result = filesAreDistinct(firstFile, secondFile);
      firstFile  = null;
      secondFile = null;
      return result;
      }



   /*:                                    :METHOD:080:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tests to see if this file is distinct (distinguished as not being the same; separate)
   from the other file identified by pOtherPathname.


   <P><B>Implementation: </B><A HREF="XFile.java.html#080">View source</A>

   @return
      This method returns true if the two files are distinct. If they are the same file, this method
      returns false.

   @param
      pOtherPathname is anything that can be converted to a String representing the name of any file
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> boolean filesAreDistinct(Type1 pOtherPathname) throws Exception
      {
      return filesAreDistinct(this.getCanonicalPath(), pOtherPathname);
      }



   /*:                                    :METHOD:081:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces this file with another. The content of this file is made to be an exact copy of
   the replacement file and the replacement file no longer exists after this method completes.

   <P><B>Implementation: </B><A HREF="XFile.java.html#081">View source</A>

   @param
      pReplacementPathname is anything that can be converted to a String representing the name of any
      file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void replaceFile(Type1 pReplacementPathname) throws Exception
      {
      replaceFile(this.getCanonicalPath(), pReplacementPathname);
      }



   /*:                                    :METHOD:082:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces one file with another. The content of the original file is made to be an exact
   copy of the replacement file and the replacement file no longer exists after this method completes.
   This method guarantees that a copy of the original file is kept until a successful and verified
   replacement of it has occured.

   <P><B>Implementation: </B><A HREF="XFile.java.html#082">View source</A>

   @param
      pOriginalPathname is anything that can be converted to a String representing the name of any file
   @param
      pReplacementPathname is anything that can be converted to a String representing the name of any
      file
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static <Type1, Type2> void replaceFile
      (
      Type1  pOriginalPathname,
      Type2  pReplacementPathname
      )
      throws Exception
      {
      /*.
      ==========================================================================================
      Check the parameters.
      ------------------------------------------------------------------------------------------ */
      if (pOriginalPathname == null)
         {
         throw new Exception("pOriginalPathname is null");
         }
      if (pReplacementPathname == null)
         {
         throw new Exception("pReplacementPathname is null");
         }
      if (pOriginalPathname.equals(pReplacementPathname))
         {
         throw new Exception("pOriginalPathname and pReplacementPathname are the same file");
         }
      /*.
      ==========================================================================================
      Open the two files. These XFile constructors throw an Exception if anything goes wrong.
      ------------------------------------------------------------------------------------------ */
      XFile  originalFile    = new XFile(UString.toString(pOriginalPathname));
      XFile  replacementFile = new XFile(UString.toString(pReplacementPathname));
      /*.
      ==========================================================================================
      Make sure we're not messing with directories and these are two different files.
      ------------------------------------------------------------------------------------------ */
      if ( ! filesAreNormalAndDistinct(originalFile, replacementFile))
         {
         cOut.titledSeverityPrintf
            (
            Const.HALT,
            CLASS_NAME,
            "I/O Error: pOriginalPathname <"       +
            UString.toString(pOriginalPathname)    +
            " and/or pReplacementPathname <"       +
            UString.toString(pReplacementPathname) +
            "> either refer to a directory or refer to the same file." +
            " "                                    +
            "The original file <"                  +
            UString.toString(pOriginalPathname)    +
            "> was not changed."
            );
         throw new Exception("Files are not normal and distinct.");
         }
      /*.
      ==========================================================================================
      Only replace the file if it is different from the original
      ------------------------------------------------------------------------------------------   */
      if (originalFile.contentEquals(replacementFile))
         {
         /*.
         ==========================================================================================
         Delete the replacement file.
         ------------------------------------------------------------------------------------------ */
         if ( ! replacementFile.delete())
            {
            throw new Exception("Cannot delete replacement file <" + replacementFile.getCanonicalPath() + ">");
            }
         replacementFile = null;
         return;
         }
      /*.
      ==========================================================================================
      Make a temporary "Save My Ass" file. This throws an Exception if anything goes wrong.
      ------------------------------------------------------------------------------------------ */
      String  recoveryPathname = null;
      XFile   recoveryFile     = null;
      try
         {
         recoveryPathname = Util.getTempFileName().toString();
         recoveryFile     = new XFile(recoveryPathname);
         }
      catch (Exception e)
         {
         cOut.titledSeverityPrintf
            (
            Const.HALT,
            CLASS_NAME,
            "I/O Error: Cannot create temporary recovery file <"  +
            recoveryPathname                                      +
            ">."                                                  +
            " "                                                   +
            "The original file <"                                 +
            UString.toString(pOriginalPathname)                   +
            "> was not changed."
            );
         throw e;
         }
      /*.
      ==========================================================================================
      Make a temporary "Save My Ass" copy of the original file. This throws an Exception if
      anything goes wrong.
      ------------------------------------------------------------------------------------------ */
      try
         {
         recoveryFile.copyFrom(originalFile);
         }
      catch (Exception e)
         {
         cOut.titledSeverityPrintf
            (
            Const.HALT,
            CLASS_NAME,
            "I/O Error: Cannot copy original file <"  +
            originalFile.getCanonicalPath()           +
            "> into recovery file <"                  +
            recoveryFile.getCanonicalPath()           +
            ">."                                      +
            " "                                       +
            "The original file <"                     +
            UString.toString(pOriginalPathname)       +
            "> was not changed."
            );
         throw e;
         }
      /*.
      ==========================================================================================
      Test to make sure the copy was exact. This throws an Exception if anything goes wrong.
      ------------------------------------------------------------------------------------------ */
      if ( ! recoveryFile.contentEquals(originalFile))
         {
         cOut.titledSeverityPrintf
            (
            Const.HALT,
            CLASS_NAME,
            "I/O Error: recovery file <"                    +
            recoveryFile.getCanonicalPath()                 +
            "> is not an exact copy of the original file <" +
            originalFile.getCanonicalPath()                 +
            ">."                                            +
            " "                                             +
            "The original file <"                           +
            UString.toString(pOriginalPathname)             +
            "> was not changed."
            );
         throw new Exception("Copy of original file to recovery file failed");
         }
      /*.
      ==========================================================================================
      For source code files, we do something special. We load the full content of both the
      original and the replacement file into separate XStrings. Then we delete all the
      whitespace out of both XStrings. Then we compare the two XStrings and if they are
      different, we print a warning message and save the original somewhere so the user can
      retrieve it after this file replacement method is done. Often, there are meaningful
      changes made to a source file before this replace method is called, and in those cases the
      user can ignore the warning. If however, the changes were just cosmetic formatting (extra
      or reduced whitespace, including newlines, spaces instead of tabs, etc.) then after
      removing all the whitespace, the two files should be identical. If the user wasn't
      expecting any changes of substance, the warning message will tell him where he can recover
      the original file from.
      ------------------------------------------------------------------------------------------ */
      boolean  keeprecoveryFile  = false;
      XString  extension         = originalFile.getExtension();
      boolean  isSourceFile      =
         (
            extension.equalsIgnoreCase("java")
         || extension.equalsIgnoreCase("c")
         || extension.equalsIgnoreCase("cpp")
         || extension.equalsIgnoreCase("cs")
         || extension.equalsIgnoreCase("cpp")
         || extension.equalsIgnoreCase("h")
         || extension.equalsIgnoreCase("hpp")
         || extension.equalsIgnoreCase("bas")
         || extension.equalsIgnoreCase("vba")
         || extension.equalsIgnoreCase("js")
         );
      if (isSourceFile)
         {
         XString theWholeOriginalFile    = new XString(originalFile   ).replaceAll("\\s+","").replace("/", "");
         XString theWholeReplacementFile = new XString(replacementFile).replaceAll("\\s+","").replace("/", "");
         if (!theWholeOriginalFile.contentEquals(theWholeReplacementFile))
            {
            keeprecoveryFile = true;
            cOut.titledSeverityPrintf
               (
               Const.WARNING,
               CLASS_NAME,
               "Substantive Source Code Change: The file changes will go beyond simple whitespace " +
               "formatting changes to include format-insensitive content changes as well. If "      +
               "that is unexpected, a recovery copy of the original file was saved, and you "       +
               "can restore the original from it if needed.\n"                                      +
               "\n"                                                                                 +
               "=> Original file ------- <"  + originalFile.getCanonicalPath() + ">\n"              +
               "=> Saved recovery file - <"  + recoveryFile.getCanonicalPath() + ">\n"              +
               "\n"                                                                                 +
               "Moving on now to modify the original file . . .\n"                                  +
               " "
               );
            }
         theWholeOriginalFile    = null;
         theWholeReplacementFile = null;
         }
      /*.
      ==========================================================================================
      Copy the replacement file into the original file. This throws an Exception if anything
      goes wrong.
      ------------------------------------------------------------------------------------------ */
      try
         {
         originalFile.copyFrom(replacementFile);
         }
      catch (Exception e)
         {
         cOut.titledSeverityPrintf
            (
            Const.HALT,
            CLASS_NAME,
            "I/O Error: Cannot overwrite original file <" +
            originalFile.getCanonicalPath()               +
            "> from replacement file <"                   +
            replacementFile.getCanonicalPath()            +
            ">. Just in case, a recovery copy of the "    +
            "original file is in <"                       +
            recoveryFile.getCanonicalPath()               +
            ">."
            );
         throw e;
         }
      /*.
      ==========================================================================================
      Test to make sure the copy was exact. This throws an Exception if anything goes wrong.
      ------------------------------------------------------------------------------------------ */
      if ( ! originalFile.contentEquals(replacementFile))
         {
         cOut.titledSeverityPrintf
            (
            Const.HALT,
            CLASS_NAME,
            "I/O Error: Original file <"                       +
            originalFile.getCanonicalPath()                    +
            "> is not an exact copy of the replacement file <" +
            replacementFile.getCanonicalPath()                 +
            ">. A recovery copy of the original file is in <"  +
            recoveryFile.getCanonicalPath()                    +
            ">."
            );
         throw new Exception("Copy of replacement file to original file failed");
         }
      /*.
      ==========================================================================================
      Unless the "Save My Ass" file needs to be kept as a recovery file, delete it.
      ------------------------------------------------------------------------------------------ */
      if (!keeprecoveryFile)
         {
         if (!recoveryFile.delete())
            {
            throw new Exception
               (
               "I/O Error: Cannot delete temporary recovery file. (It's a copy of the original file). " +
               "The requested changes WERE successfully made to the original file. Hence, the "         +
               "recovery file is no longer needed. So an attempt was made to delete it, but failed.\n"  +
               "\n"                                                                                     +
               "=> Original file ----------- <"  + originalFile.getCanonicalPath() + ">\n"              +
               "=> Temporary recovery file - <"  + recoveryFile.getCanonicalPath() + ">\n"              +
               "\n"                                                                                     +
               "Other than investigating why this I/O operation failed and fixing the problem, "        +
               "no user action is required. However, you may want to go occasionally to the "           +
               "temp directory and clean out leftover junk like this unneeded recovery file."
               );
            }
         recoveryFile = null;
         }
      /*.
      ==========================================================================================
      Delete the replacement file.
      ------------------------------------------------------------------------------------------ */
      if ( ! replacementFile.delete())
         {
         throw new Exception
            (
            "I/O Error: Cannot delete temporary replacement file. (It was used during the "    +
            "modification of the original file). "                                             +
            "The requested changes WERE successfully made to the original file and the "       +
            "replacement file is no longer needed. So an attempt was made to delete it, but "  +
            "failed.\n"                                                                        +
            "\n"                                                                               +
            "=> Original file -------------- <"  + originalFile.getCanonicalPath() + ">\n"     +
            "=> Temporary replacement file - <"  + replacementFile.getCanonicalPath() + ">\n"  +
            "\n"                                                                               +
            "Other than investigating why this I/O operation failed and fixing the problem, "  +
            "no user action is required. However, you may want to go occasionally to the "     +
            "temp directory and clean out leftover junk like this unneeded replacement file."
            );
         }
      replacementFile = null;
      }



   /*:                                    :METHOD:083:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method supports a pattern I use over and over. The pattern is that a file is to be modified in
   place. The pathname of the file to modify is provided and must be validated to ensure that it exists
   and that it is not a directory. Then a temporary file is created. A call is made to another method
   (call it the "modifying method") and it is passed the pathnames for the file to modify and the
   temporary file. The modifying method arranges to modify the file by reading the file to modify,
   modifying the content as it reads it, and placing the modified content into the temporary file. When
   it's done, it returns here and this method applies XFile's robust replaceFile() method to replace
   the original file with the temporary file containing the modifications.

   <P><B>Implementation: </B><A HREF="XFile.java.html#083">View source</A>

   @param
      pInPathname is the name of the file to modify
   @param
      pMethodStack is a stack of Methods. This method pops the stack and that method is the one that is
      used as the modifying method . What's left of the stack is passed along to the modifying method
      that is called from in here.
   @param
      pArguments is a variable list of arguments containing anywhere from 0 to n additional arguments
      which this method passes along to the modifying method it calls.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static <Type1> Type1 modifyXFileInPlace(String pInPathname, Stack<Method> pMethodStack, Object... pArguments) throws Exception
      {
      Type1  result;
      /*.
      ==========================================================================================
      Get the arguments we're interested in from pArguments. PArguments[0] is always a
      Stack<Method> with the first element being the method we will call from in this method.
      ------------------------------------------------------------------------------------------ */
      Method  runMethod = pMethodStack.pop();
      /*.
      ==========================================================================================
      Get file for input, create file for output
      ------------------------------------------------------------------------------------------ */
      File  inFile  = new File(pInPathname);
      File  outFile = new XFile(File.createTempFile(CLASS_NAME.toString() + ".modifyXFileInPlace",".tmp"));
      /*.
      ==========================================================================================
      Get file pathnames
      ------------------------------------------------------------------------------------------ */
      String  outPathname = outFile.getCanonicalPath();
      pInPathname = inFile.getCanonicalPath();
      /*.
      ==========================================================================================
      Make sure the input file exists.
      ------------------------------------------------------------------------------------------ */
      if ( ! inFile.exists())
         {
         outFile.delete();
         inFile  = null;
         outFile = null;
         cOut.titledSeverityPrintf(Const.HALT, CLASS_NAME, "I/O Error: <" + pInPathname +"> does not exist.");
         throw new Exception("Input file does not exist.");
         }
      /*.
      ==========================================================================================
      Make sure we're not messing with a directory.
      ------------------------------------------------------------------------------------------ */
      if ( ! inFile.isFile())
         {
         outFile.delete();
         inFile  = null;
         outFile = null;
         cOut.titledSeverityPrintf(Const.HALT, CLASS_NAME, "I/O Error: <" + pInPathname +"> is a directory.");
         throw new Exception("Input file cannot be a directory.");
         }
      /*.
      ==========================================================================================
      We just needed the pathnames so free the Files for the garbage collector.
      ------------------------------------------------------------------------------------------ */
      inFile  = null;
      outFile = null;
      /*.
      ==========================================================================================
      Call the next method in the stack
      ------------------------------------------------------------------------------------------ */
      if (pMethodStack.size() == 0) result = Util.invoke(runMethod, pInPathname, outPathname,               pArguments);
      else                          result = Util.invoke(runMethod, pInPathname, outPathname, pMethodStack, pArguments);
      /*.
      ==========================================================================================
      Delete the original file and replace it with the new file.
      ------------------------------------------------------------------------------------------ */
      XFile.replaceFile(pInPathname, outPathname);
      return result;
      }



   /*:                                    :METHOD:084:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the ConsoleStream for this object.

   <P><B>Implementation: </B><A HREF="XFile.java.html#084">View source</A>

   @return
      A reference to this object

   @param
      pConsole is a ConsoleStream through which this objects sends its output.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XFile setOut(ConsoleStream pConsole)
      {
      cOut = pConsole;
      return this;
      }



   /*:                                    :METHOD:085:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This boilerplate method is used for testing an instantiated object of this class and may include any
   code the developer chooses.

   <P><B>Implementation: </B><A HREF="XFile.java.html#085">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XFile test() throws Exception
      {
      cOut.titledPrintf
         (
         "\"HELLO WORLD!\"",
         "%s  %s  %s",
         "I'm an object of the", CLASS_NAME, "class, and I approved this message."
         );
      return this;
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Protected  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Private  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*.
   ==========================================================================================
   Class Constants
      CLASS_NAME : the name of this class DFLT_LINE_LEN : the default line length for word
      wrapping
   ------------------------------------------------------------------------------------------ */
   private static final XString  CLASS_NAME    = new XString(XFile.class.getName());
   private static final int      DFLT_LINE_LEN = ConsoleMessage.defaultLineLength();
   private static final int      MAX_BKUPS     = 3;
   /*.
   ==========================================================================================
   Class variables
      cOut : console output.
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();
   /*.
   ==========================================================================================
   Instance variables
      iOut : console output. ICipher : the cipher object used to encrypt/decrypt this XFile
   ------------------------------------------------------------------------------------------ */
   private FileCipher     iCipher = null;



   /*:                                    :METHOD:086:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method deletes the file or directory denoted by this abstract pathname.

   <P><B>Implementation: </B><A HREF="XFile.java.html#086">View source</A>

   @return
      True if and only if the file or directory is successfully deleted; false otherwise
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private static boolean deleteFile(File pFile)
      {
      /*.
      ==========================================================================================
      HACK (NEW) - 2013.12.08: Got this additional tip from:
         Http://stackoverflow.com/questions/991489/i-cant-delete-a-file-in-java
      ------------------------------------------------------------------------------------------ */
      pFile.setWritable(true);
      /*.
      ==========================================================================================
      HACK - I should just be able to call delete() here and return its result. In fact, I used
      to do just that and this method always worked fine. Now with this new version of Java
      (1.5.0), delete (and other file methods) sometimes don't work on the first try. This is
      because file objects that have been closed are hung onto, pending garbage collection. By
      suggesting garbage collection, the next time, the delete() usually (but not always) works.
      I'm not the only one with this issue. See: http://java.itags.org/java-essentials/79385/
      ------------------------------------------------------------------------------------------ */
      for (int i=0; i<20; i++)
         {
         if (pFile.delete())
            {
            return true;
            }
         System.gc();
         Util.sleep(50);
         }
      return false;
      }



   /*:                                    :METHOD:087:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method deletes the file or directory denoted by this abstract pathname.

   <P><B>Implementation: </B><A HREF="XFile.java.html#087">View source</A>

   @return
      True if and only if the file or directory is successfully deleted; false otherwise
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private boolean deleteFile() throws IOException
      {
      File file = new File(this.getCanonicalPath());
      return deleteFile(file);
      }



   /*:                                    :METHOD:088:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method wipes the file denoted by this abstract pathname.<P>

   WipeFile() is a security tool, which allows you to completely remove sensitive data from your hard
   disk by overwriting it with random data.

   Caveats:
      1) On a journaling filesystem like NTFS there is actually no way to securely erase a single file
         Without wiping all the free space on the drive. The problem is that the new blocks (which
         you've presumably overwritten with random data) are not guaranteed to be in the same place on
         disk as the old ones.

      2) On systems with buffered IO subsystems, there is no guarantee that this method will work to
         Actually overwrite the file blocks on disk because the IO subsystem buffering mechanism may
         not commit the bytes to disk immediately and there is no way to force it to. It is possible
         that the bytes won't be committed to disk before the file is deleted. In that case, the IO
         subsystem may simply release the contents of its internal buffers and delete the file without
         ever committing the new bytes to disk.

      Therefor, in order to keep your data secure, the only real solution you have is to completely
      encrypt the drive

   <P><B>Implementation: </B><A HREF="XFile.java.html#088">View source</A>

   @return
      True if and only if the file is successfully deleted; false otherwise
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private static boolean wipeFile(File pFile)
      {
      try
         {
         if (pFile.exists())
            {
            /*.
            ==========================================================================================
            1) the size of a file is given as a long. The size of a byte array is assumed by write()
               To be given as an int.

            2) we don't want Java to extend the file size (reallocating blocks and all that) so we
               Don't want to pass in more data than will fit in the existing file (the blocks already
               allocated) because our ultimate purpose here is overwrite those already allocated
               blocks.

            So the largest byte array that can be passed to write() is either the size of the file or
            the maximum integer size, whichever is LESS. Furthermore, an array that is the maximum
            integer size long would be 2Gb and we neither want to allocate that big of an array nor
            have to generate that much random data to fill it with. So we'll allocate the file size or
            a smaller integer of our choice, whichever is less.
            ------------------------------------------------------------------------------------------ */
            long  length   = pFile.length();
            int   numBytes = (int)Math.min(length,(long)(2 * UMath.ONE_MEG));
            if (numBytes > 0)
               {
               byte[]  data = new byte[numBytes];
               /*.
               ==========================================================================================
               We're gonna overwrite the contents of the file with the contents of our byte array which
               we fill with random data
               ------------------------------------------------------------------------------------------ */
               Random  randGenerator = new Random();
               randGenerator.nextBytes(data);
               /*.
               ==========================================================================================
               We're gonna overwrite the contents of a file by treating it as a writable random access
               file in which we set the write pointer to the first byte in the file by using the seek()
               method.
               ------------------------------------------------------------------------------------------ */
               RandomAccessFile  raFile = new RandomAccessFile(pFile, "rws");
               raFile.seek(0);
               /*.
               ==========================================================================================
               Write the random data byte array to the file as many times as it will completely fit
               ------------------------------------------------------------------------------------------ */
               for (long i=1; i<(length/numBytes); i++)
                  {
                  raFile.write(data);
                  }
               /*.
               ==========================================================================================
               There will likely be a small chunk of the file remaining at the end (the last bit that is
               less than numBytes long). Figure out how big it is, allocate a new byte array exactly that
               long, fill it with random data and write it out to the end of the file.
               ------------------------------------------------------------------------------------------ */
               numBytes = (int)(length % (long)numBytes);
               if (numBytes > 0)
                  {
                  data = new byte[numBytes];
                  randGenerator.nextBytes(data);
                  raFile.write(data);
                  }
               /*.
               ==========================================================================================
               There is no guarantee that this method will work to actually overwrite the file blocks on
               disk because the IO subsystem buffering mechanism may not commit the bytes to disk
               immediately and there is no way to force it to. It is possible that the bytes won't be
               committed to disk before the file is deleted in the next code section of this method. In
               that case, the IO subsystem may simply release the contents of its internal buffers and
               delete the file without ever committing the new bytes to disk. This section of code is
               intended to give the IO subsystem a chance to commit the bytes by relinquishing the CPU
               for a bit. It does not guarantee that the IO subsystem will commit the bytes though.
               ------------------------------------------------------------------------------------------ */
               raFile.close();
               Util.sleep(50);
               }
            /*.
            ==========================================================================================
            Hopefully the bytes on disk were overwritten. Go ahead and delete the file now and hope
            for the best.
            ------------------------------------------------------------------------------------------ */
            return deleteFile(pFile);
            }
         return false;
         }
      catch (Exception e)
         {
         return false;
         }
      }



   /*:                                    :METHOD:089:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method wipes the file denoted by this abstract pathname.

   WipeFile() is a security tool, which allows you to completely remove sensitive data from your hard
   disk by overwriting it with random data.

   Caveat: Caveat:
      On a journaling filesystem like NTFS there is actually no way to securely erase a single file
      without wiping all the free space on the drive. The problem is that the new blocks (which you've
      presumably overwritten with random data) are not guaranteed to be in the same place on disk as
      the old ones.

      In order to keep your data secure, the only real solution you have is to completely encrypt the
      drive

   <P><B>Implementation: </B><A HREF="XFile.java.html#089">View source</A>

   @return
      True if and only if the file or directory is successfully wiped; false otherwise
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private boolean wipeFile() throws IOException
      {
      File file = new File(this.getCanonicalPath());
      return wipeFile(file);
      }



   /*:                                    :METHOD:090:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method should not be used except from inside of the public backup() method.

   This method makes a backup of this file if this file is a normal file. It does not do anything if
   this file is a directory. It also backs up any existing backups of this file, up to the number of
   backup files specified by the static value of MAX_BKUPS. This method does the multi backup work for
   backup(). Here is an example of what this method does:

      <BLOCKQUOTE>
         <PRE id="unindent">
            *  if
            *     this is a normal file named \temp\x.x
            *     and MAX_BKUPS is 3
            *     and the backup directory is \backup
            *
            *  then the first time this method is called on \temp\x.x
            *     \temp\x.x is copied to \backup\temp\x.x.1
            *
            *  and the second time this method is called on \temp\x.x
            *     first, \backup\temp\x.x.1 is copied to \backup\temp\x.x.2
            *     then,  \temp\x.x          is copied to \backup\temp\x.x.1
            *
            *  and the third and all subsequent times this method is called on \temp\x.x
            *     first, \backup\temp\x.x.2 is copied to \backup\temp\x.x.3
            *     next,  \backup\temp\x.x.1 is copied to \backup\temp\x.x.2
            *     then,  \temp\x.x          is copied to \backup\temp\x.x.1
         </PRE>
      </BLOCKQUOTE>

   This method preserves file dates. In other words, it sets the file dates on the backup file to match
   the dates on the file that the backup file backs up.

   <P><B>Implementation: </B><A HREF="XFile.java.html#090">View source</A>

   @param
      pBackupDirName is the name of the directory that this file will be backed up into. Set this value
      to null for the first call to this recursive method.
   @param
      pBackupFileName is the name of the file to be backed up. Set this value to null for the first
      call to this recursive method.
   @param
      pBackupRecursionLevel is the current recursion level for this method. Set this value to 0 for the
      first call to this recursive method.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private <Type1,Type2> void backupNormalFile
      (
      Type1  pBackupDirName,
      Type2  pBackupFileName,
      int    pBackupRecursionLevel
      )
      throws Exception
      {
      XString  backupDirName  = XString.toXString(pBackupDirName);
      XString  backupFileName = XString.toXString(pBackupFileName);
      /*.
      ==========================================================================================
      This method does not do back ups of directories. Use the public method backup() instead
      for directories.
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory())
         {
         return;
         }
      /*.
      ==========================================================================================
      This is a recursive method. This code is what stops the recursion. If all MAX_BKUPS have
      already been backed up then this code stops any further back up efforts and allows the
      recursion process to unwind.
      ------------------------------------------------------------------------------------------ */
      if (pBackupRecursionLevel >= MAX_BKUPS)
         {
         return;
         }
      /*.
      ==========================================================================================
      If this is the first call to this recursive method, then initialize the arguments and
      build the backup directory path for this file.
      ------------------------------------------------------------------------------------------ */
      if (pBackupRecursionLevel == 0)
         {
         backupFileName  = new XString(this.getName());
         backupDirName   = new XString(Const.BKUPDIR.concat(this.getDrivelessParent()));
         File  backupDir = new File(UString.toString(backupDirName));
         backupDir.mkdirs();
         }
      /*.
      ==========================================================================================
      Keep track of what level of recursion we are at so that the recursion process can be
      stopped after the specified number of recursions.
      ------------------------------------------------------------------------------------------ */
      pBackupRecursionLevel++;
      /*.
      ==========================================================================================
      Figure out what the name of the backup file will be and create a file object for it
      ------------------------------------------------------------------------------------------ */
      XString  backupName = new XString
         (
         backupDirName.string()   +
         "/"                      +
         backupFileName.string()  +
         "."                      +
         pBackupRecursionLevel
         );
      XFile  backupFile = new XFile(backupName);
      /*.
      ==========================================================================================
      Recursively call this method to make a back up of this file's backup file.
      ------------------------------------------------------------------------------------------ */
      backupFile.backupNormalFile(backupDirName,backupFileName,pBackupRecursionLevel);
      /*.
      ==========================================================================================
      Now back up this file.
      ------------------------------------------------------------------------------------------ */
      if (this.exists())
         {
         this.copyTo(backupFile);
         }
      /*.
      ==========================================================================================
      Pop the recursion level because we're going to return from this recursive call.
      ------------------------------------------------------------------------------------------ */
      pBackupRecursionLevel--;
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Inner Classes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Public Static Methods  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:                                    :METHOD:091:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method allows this class file to be unit tested as a standalone application. It's the method
   that's called when the class is invoked from the command line by using the java application launcher
   - "java". Main() is not a required method, but the practice of putting one in each class and
   wrapping class test code within it allows easy unit testing of the class; and main does not need to
   be removed when testing is complete.

   <P>
      <DL>
         <DT>
            <B>
               Command line usage:
            </B>
            <DD>
               Java cosmicabyss.com.lib.XFile
            </DD>
         </DT>
      </DL>

   <P><B>Implementation: </B><A HREF="XFile.java.html#091">View source</A>

   @param
      pArgs contains the command line arguments with which this class was invoked as an application.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static void main(String[] pArgs) throws Exception
      {
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      XFile f1 = new XFile("C:/ABDITORY/bat/9xx/Cmplr/LLVM/config.guess");
      f1.setCreated(1992, 6, 15);
      f1.setLastModified(2017, 3, 5);

      XFile f2 = new XFile("C:/ABDITORY/bat/9xx/Cmplr/LLVM/config.sub");
      f2.setCreated(1992, 7, 15);
      f2.setLastModified(2017, 4, 2);
      }



   }  // class XFile



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Notes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */