/*::.
==================================================================================================================================
=================================================¦ Copyright © 2002 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       TextFile.java
Originator: Allen Baker (2002.12.30)
LayoutRev:  5
================================================================================================================================== */



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



/*.
==========================================================================================
Imports
------------------------------------------------------------------------------------------ */
import java.io.*;
import java.util.*;
import java.net.*;
import java.lang.reflect.*;
import java.lang.invoke.*;


/*::
======================================================================================================================== *//**
Instances of this class represent Files containing lines of text. A lot of things can be done with a file once you know
that it is this TextFile and this class provides methods to do many of those things. Most of the methods provided here
operate on the individual lines of this TextFile, permitting such things as de/entabbing, string replacement, trimming
in various ways, sorting, tabularizing, etc.. And since TextFile is a subclass of XFile, all the things that can be done
with an XFile can also be done with a TextFile.

<P>
   <DL>
      <DT>
         <B>
            Example usage:
         </B>
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  If the file named test.tmp comprises these lines:

                     1      22     333 4444  55555
                     1      22     333 4444  55555
                     11111 2222  333   44   5
                     1      22     333
                     1

                  then this code:
                     '
                     =========================================================================================
                     create a TextFile to play with
                     ----------------------------------------------------------------------------------------- '
                     TextFile  tf   = new TextFile("test.tmp");
                     '
                     =========================================================================================
                     detab and trim the file
                     ----------------------------------------------------------------------------------------- '
                     tf.detabAndTrim();
                     '
                     =========================================================================================
                     replace all the "2" strings in the file with "XY" strings
                     ----------------------------------------------------------------------------------------- '
                     tf.replace("2","XY");
                     '
                     =========================================================================================
                     tabularize the file
                     ----------------------------------------------------------------------------------------- '
                     tf.tabularize();
                     '
                     =========================================================================================
                     display the file
                     ----------------------------------------------------------------------------------------- '
                     Iterator  iter = tf.iterator();
                     while (iter.hasNext())
                        {
                        System.out.println((XString)iter.next());
                        }

                  will change test.tmp to look like this:

                     1      XYXY      333  4444  55555
                     1      XYXY      333  4444  55555
                     11111  XYXYXYXY  333  44    5
                     1      XYXY      333
                     1
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="TextFile.java.html">
               TextFile.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class TextFile extends XFile
   {



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



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the TextFile class.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public TextFile(XFile xfile) throws Exception
      {
      super(xfile.getCanonicalPath());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the TextFile class.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public TextFile(TextFile tfile) throws Exception
      {
      super(tfile.getCanonicalPath());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the TextFile class.

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

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



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the TextFile class.

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

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



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the TextFile class.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> TextFile(File parent, Type1 child)
      {
      super(parent,child);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the TextFile class.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> TextFile(Type1 pathname)
      {
      super(pathname);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the TextFile class.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> TextFile(Type1 parent, Type2 child)
      {
      super(parent,child);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method searches each line of this TextFile for the string specified by pStr. As soon as it
   finds the first instance of the string, it stops searching and immediately returns true. If it
   reaches the end of this TextFile without finding the string, it returns false.

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

   @return
      This method returns true if this TextFile contains any instance of the search string; otherwise
      it returns false.


   @param
      pStr is an object whose string representation is used as the "search string." This TextFile is
      searched for any instance of the search string.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> boolean containsString(Type1 pStr) throws Exception
      {
      boolean  result = false;
      XString  str    = XString.toXString(pStr);
      /*.
      ==========================================================================================
      Get the pathname of the input file
      ------------------------------------------------------------------------------------------ */
      String  inPathname = this.getCanonicalPath();
      /*.
      ==========================================================================================
      Make sure the input file exists.
      ------------------------------------------------------------------------------------------ */
      if ( ! this.exists())
         {
         cOut.titledSeverityPrintf(Const.HALT, CLASS_NAME, "I/O Error: <" + inPathname +"> does not exist.");
         throw new Exception("Input file does not exist.");
         }
      /*.
      ==========================================================================================
      Make sure we're not messing with a directory.
      ------------------------------------------------------------------------------------------ */
      if ( ! this.isFile())
         {
         cOut.titledSeverityPrintf(Const.HALT, CLASS_NAME, "I/O Error: <" + inPathname +"> is a directory.");
         throw new Exception("Input file cannot be a directory.");
         }
      /*.
      ==========================================================================================
      Set up the input iterator and output writer
      ------------------------------------------------------------------------------------------ */
      XStringsIterator  iter = new XStringsIterator(inPathname);
      while (iter.hasNext())
         {
         if (iter.next().contains(str))
            {
            iter.cleanup();
            result = true;
            break;
            }
         }
      return result;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method compares the content of this TextFile with another TextFile.

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

   @return
      This method returns true if the content of the two file is the same, otherwise it returns false.

   @param
      pOtherFile is the other TextFile to compare this TextFile to.
   @param
      pIgnoreWhitespace tells this method to ignore whitespace if it is set to true. If false, this
      method does not ignore whitespace.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean contentEquals(TextFile pOtherFile, boolean pIgnoreWhiteSpace) throws Exception
      {
      StringBuffer      thisFileBuf = new StringBuffer(2 * UMath.ONE_MEG);
      StringBuffer      thatFileBuf = new StringBuffer(2 * UMath.ONE_MEG);
      XStringsIterator  thisIter    = this.xstringsIterator();
      XStringsIterator  thatIter    = pOtherFile.xstringsIterator();
      /*.
      ==========================================================================================
      copy all the content of each TextFile into its own StringBuffer.
      ------------------------------------------------------------------------------------------ */
      while (thisIter.hasNext())
         {
         thisFileBuf.append(thisIter.next().concat("\n").toString());
         }
      while (thatIter.hasNext())
         {
         thatFileBuf.append(thatIter.next().concat("\n").toString());
         }
      thisIter.cleanup();
      thatIter.cleanup();
      /*.
      ==========================================================================================
      make each StringBuffer file representation into an XString file representation
      ------------------------------------------------------------------------------------------ */
      XString  thisFile = new XString(thisFileBuf.toString());
      XString  thatFile = new XString(thatFileBuf.toString());
      /*.
      ==========================================================================================
      free up all the memory that those two StringBuffers were consuming.
      ------------------------------------------------------------------------------------------ */
      thisFileBuf = null;
      thatFileBuf = null;
      /*.
      ==========================================================================================
      If we're ignoring whitespace, remove all of it from both XString file representations.
      ------------------------------------------------------------------------------------------ */
      if (pIgnoreWhiteSpace)
         {
         thisFile = thisFile.removeWhitespace();
         thatFile = thatFile.removeWhitespace();
         }
      /*.
      ==========================================================================================
      Compare the XString file representations for equality and return the result
      ------------------------------------------------------------------------------------------ */
      return thisFile.equals(thatFile);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method compares the content of this TextFile, including whitespace, with another TextFile.

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

   @return
      This method returns true if the content of the two file is the same, otherwise it returns false.

   @param
      pOtherFile is the other TextFile to compare this TextFile to.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean contentEquals(TextFile pOtherFile) throws Exception
      {
      return contentEquals(pOtherFile, false);
      }


   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method facilitates applying the "modify file in place" pattern to this TextFile. This pattern
   creates the "appearance" that this TextFile is modified in place. When this method completes, this
   TextFile's content has been modified. The pattern is characterized by the following procedure:
      <BLOCKQUOTE>
         => Open this TextFile for reading.<BR>
         => Open a new file for writing.<BR>
         => Get an iterator for reading this TextFile.<BR>
         => Get a writer for writing to the file opened for writing.<BR>
         => Pass the iterator and writer into a method (a delegate) that reads this TextFile using the
            Iterator, modifies its content, and writes the modified content to the new file using the
            writer.<BR>
         => Clean up and close the iterator.<BR>
         => Flush and close the writer.<BR>
         => Replace this TextFile with the new file that was opened for writing.
      </BLOCKQUOTE>

   The key feature of this method is that it is passed the name of a modification method as an argument
   through its pMethodName parameter, and an arbitrary set of additional arguments through it pArguments
   parameter. It identifies the class of this TextFile (it may be a TextFile instance or an instance of
   a subclass of TextFile) and uses that information along with the modification method's name and the
   additional arguments to find out what the modification method's return type is. It passes all this
   information along to a method that performs all the logistical work of opening files, creating
   iterators and writers, cleaning up iterators, flushing and closing writers, and replacing the input
   file with the output file. That method delegates to the modification method the task of actually
   modifying the content of the input file and putting it into the output file.

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

   @return
      This method returns whatever the modification method returns, if anything.

   @param
      pMethodName is the name of a modification method.
   @param
      pArguments is a variable length list of arguments [0..n arguments] to be passed to the
      modification method when it is called. Each actual modification method has its own expectations
      regarding what that arguments in the list are and what their types are.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   @SuppressWarnings("unchecked")
   public <Type1> Type1 modify(String pMethodName, Object... pArguments) throws Exception
      {
      Class<?>  thisClass  = this.getClass();
      Method    thisMethod = thisClass.getMethod(pMethodName, XStringsIterator.class, TextWriter.class, Object[].class);
      return (Type1)modify(thisMethod.getReturnType(), pMethodName, pArguments);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method applies the "modify file in place" pattern to this TextFile. This pattern creates the
   "appearance" that this TextFile is modified in place. When this method completes, this TextFile's
   content has been modified. This method performs the following procedure:
      <BLOCKQUOTE>
         => Open this TextFile for reading.<BR>
         => Open a new file for writing.<BR>
         => Get an iterator for reading this TextFile.<BR>
         => Get a writer for writing to the file opened for writing.<BR>
         => Pass the iterator and writer into a method (a delegate) that reads this TextFile using the
            Iterator, modifies its content, and writes the modified content to the new file using the
            writer.<BR>
         => Clean up and close the iterator.<BR>
         => Flush and close the writer.<BR>
         => Replace this TextFile with the new file that was opened for writing.
      </BLOCKQUOTE>

   The key feature of this method is that it encapsulates and hides all the reflection-like code that
   is necessary to use the modification method as a delegate that modifies the content of the input
   file and puts it into the output file.

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

   @return
      This method returns whatever the modification method returns, if anything.

   @param
      pReturnType is the class or type returned by the modification method.
   @param
      pMethodName is the name of a modification method.
   @param
      pArguments is a variable length list of arguments [0..n arguments] to be passed to the
      modification method when it is called. Each actual modification method has its own expectations
      regarding what that arguments in the list are and what their types are.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1, Type2> Type1 modify(Class<Type1> pReturnType, Type2 pMethodName, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Figure out the method to call from the parameters
      ------------------------------------------------------------------------------------------ */
      String                methodName = UString.toString(pMethodName);
      MethodHandles.Lookup  lookup     = MethodHandles.lookup();
      MethodType            signature  = MethodType.methodType(pReturnType, XStringsIterator.class, TextWriter.class, pArguments.getClass());
      MethodHandle          modifer    = lookup.findVirtual(this.getClass(), methodName, signature);
      /*.
      ==========================================================================================
      This will catch the return value from modifier and return it from this method.
      ------------------------------------------------------------------------------------------ */
      Type1  result;
      /*.
      ==========================================================================================
      Get the pathname of the input file (the file this method will modify "in place").
      ------------------------------------------------------------------------------------------ */
      String  inPathname = this.getCanonicalPath();
      /*.
      ==========================================================================================
      Get file for input, create file for output
      ------------------------------------------------------------------------------------------ */
      File  inFile  = new File(inPathname);
      File  outFile = new XFile(Util.getTempFileName());
      /*.
      ==========================================================================================
      Get the pathname of the output file.
      ------------------------------------------------------------------------------------------ */
      String  outPathname = outFile.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: <" + inPathname +"> 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: <" + inPathname +"> 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;
      /*.
      ==========================================================================================
      Set up the input iterator and output writer
      ------------------------------------------------------------------------------------------ */
      XStringsIterator  iter   = new XStringsIterator(inPathname);
      TextWriter        output = new TextWriter(outPathname);
      /*.
      ==========================================================================================
      MethodHandle.invoke throws a Throwable instead of an Exception. But we don't want to throw
      a Throwable out of this method because every method up the call chain would have to be
      modified to throw Throwables if we did. So catch it if it happens and convert it into an
      Exception and throw that instead.
      ------------------------------------------------------------------------------------------ */
      try
         {
         /*.
         ==========================================================================================
         Call the method that was passed in to modify the file.
         ------------------------------------------------------------------------------------------ */
         result = (Type1)modifer.invoke(this, iter, output, pArguments);
         }
      catch (Throwable cause)
         {
         throw new Exception(cause);
         }
      /*.
      ==========================================================================================
      Make sure the iterator is all tidied up in case it was abandoned before reaching the end
      of file.
      ------------------------------------------------------------------------------------------ */
      iter.cleanup();
      iter = null;
      /*.
      ==========================================================================================
      Flush the output's buffers to the OS, and close the file.
      ------------------------------------------------------------------------------------------ */
      output.flush();
      output.close();
      output = null;
      /*.
      ==========================================================================================
      Delete the original file and replace it with the new file.
      ------------------------------------------------------------------------------------------ */
      XFile.replaceFile(inPathname, outPathname);
      return result;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns an Iterator over the lines in this TextFile. This TextFile is treated as an
   ordered list of Strings. Each String contains one line of this TextFile. This provides an
   alternative paradigm for reading this TextFile from the one provided by the BufferedReader or
   TextReader class.

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

   @return
      An iterator over the lines in this TextFile in proper sequence.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public TextReaderIterator iterator() throws Exception
      {
      return new TextReaderIterator(this.getCanonicalPath());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns an Iterator over the chars in this TextFile. This TextFile is treated as an
   ordered list of chars. This provides an alternative paradigm for reading this TextFile from the one
   provided by the BufferedReader or TextReader class.

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

   @return
      An iterator over the chars in this TextFile in proper sequence.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public CharIterator charIterator() throws Exception
      {
      return new CharIterator(this.getCanonicalPath());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns an Iterator over the lines in this TextFile. This TextFile is treated as an
   ordered list of XStrings. Each XString contains one line of this TextFile. This provides an
   alternative paradigm for reading this TextFile from the one provided by the BufferedReader or
   TextReader class.<P>

   The advantage of using a XStringsIterator instead of a TextReaderIterator is that you don't have to
   explicitly cast the objects returned by next() and peek() to XString.

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

   @return
      An iterator over the lines in this TextFile in proper sequence.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XStringsIterator xstringsIterator() throws Exception
      {
      return new XStringsIterator(this.getCanonicalPath());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns an Iterator over the lines in this TextFile. This TextFile is treated as an
   ordered list of XStrings. Each XString contains one line of this TextFile. This provides an
   alternative paradigm for reading this TextFile from the one provided by the BufferedReader or
   TextReader class.<P>

   The advantage of using a XStringsIterator instead of a TextReaderIterator is that you don't have to
   explicitly cast the objects returned by next() and peek() to XString.

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

   @return
      An iterator over the lines in this TextFile in proper sequence.

   @param
      pCaseSensitive specifies whether or not the new XString is case sensitive.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XStringsIterator xstringsIterator(boolean pCaseSensitive) throws Exception
      {
      return new XStringsIterator(this.getCanonicalPath(),pCaseSensitive);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a convenience object for buffered reading of this TextFile. A TextReader reads
   text from this TextFile as a character-input stream, buffering characters so as to provide efficient
   reading.

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

   @return
      A buffered reader for reading the lines in this TextFile in proper sequence.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public TextReader reader() throws Exception
      {
      return new TextReader(this.getCanonicalPath());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a convenience object for buffered writing of this TextFile. A TextWriter writes
   text to this TextFile as a character-output stream, buffering characters so as to provide efficient
   writing.

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

   @return
      A buffered writer for writing the lines to this TextFile in proper sequence.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public TextWriter writer() throws Exception
      {
      return new TextWriter(this.getCanonicalPath());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a PrintWriter for writing to this TextFile.

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

   @return
      A PrintWriter for writing to this TextFile.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public PrintWriter printWriter() throws Exception
      {
      return new PrintWriter(this.writer());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method does a case-sensitive replacment of all instances of pOldString with pNewString
   throughout this TextFile.

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

   @param
      pOldString is the substring that will be replaced.
   @param
      pNewString is the XString that will replace the pOldString.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> void replace(Type1 pOldString, Type2 pNewString) throws Exception
      {
      this.replace(pOldString, pNewString, true);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces all instances of pOldString with pNewString throughout this TextFile.

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

   @param
      pOldString is the substring that will be replaced.
   @param
      pNewString is the XString that will replace the pOldString.
   @param
      pCaseSensitive specifies whether or not to use case considerations. True means case sensitive,
      false means case in-sensitive.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> void replace(Type1 pOldString, Type2 pNewString, boolean pCaseSensitive) throws Exception
      {
      XString[][] strPairs =
         {
         {XString.toXString(pOldString), XString.toXString(pNewString)}
         };
      this.replace(strPairs, pCaseSensitive);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   For each pair of Strings in pStrPairs, this method does a case-sensitive replacment of all instances
   of the first String in the pair with the second String in the pair throughout this TextFile.

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

   @param
      pStrPairs is an array of pairs of Objects that will be converted into their String
      representations. That is, each element of the array contains an array of two Objects whose String
      representations will be used. The first of the two Strings is the String that will be replaced.
      The second is the String that will replace the first.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void replace(Type1[][] pStrPairs) throws Exception
      {
      this.replace(pStrPairs, true);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   For each pair of Strings in pStrPairs, this method replaces all instances of the first String in the
   pair with the second String in the pair throughout this TextFile.

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

   @param
      pStrPairs is an array of pairs of Objects that will be converted into their String
      representations. That is, each element of the array contains an array of two Objects whose String
      representations will be used. The first of the two Strings is the String that will be replaced.
      The second is the String that will replace the first.
   @param
      pCaseSensitive specifies whether or not to use case considerations. True means case sensitive,
      false means case in-sensitive.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void replace(Type1[][] pStrPairs, boolean pCaseSensitive) throws Exception
      {
      this.modify("replace", pStrPairs, pCaseSensitive);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is String replacement. It receives an argument that is an
   array of 2-element arrays. Each 2-element array contains 2 Strings. The first String is text to
   search for in this TextFile and the second String is a replacement for each occurrence of the first
   String that is found.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: Type1[][] strPairs
               <BLOCKQUOTE>
                  An array of pairs of Objects that will be converted into their String
                  representations. That is, each element of the array contains an array of two Objects
                  whose String representations will be used. The first of the two strings is the String
                  that will be replaced. The second is the String that will replace the first.
               </BLOCKQUOTE>
            pArguments[1]: boolean caseSensitive
               <BLOCKQUOTE>
                  A boolean that specifies whether or not to use case considerations. True means case
                  sensitive, false means case in-sensitive.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   @SuppressWarnings("unchecked")
   public <Type1> void replace(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      Type1[][]  strPairs      = (Type1[][])pArguments[0];
      boolean    caseSensitive = (boolean)pArguments[1];
      /*.
      ==========================================================================================
      Read each line of this TextFile replacing all the substrings and writing the modified
      lines to the pOutput file. The EOL that is lost when reading from a TextReader is
      reattached so that replacement searches will work with EOL.
      ------------------------------------------------------------------------------------------ */
      StringBuilder  theWholeFile = new StringBuilder(2 * UMath.ONE_MEG);
      while (pIter.hasNext())
         {
         XString  s = pIter.next();
         s = s.trimRight();
         s = s.concat("\n");
         theWholeFile.append(s.toString());
         }
      XString wholeFile = new XString(theWholeFile.toString());
      theWholeFile = null;
      if (caseSensitive)
         {
         for (int i=0; i<strPairs.length; i++)
            {
            wholeFile = wholeFile.replace(strPairs[i][0],strPairs[i][1]);
            }
         }
      else
         {
         for (int i=0; i<strPairs.length; i++)
            {
            wholeFile = wholeFile.replaceIgnoreCase(strPairs[i][0],strPairs[i][1]);
            }
         }
      wholeFile = wholeFile.trimRight("\n");
      pOutput.print(wholeFile);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces all instances throughout this TextFile of substrings matching the regular
   expression in pOldString with a string generated from the regular expression in pNewString.

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

   @param
      pOldString is the regular expression to which this text in this TextFile is to be matched.
   @param
      pNewString is the the string to be substituted for each match.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> void replaceRegex(Type1 pOldString, Type2 pNewString) throws Exception
      {
      XString[][] strPairs =
         {
         {XString.toXString(pOldString), XString.toXString(pNewString)}
         };
      this.replaceRegex(strPairs);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   For each pair of regular expression Strings in pStrPairs, this method replaces all instances
   throughout this TextFile of substrings matching the regular expression of the first String in the
   pair with the String generated from the regular expression in the second String in the pair.

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

   @param
      pStrPairs is an array of pairs of Objects that will be converted into their String
      representations. That is, each element of the array contains an array of two Objects whose String
      representations will be used. The first of the two Strings is the String that will be replaced.
      The second is the String that will replace the first.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void replaceRegex(Type1[][] pStrPairs) throws Exception
      {
      /*.
      ==========================================================================================
      false is a dummy argument. It is not looked for. If it is not there in the argument list,
      the Java compiler complains about the 2-dimentional array -- pStrPairs -- being passed as
      the only memberof the variable argument list. Java thinks the 2D array *is* the variable
      argument list.
      ------------------------------------------------------------------------------------------ */
      this.modify("replaceRegex", pStrPairs, false);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is String replacement. It receives an argument that is an
   array of 2-element arrays. Each 2-element array contains 2 Strings. The first String is a regular
   expression to search for in this TextFile and the second String is a regular expression from which
   the replacement is generated for each occurrence of the first String that is found.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: Type1[][] strPairs
               <BLOCKQUOTE>
                  An array of pairs of Objects that will be converted into their String
                  representations. That is, each element of the array contains an array of two Objects
                  whose String representations will be used. The first of the two strings is the String
                  that will be replaced. The second is the String that will replace the first.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   @SuppressWarnings("unchecked")
   public <Type1> void replaceRegex(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      XString[][]  strPairs = (XString[][])pArguments[0];
      /*.
      ==========================================================================================
      Read each line of this TextFile replacing all the substrings and writing the modified
      lines to the pOutput file. The EOL that is lost when reading from a TextReader is
      reattached so that replacement searches will work with EOL.
      ------------------------------------------------------------------------------------------ */
      StringBuilder  theWholeFile = new StringBuilder(UMath.ONE_MEG);
      while (pIter.hasNext())
         {
         XString  s = pIter.next();
         s = s.trimRight();
         s = s.concat("\n");
         theWholeFile.append(s.toString());
         }
      XString wholeFile = new XString(theWholeFile.toString());
      theWholeFile = null;
      for (int i=0; i<strPairs.length; i++)
         {
         wholeFile = wholeFile.replaceAll(strPairs[i][0],strPairs[i][1]);
         }
      wholeFile = wholeFile.trimRight("\n");
      pOutput.print(wholeFile);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method does a case-sensitive replacment of all instances of pOldString with pNewString
   throughout this TextFile.<P>

   This method repeatedly checks the resulting TextFile for occurrences of pOldString and replaces them
   if they exist. The checking continues untill there are no occurrences of pOldString in the result.
   The reason for this is to handle situations like this: say that thisFile = "AAA", pOldString = "AA",
   and pNewString = "A". Running replace() one time would result in this TextFile: "AA". Running
   iteratingReplace() one time would result in this TextFile: "A".

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

   @param
      pOldString is the substring that will be replaced.
   @param
      pNewString is the XString that will replace the pOldString.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> void iteratingReplace(Type1 pOldString, Type2 pNewString) throws Exception
      {
      this.iteratingReplace(pOldString,pNewString,true);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces all instances of pOldString with pNewString throughout this TextFile.<P>

   This method repeatedly checks the resulting TextFile for occurrences of pOldString and replaces them
   if they exist. The checking continues untill there are no occurrences of pOldString in the result.
   The reason for this is to handle situations like this: say that thisFile = "AAA", pOldString = "AA",
   and pNewString = "A". Running replace() one time would result in this TextFile: "AA". Running
   iteratingReplace() one time would result in this TextFile: "A".

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

   @param
      pOldString is the substring that will be replaced.
   @param
      pNewString is the XString that will replace the pOldString.
   @param
      pCaseSensitive specifies whether or not to use case considerations. True means case sensitive,
      false means case in-sensitive.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> void iteratingReplace(Type1 pOldString, Type2 pNewString, boolean pCaseSensitive) throws Exception
      {
      XString[][] strPairs =
         {
            {XString.toXString(pOldString), XString.toXString(pNewString)}
         };
      this.iteratingReplace(strPairs,pCaseSensitive);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   For each pair of Strings in pStrPairs, this method does a case-sensitive replacment of all instances
   of the first String in the pair with the second String in the pair throughout this TextFile.<P>

   This method repeatedly checks the resulting TextFile for occurrences of the first String in each
   pair and replaces them if they exist. The checking continues untill there are no occurrences of the
   first String in each pair in the result. The reason for this is to handle situations like this: say
   that thisFile = "AAA", the first String in a pair = "AA", and the second String in the pair = "A".
   Running replace() one time would result in this TextFile: "AA". Running iteratingReplace() one time
   would result in this TextFile: "A".

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

   @param
      pStrPairs is an array of pairs of Objects that will be converted into their String
      representations. That is, each element of the array contains an array of two Objects whose String
      representations will be used. The first of the two Strings is the String that will be replaced.
      The second is the String that will replace the first.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void iteratingReplace(Type1[][] pStrPairs) throws Exception
      {
      this.iteratingReplace(pStrPairs, true);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   For each pair of Strings in pStrPairs, this method replaces all instances of the first String in the
   pair with the second String in the pair throughout this TextFile.<P>

   This method repeatedly checks the resulting TextFile for occurrences of the first String in each
   pair and replaces them if they exist. The checking continues untill there are no occurrences of the
   first String in each pair in the result. The reason for this is to handle situations like this: say
   that thisFile = "AAA", the first String in a pair = "AA", and the second String in the pair = "A".
   Running replace() one time would result in this TextFile: "AA". Running iteratingReplace() one time
   would result in this TextFile: "A".

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

   @param
      pStrPairs is an array of pairs of Objects that will be converted into their String
      representations. That is, each element of the array contains an array of two Objects whose String
      representations will be used. The first of the two Strings is the String that will be replaced.
      The second is the String that will replace the first.
   @param
      pCaseSensitive specifies whether or not to use case considerations. True means case sensitive,
      false means case in-sensitive.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void iteratingReplace(Type1[][] pStrPairs, boolean pCaseSensitive) throws Exception
      {
      this.modify("iteratingReplace", pStrPairs, pCaseSensitive);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is String replacement. It receives an argument that is an
   array of 2-element arrays. Each 2-element array contains 2 Strings. The first String is text to
   search for in the input and the second String is a replacement for each occurrence of the first
   String that is found.<P>

   This method repeatedly checks the resulting TextFile for occurrences of the first String in each
   pair and replaces them if they exist. The checking continues untill there are no occurrences of the
   first String in each pair in the result. The reason for this is to handle situations like this: say
   that thisFile = "AAA", the first String in a pair = "AA", and the second String in the pair = "A".
   Running replace() one time would result in this TextFile: "AA". Running iteratingReplace() one time
   would result in this TextFile: "A".

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: Type1[][] strPairs
               <BLOCKQUOTE>
                  An array of pairs of Objects that will be converted into their String
                  representations. That is, each element of the array contains an array of two Objects
                  whose String representations will be used. The first of the two strings is the String
                  that will be replaced. The second is the String that will replace the first.
               </BLOCKQUOTE>
            pArguments[1]: boolean caseSensitive
               <BLOCKQUOTE>
                  A boolean that specifies whether or not to use case considerations. True means case
                  sensitive, false means case in-sensitive.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   @SuppressWarnings("unchecked")
   public <Type1> void iteratingReplace(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      Type1[][]  strPairs      = (Type1[][])pArguments[0];
      boolean    caseSensitive = (boolean)pArguments[1];
      /*.
      ==========================================================================================
      Read each line of the source file replacing all the substrings and writing the modified
      lines to the pOutput file. The EOL that is lost when reading from a TextReader is
      reattached so that replacement searches will work with EOL.
      ------------------------------------------------------------------------------------------ */
      StringBuilder  theWholeFile = new StringBuilder(2 * UMath.ONE_MEG);
      while (pIter.hasNext())
         {
         XString  s = pIter.next();
         s = s.trimRight();
         s = s.concat("\n");
         theWholeFile.append(s.toString());
         }
      XString wholeFile = new XString(theWholeFile.toString());
      theWholeFile = null;
      if (caseSensitive)
         {
         for (int i=0; i<strPairs.length; i++)
            {
            wholeFile = wholeFile.iteratingReplace(strPairs[i][0],strPairs[i][1]);
            }
         }
      else
         {
         for (int i=0; i<strPairs.length; i++)
            {
            wholeFile = wholeFile.iteratingReplaceIgnoreCase(strPairs[i][0],strPairs[i][1]);
            }
         }
      wholeFile = wholeFile.trimRight("\n");
      pOutput.print(wholeFile);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tabularizes the lines in this TextFile using the default delimiter -- spaces, and the
   default separator between columns -- 2 spaces: " ".

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void tabularize() throws Exception
      {
      this.tabularize(sDelimiters,sSeparator);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tabularizes the lines in this TextFile using the default separator between columns -- 2
   spaces: " ".

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

   @param
      pDelimiters is the set of delimiters (the characters that separate tokens). Delimiter characters
      themselves will not be treated as tokens.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void tabularize(Type1 pDelimiters) throws Exception
      {
      this.tabularize(pDelimiters,sSeparator);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tabularizes the lines in this TextFile.

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

   @param
      pDelimiters is the set of delimiters (the characters that separate tokens). Delimiter characters
      themselves will not be treated as tokens.
   @param
      pSeparator is the XString that is placed between the columns.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> void tabularize(Type1 pDelimiters, Type2 pSeparator) throws Exception
      {
      XString  delimiters = new XString(pDelimiters);
      /*.
      ==========================================================================================
      Read each line of the source file to determine the maximum width of each column in the
      file
      ------------------------------------------------------------------------------------------ */
      XStringsIterator    iter            = new XStringsIterator(this);
      ArrayList<Integer>  maxColumnWidths = new ArrayList<Integer>();
      while (iter.hasNext())
         {
         XString    xstr  = iter.next();
         ArrayList  array = xstr.tokenizedString(delimiters);
         for (int i=0; i<array.size(); i++)
            {
            XString  chunk = (XString)array.get(i);
            int      len   = chunk.length();
            if (i == maxColumnWidths.size())
               {
               maxColumnWidths.add(new Integer(len));
               }
            else
               {
               int max = ((Integer)maxColumnWidths.get(i)).intValue();
               if (len > max)
                  {
                  maxColumnWidths.set(i,new Integer(len));
                  }
               }
            }
         }
      /*.
      ==========================================================================================
      Pass the column widths into a method that tabularizes.
      ------------------------------------------------------------------------------------------ */
      this.modify("tabularize", pDelimiters, pSeparator, maxColumnWidths);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is tabularization. For example:
      <BLOCKQUOTE>
         If this TextFile comprises these lines:
            <BLOCKQUOTE>
               <PRE id="unindent">
                  1 22 333 4444 55555
                  1/22/333/4444/55555
                  11111 2222 333 44 5
                  1 22 333
               </PRE>
            </BLOCKQUOTE>
         Then this code:
            <BLOCKQUOTE>
               <PRE id="unindent">
                  this.tabularize(" /",  ",");
               </PRE>
            </BLOCKQUOTE>
         Will create the file out.tmp to look like this:
            <BLOCKQUOTE>
               <PRE id="unindent">
                  1,    22,  333,4444,55555
                  1,    22,  333,4444,55555
                  11111,2222,333,44,  5
                  1,    22,  333
               </PRE>
            </BLOCKQUOTE>
         And this code:
            <BLOCKQUOTE>
               <PRE id="unindent">
                  TextFile.tabularize("test.tmp",  "out.tmp",  " /",  "---");
               </PRE>
            </BLOCKQUOTE>
         Will create the file out.tmp to look like this:
            <BLOCKQUOTE>
               <PRE id="unindent">
                  1---    22---  333---4444---55555
                  1---    22---  333---4444---55555
                  11111---2222---333---44---  5
                  1---    22---  333
               </PRE>
            </BLOCKQUOTE>
      </BLOCKQUOTE>

            Assuming the separator is "--|--" this algorithm will produce output like this"
               <BLOCKQUOTE>
                  <PRE id="unindent">
                     1    --|--22  --|--333--|--4444--|--55555
                     1    --|--22  --|--333--|--4444--|--55555
                     11111--|--2222--|--333--|--44  --|--5
                     1    --|--22  --|--333--|--    --|--
                  </PRE>
               </BLOCKQUOTE>
            And this separator " | " will produce output like this"
               <BLOCKQUOTE>
                  <PRE id="unindent">
                     1     | 22   | 333 | 4444 | 55555
                     1     | 22   | 333 | 4444 | 55555
                     11111 | 2222 | 333 | 44   | 5
                     1     | 22   | 333 |      |
                  </PRE>
               </BLOCKQUOTE>

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: XString delimiters
               <BLOCKQUOTE>
                  Is an Object whose XString representation is the set of delimiters (the characters
                  that separate tokens). Delimiter characters themselves will not be treated as tokens.
               </BLOCKQUOTE>
            pArguments[1]: XString separator
               <BLOCKQUOTE>
                  Is an Object whose XString representation is used to separate the columns in the
                  output.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   @SuppressWarnings("unchecked")
   public void tabularize(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      XString             delimiters = new XString(pArguments[0]);
      XString             separator  = new XString(pArguments[1]);
      ArrayList<Integer>  colWidths  = (ArrayList<Integer>)(pArguments[2]);
      int                 colCount   = colWidths.size();
      /*.
      ==========================================================================================
      Read each line of the source file again and write a tabularized version of it to the
      pOutput file.
      ------------------------------------------------------------------------------------------ */
      while (pIter.hasNext())
         {
         /*.
         ==========================================================================================
         Get the next line from the file and split it up into delimeters-separated chunks
         ------------------------------------------------------------------------------------------ */
         XString             line       = pIter.next();
         ArrayList<XString>  chunks     = line.tokenizedString(delimiters);
         int                 chunkCount = chunks.size();
         /*.
         ==========================================================================================
         If this row is missing chunks for any columns, add them to the row as empty chunks
         ------------------------------------------------------------------------------------------ */
         while (chunkCount < colCount)
            {
            chunks.add(XString.EMPTY);
            chunkCount++;
            }
         /*.
         ==========================================================================================
         Write each chunk out separated from its succeeding chunk with the separator
         ------------------------------------------------------------------------------------------ */
         for (int i=0; i<chunkCount; i++)
            {
            /*.
            ==========================================================================================
            Get the next chunk and its column width
            ------------------------------------------------------------------------------------------ */
            XString  chunk    = (XString)chunks.get(i);
            int      colWidth = ((Integer)colWidths.get(i)).intValue();
            /*.
            ==========================================================================================
            If this chunk is not the one in the last column, widen it to the column width and concat
            the separator to it and write it
            ------------------------------------------------------------------------------------------ */
            if (i < (colCount - 1))
               {
               chunk = chunk.alignLeft(colWidth, ' ').concat(separator);
               pOutput.print(chunk);
               }
            /*.
            ==========================================================================================
            If this chunk is the one in the last column, don't widen it or concat the separator to it.
            Instead, effectivly concat an end of line to it and write it out.
            ------------------------------------------------------------------------------------------ */
            else
               {
               pOutput.println(chunk);
               }
            }
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces all the tabs in this TextFile with the appropriate number of spaces using the
   default tab size -- 3.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void detab() throws Exception
      {
      this.detab(sTabSize);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces all the tabs in this TextFile with the appropriate number of spaces.

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

   @param
      pTabSize is the number of spaces between tab stops.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void detab(int pTabSize) throws Exception
      {
      this.modify("detab", pTabSize);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is tab replacement. It replaces all the tabs in a file with
   the appropriate number of spaces.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: int tabSize
               <BLOCKQUOTE>
                  Is the number of spaces between tab stops.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void detab(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      int  tabSize = (int)pArguments[0];
      /*.
      ==========================================================================================
      Read each line of the source file and write a detabed version of it to the output file.
      ------------------------------------------------------------------------------------------ */
      while (pIter.hasNext())
         {
         pOutput.println(pIter.next().detab(tabSize));
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces all the spaces in this TextFile with the appropriate number of tabs.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void entab() throws Exception
      {
      this.entab(sTabSize);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces all the spaces in this TextFile with the appropriate number of tabs.

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

   @param
      pTabSize is the number of spaces between tab stops.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void entab(int pTabSize) throws Exception
      {
      this.modify("entab", pTabSize);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is space replacement. It replaces spaces in a file with the
   appropriate number of tabs.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: int tabSize
               <BLOCKQUOTE>
                  Is the number of spaces between tab stops.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void entab(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      int  tabSize = (int)pArguments[0];
      /*.
      ==========================================================================================
      Read each line of the source file and write a entabed version of it to the output file.
      ------------------------------------------------------------------------------------------ */
      while (pIter.hasNext())
         {
         pOutput.println(pIter.next().entab(tabSize));
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces all the tabs in this TextFile with the appropriate number of spaces using the
   default tab size -- 3 and trims any whitespace from the end of the lines.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void detabAndTrim() throws Exception
      {
      /*.
      ==========================================================================================
      DetabAndTrim the file
      ------------------------------------------------------------------------------------------ */
      this.detabAndTrim(sTabSize);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces all the tabs in this TextFile with the appropriate number of spaces and trims
   any whitespace from the end of the lines.

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

   @param
      pTabSize is the number of spaces between tab stops.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void detabAndTrim(int pTabSize) throws Exception
      {
      this.modify("detabAndTrim", pTabSize);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is space replacement and trailing whitespace reoval. It
   replaces spaces in a file with the appropriate number of tabs and deletes any trailing whitespace
   from the line.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: int tabSize
               <BLOCKQUOTE>
                  Is the number of spaces between tab stops.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void detabAndTrim(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      int  tabSize = (int)pArguments[0];
      /*.
      ==========================================================================================
      Read each line of the source file and write a entabed version of it to the output file.
      ------------------------------------------------------------------------------------------ */
      while (pIter.hasNext())
         {
         pOutput.println(pIter.next().detab(tabSize).trimRight());
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces all the spaces in this TextFile with the appropriate number of tabs using the
   default tab size -- 3 and trims any whitespace from the end of the lines.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void entabAndTrim() throws Exception
      {
      /*.
      ==========================================================================================
      DetabAndTrim the file
      ------------------------------------------------------------------------------------------ */
      this.entabAndTrim(sTabSize);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method replaces all the spaces in this TextFile with the appropriate number of tabs and trims
   any whitespace from the end of the lines.

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

   @param
      pTabSize is the number of spaces between tab stops.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void entabAndTrim(int pTabSize) throws Exception
      {
      this.modify("entabAndTrim", pTabSize);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is space replacement and trailing whitespace reoval. It
   replaces spaces in a file with the appropriate number of tabs and deletes any trailing whitespace
   from the line.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: int tabSize
               <BLOCKQUOTE>
                  Is the number of spaces between tab stops.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void entabAndTrim(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      int  tabSize = (int)pArguments[0];
      /*.
      ==========================================================================================
      Read each line of the source file and write a entabed version of it to the output file.
      ------------------------------------------------------------------------------------------ */
      while (pIter.hasNext())
         {
         pOutput.println(pIter.next().entab(tabSize).trimRight());
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method trims any whitespace from the end of the lines.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void trimRight() throws Exception
      {
      this.modify("trimRight");
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is it trims any trailing whitespace from the end of each line.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            This method uses no arguments passed through pArguments.
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void trimRight(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Read each line of the source file and write a trimmed version of it to the output file.
      ------------------------------------------------------------------------------------------ */
      while (pIter.hasNext())
         {
         pOutput.println(pIter.next().trimRight());
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method trims any whitespace from the beginning of the lines.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void trimLeft() throws Exception
      {
      this.modify("trimLeft");
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method does a case-sensitive removal of a substring from the beginning of the lines.

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

   @param
      pPrefix is the substring that will be trimmed from the beginning of the lines.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void trimLeft(Type1 pPrefix) throws Exception
      {
      trimLeft(pPrefix, true);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method removes of a substring from the beginning of the lines.

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

   @param
      pPrefix is the substring that will be trimmed from the beginning of the lines.
   @param
      pCaseSensitive specifies whether or not to use case considerations. True means case sensitive,
      false means not case sensitive.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void trimLeft(Type1 pPrefix, boolean pCaseSensitive) throws Exception
      {
      this.modify("trimLeft", pPrefix, pCaseSensitive);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is it trim either a substring or all the leading whitespace
   from the start of each line.

   If an argument is passed in, this method trims the string representation of it from the beginning
   of each line. If no argument is passed in, this method trims all whitespace from the beginning of
   each line.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: [optional] XString pPrefix
               <BLOCKQUOTE>
                  If this optional argument is passed in, its string representation is removed from the
                  beginning of each line where it is present. If the optional argument is not passed
                  in, all the leading whitespace is removed from the beginning of each line.
               </BLOCKQUOTE>
            pArguments[1]: [optional] boolean caseSensitive
               <BLOCKQUOTE>
                  A boolean that specifies whether or not to use case considerations. True means case
                  sensitive, false means not case sensitive. If this argument is not passed in, the
                  default is case sensitive.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void trimLeft(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      If an argument was passed in, trim the XString representation of it from the beginning of
      each line. If no argument was passed in, trim all whitespace from the beginning of each
      line.
      ------------------------------------------------------------------------------------------ */
      if (pArguments.length > 0)
         {
         XString  prefix = new XString(pArguments[0]);
         /*.
         ==========================================================================================
         If a second argument was passed in, it is a boolean (or better be) that tells us whether
         or not to make case distinctions when looking for substring matches at the beginning of
         each line. The default is: true - make case distinctions.
         ------------------------------------------------------------------------------------------ */
         boolean  caseSensitive = true;
         if (pArguments.length > 1)
            {
            caseSensitive = (boolean)pArguments[1];
            }
         if (caseSensitive)
            {
            while (pIter.hasNext())
               {
               pOutput.println(pIter.next().trimLeft(prefix));
               }
            }
         else
            {
            while (pIter.hasNext())
               {
               pOutput.println(pIter.next().trimLeftIgnoreCase(prefix));
               }
            }
         }
      /*.
      ==========================================================================================
      No prefix was supplied through the parameters so default to removing all leading
      whitespace from each line.
      ------------------------------------------------------------------------------------------ */
      else
         {
         while (pIter.hasNext())
            {
            pOutput.println(pIter.next().trimLeft());
            }
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method trims any whitespace from the beginning and end of the lines.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void trim() throws Exception
      {
      this.modify("trim");
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is it trims any leading or trailing whitespace from the start
   and end of each line.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            This method uses no arguments passed through pArguments.
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void trim(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Read each line of the source file and write a trimmed version of it to the pOutput file.
      ------------------------------------------------------------------------------------------ */
      while (pIter.hasNext())
         {
         pOutput.println(pIter.next().trim());
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method removes all blank lines from the file.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void tossBlankLines() throws Exception
      {
      this.modify("tossBlankLines");
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is that it removes all the blank lines from the file.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            This method uses no arguments passed through pArguments.
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void tossBlankLines(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Read each line of the source file and write a tossed version of it to the pOutput file.
      ------------------------------------------------------------------------------------------ */
      while (pIter.hasNext())
         {
         XString  line = pIter.next();
         if ( ! line.isEmpty())
            {
            pOutput.println(line);
            }
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sorts the lines in a file.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void sort() throws Exception
      {
      this.modify("sort");
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is it sorts the lines of the file.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            This method uses no arguments passed through pArguments.
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void sort(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Read each line of the source file into an array of strings
      ------------------------------------------------------------------------------------------ */
      ArrayList<XString>  strings = new ArrayList<XString>();
      while (pIter.hasNext())
         {
         strings.add(pIter.next());
         }
      /*.
      ==========================================================================================
      Sort the array of strings
      ------------------------------------------------------------------------------------------ */
      strings = (ArrayList<XString>)Util.sortedArrayList(strings);
      /*.
      ==========================================================================================
      Write each XString from the sorted array into the pOutput file.
      ------------------------------------------------------------------------------------------ */
      XStringsIterator  sIter = new XStringsIterator(strings);
      while (sIter.hasNext())
         {
         pOutput.println(sIter.next());
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method dedups the lines in a file.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void dedup() throws Exception
      {
      this.modify("dedup");
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over ·this· TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is it removes duplicate lines from the file.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            This method uses no arguments passed through pArguments.
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void dedup(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Read each line of the source file and compare it to all the other lines read. If it is the
      same as any of the others, throw it out, otherwise, write it to the output.
      ------------------------------------------------------------------------------------------ */
      HashSet<XString>  linesSeen = new HashSet<XString>();
      while (pIter.hasNext())
         {
         XString  line = pIter.next();
         if ( ! linesSeen.contains(line))
            {
            pOutput.println(line);
            linesSeen.add(line);
            }
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method removes newlines from a file.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void removeNewlines() throws Exception
      {
      this.modify("removeNewlines");
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is it removes all the newlines from the file.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            This method uses no arguments passed through pArguments.
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void removeNewlines(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Read each line of the source file and write it out without a newline
      ------------------------------------------------------------------------------------------ */
      XString  line = null;
      while (pIter.hasNext())
         {
         line = pIter.next();
         pOutput.print(line);
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method adds a prefix string to the beginning of each line in this file

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

   @param
      pPrefix is the Object whose string representation is added as a prefix to each line.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void prefixLines(Type1 pPrefix) throws Exception
      {
      XString  prefix = new XString(pPrefix);
      this.modify("prefixLines", prefix);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is it adds a prefix string to the beginning of each line in
   this file.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: XString pPrefix
               <BLOCKQUOTE>
                  pPrefix is the Object whose string representation is added as a prefix to each line.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void prefixLines(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      XString  prefix = new XString(pArguments[0]);
      /*.
      ==========================================================================================
      Read each line of the source file and write it out with it prefix
      ------------------------------------------------------------------------------------------ */
      XString  line = null;
      while (pIter.hasNext())
         {
         line = prefix.concat(pIter.next());
         pOutput.println(line);
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method adds a string (which of course, may include newlines) to the beginning of this file.<P>

   This method is equivalent to insertIntoFile(pStr, 1), where 1 is the number of the first line of
   this file.

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

   @param
      pStr is the Object whose string representation is prepended to this file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void prependToFile(Type1 pStr) throws Exception
      {
      this.insertIntoFile(pStr, 1);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method inserts a string (which of course, may include newlines) into a this file immediately
   preceeding the specified line. If the line number specified is greater than the number of lines in
   this file, the string will be written to the end of the file.

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

   @param
      pStr is the Object whose string representation is inserted into this file.
   @param
      pLineNumber is the line number immediately prior to which the string is inserted into this file.
      If the line number specified is greater than the number of lines in this file, the string will
      be written to the end of the file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void insertIntoFile(Type1 pStr, int pLineNumber) throws Exception
      {
      XString  str = new XString(pStr);
      this.modify("insertIntoFile", str, pLineNumber);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method reads Strings from an iterator over this TextFile (pIter), modifies them, and writes
   them out to an output (pOutput).<P>

   The modification this method performs is it inserts a string (which of course, may include newlines)
   into a this file immediately preceeding a specified line. If the line number specified is greater
   than the number of lines in this file, the string will be written to the end of the file.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: XString pStr
               <BLOCKQUOTE>
                  pStr is the string that is inserted into this file.
               </BLOCKQUOTE>
            pArguments[1]: int pLineNumber
               <BLOCKQUOTE>
                  pLineNumber is the line number immediately prior to which the string is inserted into
                  this file. If the line number specified is greater than the number of lines in this
                  file, the string will be written to the end of the file.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void insertIntoFile(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      XString  str        = new XString(pArguments[0]);
      int      lineNumber = (int)pArguments[1];
      /*.
      ==========================================================================================
      Read each line of the source file and write it back out, inserting the string where
      required.
      ------------------------------------------------------------------------------------------ */
      boolean  inserted  = false;
      int      lineCount = 1;
      while (pIter.hasNext())
         {
         if (lineCount++ == lineNumber)
            {
            pOutput.println(str);
            pOutput.println(pIter.next());
            inserted = true;
            break;
            }
         pOutput.println(pIter.next());
         }
      while (pIter.hasNext())
         {
         pOutput.println(pIter.next());
         }
      if (!inserted)
         {
         pOutput.println(str);
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method searches this TextFile for a start delimiter. Each time one is located, it finds its
   corresponding end delimiter and then once found, deletes the start delimiter, end delimiter, and
   everything in between.

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

   @param
      pStartDelimiter is the start delimiter that delimits the start of sections of the file to delete.
   @param
      pEndDelimiter is the end delimiter that delimits the end of sections of the file to delete.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> void stripDelimitedSubstrings(Type1 pStartDelimiter, Type2 pEndDelimiter) throws Exception
      {
      stripDelimitedSubstrings(pStartDelimiter, pEndDelimiter, true, true);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method searches this TextFile for a start delimiter. Each time one is located, it finds its
   corresponding end delimiter and then once found, deletes the start delimiter, end delimiter, and
   everything in between.

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

   @param
      pStartDelimiter is the start delimiter that delimits the start of sections of the file to delete.
   @param
      pEndDelimiter is the end delimiter that delimits the end of sections of the file to delete.
   @param
      pDeleteTerminalNewline tells this method whether or not to strip out newlines that happen to be
      the endDelimiter. It is a concession to source code file comment stripping. A "to end of line"
      comment type such as the double forward slash in c-type languages or the single quote in
      basic-type languages uses the newline as a comment terminator (endDelimiter). Stripping these out
      of the file ruins the formatting of these kinds of files. So pDeleteTerminalNewline is provided
      to short-circuit the stripping of them if desired. Normally, as the endDelimiter, they would be
      stripped, but if this parameter is set to 'false', they are not stripped out.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> void stripDelimitedSubstrings(Type1 pStartDelimiter, Type2 pEndDelimiter, boolean pDeleteTerminalNewline) throws Exception
      {
      stripDelimitedSubstrings(pStartDelimiter, pEndDelimiter, pDeleteTerminalNewline, true);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method searches this TextFile for a start delimiter. Each time one is located, it finds its
   corresponding end delimiter and then once found, deletes the start delimiter, end delimiter, and
   everything in between.

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

   @param
      pStartDelimiter is the start delimiter that delimits the start of sections of the file to delete.
   @param
      pEndDelimiter is the end delimiter that delimits the end of sections of the file to delete.
   @param
      pDeleteTerminalNewline tells this method whether or not to strip out newlines that happen to be
      the endDelimiter. It is a concession to source code file comment stripping. A "to end of line"
      comment type such as the double forward slash in c-type languages or the single quote in
      basic-type languages uses the newline as a comment terminator (endDelimiter). Stripping these out
      of the file ruins the formatting of these kinds of files. So pDeleteTerminalNewline is provided
      to short-circuit the stripping of them if desired. Normally, as the endDelimiter, they would be
      stripped, but if this parameter is set to 'false', they are not stripped out.
   @param
      pRecurse tells this method whether or not to recognize nested delimited substrings. If this is
      set to true, then nested substrings are recursively processes and therefor require their own
      endDelimiter. This parameter is also usually needed for source code file comment processing. A
      "to end of line" comment type such as the double forward slash in c-type languages or the single
      quote in basic-type languages uses the newline as a comment terminator (endDelimiter). If a
      double forward slash or single quote is embedded in the string befor the terminating newline of
      the the first startDelimiter, then there is not a matching newline for both startDelimiters. So
      recursively processing the second startDelimiter would lead to bizzar results as second
      startDelimiter would "eat" the first startDelimiter's corresponding endDelimiter. This in turn
      would cause this method to continue eating characters (inappropriately) on behalf of the first
      startDelimiter until another newline was encountered. So pRecurse is provided to short-circuit
      the processing of nested "to-end-of-line" comments if desired.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> void stripDelimitedSubstrings(Type1 pStartDelimiter, Type2 pEndDelimiter, boolean pDeleteTerminalNewline, boolean pRecurse) throws Exception
      {
      this.modify("stripDelimitedSubstrings", pStartDelimiter, pEndDelimiter, pDeleteTerminalNewline, pRecurse);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method repeatedly searches the file for a start delimiter. Each time one is located, it finds
   its corresponding end delimiter and then once found, deletes the start delimiter, end delimiter, and
   everything in between.

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

   @param
      pFileIn is the name of the file that will be copied to the output file with lines sorted.
      pFileOut must not be the same as pFileIn. PFileOut and pFileIn must not be null.
   @param
      pFileOut is the name that will be given to the file that is produced. PFileOut must not be the
      same as pFileIn. PFileOut and pFileIn must not be null.
   @param
      pStartDelimiter is the start delimiter that delimits the start of sections of the file to delete.
   @param
      pEndDelimiter is the end delimiter that delimits the end of sections of the file to delete.
   @param
      pDeleteTerminalNewline tells this method whether or not to strip out newlines that happen to be
      the endDelimiter. It is a concession to source code file comment stripping. A "to end of line"
      comment type such as the double forward slash in c-type languages or the single quote in
      basic-type languages uses the newline as a comment terminator (endDelimiter). Stripping these out
      of the file ruins the formatting of these kinds of files. So pDeleteTerminalNewline is provided
      to short-circuit the stripping of them if desired. Normally, as the endDelimiter, they would be
      stripped, but if this parameter is set to 'false', they are not stripped out.
   @param
      pRecurse tells this method whether or not to recognize nested delimited substrings. If this is
      set to true, then nested substrings are recursively processes and therefor require their own
      endDelimiter. This parameter is also usually needed for source code file comment processing. A
      "to end of line" comment type such as the double forward slash in c-type languages or the single
      quote in basic-type languages uses the newline as a comment terminator (endDelimiter). If a
      double forward slash or single quote is embedded in the string befor the terminating newline of
      the the first startDelimiter, then there is not a matching newline for both startDelimiters. So
      recursively processing the second startDelimiter would lead to bizzar results as second
      startDelimiter would "eat" the first startDelimiter's corresponding endDelimiter. This in turn
      would cause this method to continue eating characters (inappropriately) on behalf of the first
      startDelimiter until another newline was encountered. So pRecurse is provided to short-circuit
      the processing of nested "to-end-of-line" comments if desired.
   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: int tabSize
               <BLOCKQUOTE>
               </BLOCKQUOTE>
            pArguments[1]: XString pStartDelimiter
               <BLOCKQUOTE>
                  Is the start delimiter that delimits the start of sections of the file to delete.
               </BLOCKQUOTE>
            pArguments[2]: XString endDelimiter
               <BLOCKQUOTE>
                  Is the end delimiter that delimits the end of sections of the file to delete.
               </BLOCKQUOTE>
            pArguments[3]: boolean deleteTerminalNewline
               <BLOCKQUOTE>
                  Tells this method whether or not to strip out newlines that happen to be the
                  endDelimiter. It is a concession to source code file comment stripping. A "to end of
                  line" comment type such as the double forward slash in c-type languages or the single
                  quote in basic-type languages uses the newline as a comment terminator
                  (endDelimiter). Stripping these out of the file ruins the formatting of these kinds
                  of files. So pDeleteTerminalNewline is provided to short-circuit the stripping of
                  them if desired. Normally, as the endDelimiter, they would be stripped, but if this
                  parameter is set to 'false', they are not stripped out.
               </BLOCKQUOTE>
            pArguments[4]: boolean recurse
               <BLOCKQUOTE>
                  Tells this method whether or not to recognize nested delimited substrings. If this is
                  set to true, then nested substrings are recursively processes and therefor require
                  their own endDelimiter. This parameter is also usually needed for source code file
                  comment processing. A "to end of line" comment type such as the double forward slash
                  in c-type languages or the single quote in basic-type languages uses the newline as a
                  comment terminator (endDelimiter). If a double forward slash or single quote is
                  embedded in the string befor the terminating newline of the the first startDelimiter,
                  then there is not a matching newline for both startDelimiters. So recursively
                  processing the second startDelimiter would lead to bizzar results as second
                  startDelimiter would "eat" the first startDelimiter's corresponding endDelimiter.
                  This in turn would cause this method to continue eating characters (inappropriately)
                  on behalf of the first startDelimiter until another newline was encountered. So
                  pRecurse is provided to short-circuit the processing of nested "to-end-of-line"
                  comments if desired.
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void stripDelimitedSubstrings(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      XString  startDelimiter        = new XString(pArguments[0]);
      XString  endDelimiter          = new XString(pArguments[1]);
      boolean  deleteTerminalNewline = (boolean)pArguments[2];
      boolean  recurse               = (boolean)pArguments[3];
      /*.
      ==========================================================================================
      Read each line of the source file into a StringBuffer. The EOL that is lost when reading
      from a TextReader is reattached so that searches will work with \n.
      ------------------------------------------------------------------------------------------ */
      StringBuffer  theWholeFile = new StringBuffer(2 * UMath.ONE_MEG);
      while (pIter.hasNext())
         {
         XString  s = pIter.next();
         s = s.trimRight();
         s = s.concat("\n");
         theWholeFile.append(s.toString());
         }
      /*.
      ==========================================================================================
      DeleteTerminalNewline is a concession to source code file comment stripping. A "to end of
      line" comment type such as the double forward slash in c-type languages or the single
      quote in basic-type languages uses the \n as a comment terminator (endDelimiter).
      Stripping these out of the file ruins the formatting of these kinds of files. So
      deleteTerminalNewline is provided to short-circuit the stripping of them if desired.
      Normally, as the endDelimiter, they would be stripped, but if this parameter is set to
      'false', they are not stripped out.
      ------------------------------------------------------------------------------------------ */
      int  minusNewline = ((! deleteTerminalNewline) && (endDelimiter.endsWith("\n")))? 1 : 0;
      /*.
      ==========================================================================================
      Repeatedly search the StringBuffer for the start delimiter. Each time one is located, find
      its corresponding end delimiter. Once found, delete the start delimiter, end delimiter,
      and everything in between.
      ------------------------------------------------------------------------------------------ */
      int  startDelimiterIdx;
      while ((startDelimiterIdx = theWholeFile.indexOf(startDelimiter.toString())) != (-1))
         {
         int  idxAfterComment = (int)UString.indexAfterDelimitedSpan(theWholeFile, startDelimiter, endDelimiter, startDelimiterIdx + startDelimiter.length(), recurse);
         theWholeFile = theWholeFile.delete(startDelimiterIdx, idxAfterComment - minusNewline);
         }
      /*.
      ==========================================================================================
      Write out the StringBuffer object to the pOutput file
      ------------------------------------------------------------------------------------------ */
      pOutput.print(theWholeFile.toString());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method indents a delimited block starting at the current line containing a start delimiter and
   continuing until it has indented the line containing the matching end delimiter.

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

   @param
      pStartDelimiter is the XString that demarcates the start of the block of text to indent.
   @param
      pEndDelimiter is the XString that demarcates the end of the block of text to indent.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1, Type2> void indentDelimitedBlockFromPriorText(Type1 pStartDelimiter, Type2 pEndDelimiter) throws Exception
      {
      this.modify("indentDelimitedBlockFromPriorText", pStartDelimiter, pEndDelimiter);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method indents a delimited block starting at the current line containing a start delimiter and
   continuing until it has indented the line containing the matching end delimiter.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments. It is where pStartDelimiter and pEndDelimiter
      are placed.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void indentDelimitedBlockFromPriorText(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Convert the generic parameter types to XString
      ------------------------------------------------------------------------------------------ */
      XString  startDelimiter = new XString(pArguments[0]);
      XString  endDelimiter   = new XString(pArguments[1]);
      /*.
      ==========================================================================================
      For keeping track of the indent of the last non-blank line.
      ------------------------------------------------------------------------------------------ */
      XString  lastIndent = new XString("");
      /*.
      ==========================================================================================
      For each line of the file, if it is a start delimiter, call out to indent it along with
      its subordinate lines and matching end delimiter. Otherwise, just send it to output.
      ------------------------------------------------------------------------------------------ */
      int  currentLineNumber = 0;
      while (pIter.hasNext())
         {
         currentLineNumber++;
         /*.
         ==========================================================================================
         Get the next line from the file, its whitespace prefix and a trimmed version of it.
         ------------------------------------------------------------------------------------------ */
         XString  currentLine    = pIter.next();
         XString  currentIndent  = currentLine.whitespacePrefix();
         XString  currentTrimmed = currentLine.trim();
         /*.
         ==========================================================================================
         If this line is a start delimiter, indent it, everything subordinate to it, and its
         matching end delimiter.
         ------------------------------------------------------------------------------------------ */
         if (currentTrimmed.equals(startDelimiter))
            {
            currentLineNumber = this.indentDelimitedBlockFromPriorText(pIter, pOutput, startDelimiter, endDelimiter, currentLineNumber, currentLine, lastIndent);
            }
         /*.
         ==========================================================================================
         Otherwise, it's just a normal line so send it to the output unchanged and keep track of
         the indent of the last nonblank line.
         ------------------------------------------------------------------------------------------ */
         else
            {
            pOutput.println(currentLine);
            if (! currentTrimmed.isEmpty())
               {
               lastIndent = currentIndent;
               }
            }
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method is the recursive part of the method that indents a delimited block starting at the
   current line containing a start delimiter and continuing until it has indented the line containing
   the matching end delimiter.

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

   @return
      This method returns the line number of the last line it sent to pOutput.

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pStartDelimiter is the XString that demarcates the start of the block of text to indent.
   @param
      pEndDelimiter is the XString that demarcates the end of the block of text to indent.
   @param
      pCurrentLineNumber is the line number from the input file of the pCurrentLine
   @param
      pCurrentLine is the current line from the input file and it is expected to consist of a start
      delimiter.
   @param
      pIndentFrom is the indentation of the last non-blank line in the file prior to pCurrentLine.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private int indentDelimitedBlockFromPriorText(XStringsIterator pIter, TextWriter pOutput, XString pStartDelimiter, XString pEndDelimiter, int pCurrentLineNumber, XString pCurrentLine, XString pIndentFrom) throws Exception
      {
      /*.
      ==========================================================================================
      This is how far in the delimited block should be indented from the prior non-blank line of
      text.
      ------------------------------------------------------------------------------------------ */
      XString  oneIndent = new XString("   ");
      /*.
      ==========================================================================================
      This is the extra indentation characters that will be prepended to every line in the
      delimited block to insure that each is indented the right amount from the prior non-blank
      line of text
      ------------------------------------------------------------------------------------------ */
      XString  extraIndent = new XString("");
      /*.
      ==========================================================================================
      When we arrive in this method, a start delimiter has been found on the current line and
      that current line was passed into this method without being output. Now set all the local
      line variables to represent this incomming start delimiter line.
      ------------------------------------------------------------------------------------------ */
      XString  currentLine        = pCurrentLine;
      XString  currentLineIndent  = currentLine.whitespacePrefix();
      XString  currentLineTrimmed = currentLine.trim();
      /*.
      ==========================================================================================
      If this start delimiter line is not indented more than pIndentFrom, then figure out how
      much additional indent is needed to be one indent level further in than pIndentFrom and
      change extraIndent to be of that length of space characters.
      ------------------------------------------------------------------------------------------ */
      if (currentLineIndent.length() < pIndentFrom.length() + oneIndent.length())
         {
         int amountOfExtraIndent = pIndentFrom.length() + oneIndent.length() - currentLineIndent.length();
         extraIndent = XString.fill(amountOfExtraIndent, ' ');
         }
      /*.
      ==========================================================================================
      Now increment the current line number and output the currentLine with any required extra
      indent characters prepended to it.
      ------------------------------------------------------------------------------------------ */
      int  currentLineNumber = pCurrentLineNumber;
      currentLine        = extraIndent.concat(currentLine);
      currentLineIndent  = currentLine.whitespacePrefix();
      currentLineTrimmed = currentLine.trim();
      pOutput.println(currentLine);
      /*.
      ==========================================================================================
      Now we just start reading each suceeding line, prepending the extra indent characters to
      each one and sending it to output. When we run into the matching end delimiter, we stop
      reading, and go through the same exercise for indenting it that we went through with its
      corresponding start delimiter so we can output it with the proper indentation. (We don't
      just prepend the extra indentation just in case the end delimiter is not lined up properly
      in the input file with its start delimiter.) If we run into another start delimiter before
      hitting the end delimiter, that means there is a nested delimited block. That scenario is
      handled via recursion.
      ------------------------------------------------------------------------------------------ */
      boolean  endFound = false;
      XString  lastIndent = currentLineIndent;
      while (pIter.hasNext())
         {
         currentLineNumber++;
         /*.
         ==========================================================================================
         Get the next line from the file, its whatespace prefix and a trimmed version of it.
         ------------------------------------------------------------------------------------------ */
         currentLine        = pIter.next();
         currentLineIndent  = currentLine.whitespacePrefix();
         currentLineTrimmed = currentLine.trim();
         /*.
         ==========================================================================================
         THIS BLOCK IS THE ONLY NORMAL WAY OUT OF THIS METHOD.
            Since we came into this method because a start delimiter was found, the only legitimate
            way out of the methhod is here - when a matching end delimiter is found. Output it with
            the proper indentation to line up with the start delimiter and return the current line
            count.
         ------------------------------------------------------------------------------------------ */
         endFound = currentLineTrimmed.equals(pEndDelimiter);
         if (endFound)
            {
            /*.
            ==========================================================================================
            If this end delimiter line is not indented more than pIndentFrom, then figure out how much
            additional indent is needed to be one indent level further in than pIndentFrom and change
            extraIndent to be of that length of space characters.
            ------------------------------------------------------------------------------------------ */
            if (currentLineIndent.length() < pIndentFrom.length() + oneIndent.length())
               {
               int amountOfExtraIndent = pIndentFrom.length() + oneIndent.length() - currentLineIndent.length();
               extraIndent = XString.fill(amountOfExtraIndent, ' ');
               }
            /*.
            ==========================================================================================
            Now output the currentLine with any required extra indent characters prepended to it.
            ------------------------------------------------------------------------------------------ */
            currentLine = extraIndent.concat(currentLine);
            pOutput.println(currentLine);
            /*.
            ==========================================================================================
            And return the current line number
            ------------------------------------------------------------------------------------------ */
            return currentLineNumber;
            }
         /*.
         ==========================================================================================
         Otherwise, if we run into another start delimiter before we find the matching end
         delimiter for the start delimiter that was passed into this method, it means we have
         encountered a nested start delimiter. Pass it into a recursive call to this method for
         processing.
         ------------------------------------------------------------------------------------------ */
         else if (currentLineTrimmed.equals(pStartDelimiter))
            {
            currentLineNumber = this.indentDelimitedBlockFromPriorText(pIter, pOutput, pStartDelimiter, pEndDelimiter, currentLineNumber, currentLine, lastIndent);
            }
         /*.
         ==========================================================================================
         Otherwise, prepend the currenty extra indentation to the current line and send it to
         output.
         ------------------------------------------------------------------------------------------ */
         else
            {
            currentLine        = extraIndent.concat(currentLine);
            currentLineIndent  = currentLine.whitespacePrefix();
            currentLineTrimmed = currentLine.trim();
            pOutput.println(currentLine);
            /*.
            ==========================================================================================
            Keep track of the indent of the last nonblank line.
            ------------------------------------------------------------------------------------------ */
            if (! currentLineTrimmed.isEmpty())
               {
               lastIndent = currentLineIndent;
               }
            }
         }
      /*.
      ==========================================================================================
      If we get here, we got to the end of file and the start/end delimiters didn't all match up
      so throw an exception
      ------------------------------------------------------------------------------------------ */
      throw new Exception("TextFile.indentDelimitedBlockFromPriorText(): " + pStartDelimiter + " delimiter on line " + pCurrentLineNumber + " is unmatched");
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method repairs lines where a start or end delimiter got placed (most likely by word wrapping)
   at the beginning or ending of the lines.

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

   @param
      pStartDelimiter is the start delimiter.
   @param
      pEndDelimiter is the end delimiter.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1, Type2> void unwrapBlockDelimiters(Type1 pStartDelimiter, Type2 pEndDelimiter) throws Exception
      {
      /*.
      ==========================================================================================
      The return value from modify indicates whether or not this pass through the file found any
      block delimiters that needed to be unwrapped. This method can only find one instance of a
      wrapped delimiter per line on each pass through the file. If there are multiple instances
      on single lines, they can only be resolved by calling this method repeatedly until they
      are all resolved. If the method returns true, it means that a delimiter that had to be
      unwrapped was found and that another pass needs to be made through the file to see if
      there are any more on the lines on which one was found and resolved. When this method
      returns false, it means all instances of wrapped delimeters on all lines have been
      resolved.
      ------------------------------------------------------------------------------------------ */
      while (this.modify("unwrapBlockDelimiters", pStartDelimiter, pEndDelimiter));
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method repairs lines where a start or end delimiter got placed (most likely by word wrapping)
   at the beginning or ending of the lines.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments. It is where pStartDelimiter and pEndDelimiter
      are placed.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean unwrapBlockDelimiters(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Convert the generic parameter types to XString
      ------------------------------------------------------------------------------------------ */
      XString  startDelimiter = new XString(pArguments[0]);
      XString  endDelimiter   = new XString(pArguments[1]);
      /*.
      ==========================================================================================
      This variable will be returned. It indicates whether or not this pass through the file
      found any block delimiters that needed to be unwrapped. This method can only find one
      instance of a wrapped delimiter per line on each pass through the file. If there are
      multiple instances on single lines, they can only be resolved by calling this method
      repeatedly until they are all resolved. If this method returns true, it means that a
      delimiter that had to be unwrapped was found and that another pass needs to be made
      through the file to see if there are any more on the lines on which one was found and
      resolved. When this method returns false, it means all instances of wrapped delimeters on
      all lines have been resolved.
      ------------------------------------------------------------------------------------------ */
      boolean wrappedDelimiterFound = false;
      /*.
      ==========================================================================================
      For each line of the file, ...
      ------------------------------------------------------------------------------------------ */
      while (pIter.hasNext())
         {
         /*.
         ==========================================================================================
         Get the next line from the file, its whitespace prefix and a trimmed version of it.
         ------------------------------------------------------------------------------------------ */
         XString  currentLine        = pIter.next().trimRight();
         XString  currentLineIndent  = currentLine.whitespacePrefix();
         XString  currentLineTrimmed = currentLine.trim();
         /*.
         ==========================================================================================
         If there's a start delimiter that got wrapped onto the end of a line of other text, output
         the other text as its own line and then output a start delimiter on the next line by
         itself
         ------------------------------------------------------------------------------------------ */
         if (currentLineTrimmed.endsWithIgnoreCase(startDelimiter) && ( ! currentLineTrimmed.equalsIgnoreCase(startDelimiter)))
            {
            currentLine = currentLine.trimRightIgnoreCase(startDelimiter);
            pOutput.println(currentLine);
            pOutput.println(currentLineIndent.concat(startDelimiter));
            wrappedDelimiterFound = true;
            }
         /*.
         ==========================================================================================
         If there's an end delimiter that got wrapped onto the start of a line of other text,
         output an end delimiter on the next line by itself and then output the other text as its
         own line
         ------------------------------------------------------------------------------------------ */
         else if (currentLineTrimmed.startsWithIgnoreCase(endDelimiter) && ( ! currentLineTrimmed.equalsIgnoreCase(endDelimiter)))
            {
            currentLine = currentLine.trimLeftIgnoreCase(currentLineIndent.concat(endDelimiter));
            pOutput.println(currentLineIndent.concat(endDelimiter));
            pOutput.println(currentLineIndent.concat(currentLine));
            wrappedDelimiterFound = true;
            }
         /*.
         ==========================================================================================
         If there's an end delimiter that got wrapped onto the end of a line of other text, output
         the other text as its own line and then output an end delimiter on the next line by itself
         ------------------------------------------------------------------------------------------ */
         else if (currentLineTrimmed.endsWithIgnoreCase(endDelimiter) && ( ! currentLineTrimmed.equalsIgnoreCase(endDelimiter)))
            {
            currentLine = currentLine.trimRightIgnoreCase(endDelimiter);
            pOutput.println(currentLine);
            pOutput.println(currentLineIndent.concat(endDelimiter));
            wrappedDelimiterFound = true;
            }
         /*.
         ==========================================================================================
         If there's a start delimiter that got wrapped onto the start of a line of other text,
         output a start delimiter on the next line by itself and then output the other text as its
         own line
         ------------------------------------------------------------------------------------------ */
         else if (currentLineTrimmed.startsWithIgnoreCase(startDelimiter) && ( ! currentLineTrimmed.equalsIgnoreCase(startDelimiter)))
            {
            currentLine = currentLine.trimLeftIgnoreCase(currentLineIndent.concat(startDelimiter));
            pOutput.println(currentLineIndent.concat(startDelimiter));
            pOutput.println(currentLineIndent.concat(currentLine));
            wrappedDelimiterFound = true;
            }
         /*.
         ==========================================================================================
         If this is just a normal line without any stasrt or end delimiter at its beginning or end,
         then just output it as is
         ------------------------------------------------------------------------------------------ */
         else
            {
            pOutput.println(currentLine);
            }
         }
      return wrappedDelimiterFound;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method indents all of the delimited block's content where the delimited block is delimited by
   the specified start and end delimiters.

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

   @param
      pStartDelimiter is the XString that demarcates the start of the block of text to indent.
   @param
      pEndDelimiter is the XString that demarcates the end of the block of text to indent.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1, Type2> void indentDelimitedBlockContentFromDelimiters(Type1 pStartDelimiter, Type2 pEndDelimiter) throws Exception
      {
      this.modify("indentDelimitedBlockContentFromDelimiters", pStartDelimiter, pEndDelimiter);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method indents all of the delimited block's content where the delimited block is delimited by
   the specified start and end delimiters.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments. It is where pStartDelimiter and pEndDelimiter
      are placed.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void indentDelimitedBlockContentFromDelimiters(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Convert the generic parameter types to XString
      ------------------------------------------------------------------------------------------ */
      XString  startDelimiter = new XString(pArguments[0]);
      XString  endDelimiter   = new XString(pArguments[1]);
      /*.
      ==========================================================================================
      For each line of the file, if it is a start delimiter, call out to output it, indent its
      subordinate lines, and output its matching end delimiter. Otherwise, just send it to
      output.
      ------------------------------------------------------------------------------------------ */
      while (pIter.hasNext())
         {
         /*.
         ==========================================================================================
         Get the next line from the file and a trimmed version of it.
         ------------------------------------------------------------------------------------------ */
         XString  currentLine    = pIter.next();
         XString  currentTrimmed = currentLine.trim();
         /*.
         ==========================================================================================
         If this line is a start delimiter, indent it, everything subordinate to it, and its
         matching end delimiter.
         ------------------------------------------------------------------------------------------ */
         if (currentTrimmed.equals(startDelimiter))
            {
            this.indentDelimitedBlockContentFromDelimiters(pIter, pOutput, startDelimiter, endDelimiter, currentLine);
            }
         /*.
         ==========================================================================================
         Otherwise, it's just a normal line so send it to the output unchanged
         ------------------------------------------------------------------------------------------ */
         else
            {
            pOutput.println(currentLine);
            }
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method is the recursive portion of the method that indents all of the delimited block's content
   where the delimited block is delimited by the specified start and end delimiters.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pStartDelimiter is the XString that demarcates the start of the block of text to indent.
   @param
      pEndDelimiter is the XString that demarcates the end of the block of text to indent.
   @param
      pCurrentLine is the current line from the input file and it is expected to consist of a start
      delimiter.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void indentDelimitedBlockContentFromDelimiters(XStringsIterator pIter, TextWriter pOutput, XString pStartDelimiter, XString pEndDelimiter, XString pCurrentLine) throws Exception
      {
      /*.
      ==========================================================================================
      This is how far in the delimited block content should be indented from the start
      delimiter.
      ------------------------------------------------------------------------------------------ */
      XString  oneIndent   = new XString("   ");
      /*.
      ==========================================================================================
      This is the extra indentation characters that will be prepended to every line in the
      delimited block to insure that each is indented the right amount from the prior non-blank
      line of text
      ------------------------------------------------------------------------------------------ */
      XString  extraIndent = new XString("");
      /*.
      ==========================================================================================
      When we arrive in this method, a start delimiter has been found on the current line and
      that current line was passed into this method without being output. Now set all the local
      line variables to represent this incomming start delimiter line.
      ------------------------------------------------------------------------------------------ */
      XString  currentLine        = pCurrentLine;
      XString  currentLineIndent  = currentLine.whitespacePrefix();
      XString  currentLineTrimmed = currentLine.trim();
      /*.
      ==========================================================================================
      And since it's the start delimiter line, just write it out.
      ------------------------------------------------------------------------------------------ */
      pOutput.println(currentLine);
      /*.
      ==========================================================================================
      Remember how far in the start delimiter was indented
      ------------------------------------------------------------------------------------------ */
      XString  startDelimiterIndent = currentLineIndent;
      /*.
      ==========================================================================================
      Now we just start reading each suceeding line. The first one determines if the content
      needs to be indented more than it already is.
      ------------------------------------------------------------------------------------------ */
      boolean  firstContentLine = true;
      boolean  endFound         = false;
      while (pIter.hasNext())
         {
         /*.
         ==========================================================================================
         Get the next line from the file, its whatespace prefix and a trimmed version of it.
         ------------------------------------------------------------------------------------------ */
         currentLine        = pIter.next();
         currentLineIndent  = currentLine.whitespacePrefix();
         currentLineTrimmed = currentLine.trim();
         /*.
         ==========================================================================================
         If this is the first non-blank line of content, then this is where we figure out if we
         need to indent the content and if so, by how much.
         ------------------------------------------------------------------------------------------ */
         if (firstContentLine && (! currentLineTrimmed.isEmpty()))
            {
            firstContentLine = false;
            /*.
            ==========================================================================================
            If it is not indented more than the start delimiter is indented, then figure out how much
            additional indent is needed to be one indent level further in than the start delimiter and
            change extraIndent to be of that length of space characters.
            ------------------------------------------------------------------------------------------ */
            if (currentLineIndent.length() < startDelimiterIndent.length() + oneIndent.length())
               {
               int amountOfExtraIndent = startDelimiterIndent.length() + oneIndent.length() - currentLineIndent.length();
               extraIndent = XString.fill(amountOfExtraIndent, ' ');
               }
            }
         /*.
         ==========================================================================================
         THIS BLOCK IS THE ONLY NORMAL WAY OUT OF THIS METHOD.
            Since we came into this method because a start delimiter was found, the only legitimate
            way out of the methhod is here - when a matching end delimiter is found. Output it with
            the proper indentation to line up with the start delimiter
         ------------------------------------------------------------------------------------------ */
         endFound = currentLineTrimmed.equals(pEndDelimiter);
         if (endFound)
            {
            /*.
            ==========================================================================================
            Output the currentLine with the same indent as its corresonding start delimiter
            ------------------------------------------------------------------------------------------ */
            currentLine = startDelimiterIndent.concat(pEndDelimiter);
            pOutput.println(currentLine);
            /*.
            ==========================================================================================
            And return
            ------------------------------------------------------------------------------------------ */
            return;
            }
         /*.
         ==========================================================================================
         Otherwise, if we run into another start delimiter before we find the matching end
         delimiter for the start delimiter that was passed into this method, it means we have
         encountered a nested start delimiter. Pass it into a recursive call to this method for
         processing.
         ------------------------------------------------------------------------------------------ */
         else if (currentLineTrimmed.equals(pStartDelimiter))
            {
            this.indentDelimitedBlockContentFromDelimiters(pIter, pOutput, pStartDelimiter, pEndDelimiter, extraIndent.concat(currentLine));
            }
         /*.
         ==========================================================================================
         Otherwise, prepend the current extra indentation to the current line and send it to
         output.
         ------------------------------------------------------------------------------------------ */
         else
            {
            currentLine = extraIndent.concat(currentLine);
            pOutput.println(currentLine);
            }
         }
      /*.
      ==========================================================================================
      If we get here, we got to the end of file and the start/end delimiters didn't all match up
      so throw an exception
      ------------------------------------------------------------------------------------------ */
      throw new Exception("TextFile.indentDelimitedBlockContentFromDelimiters(): " + pStartDelimiter + " delimiter is unmatched");
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method modifies this TextFile in place to have no more and no less than the specified number of
   consecutive empty lines at the end of this TextFile.

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

   @param
      pNumberOfEmptyLines is the exact number of empty lines that will be found in the sequence of
      consecutive empty lines at the end of this TextFile after it has been modified in place.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setEmptyLinesAtEof(long pNumberOfEmptyLines) throws Exception
      {
      this.modify("setEmptyLinesAtEof", pNumberOfEmptyLines);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method modifies a file in place to have no more and no less than the specified number of
   consecutive empty lines at the end of the file.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain
      pNumberOfEmptyLines as the first element of the list
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setEmptyLinesAtEof(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      long  numberOfEmptyLines = (long)pArguments[0];
      /*.
      ==========================================================================================
      Read each line of the source file, count any sequences of consecutive empty lines and for
      the sequence at the end of the file, replace the sequence with the specified number of
      empty lines.
      ------------------------------------------------------------------------------------------ */
      XString  trimmedLine = null;
      XString  line        = null;
      long     count       = 0;
      while (pIter.hasNext())
         {
         line        = pIter.next();
         trimmedLine = line.trim();
         if (trimmedLine.isEmpty())
            {
            count++;
            }
         else
            {
            while (count > 0)
               {
               count--;
               pOutput.println();
               }
            pOutput.println(line);
            }
         }
      /*.
      ==========================================================================================
      When the EOF is encountered, we end up here without having output the current sequence of
      empty lines. Output the specified number of empty lines now.
      ------------------------------------------------------------------------------------------ */
      while (numberOfEmptyLines-- > 0) pOutput.println();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method modifies this TextFile in place to have no more than the specified number of consecutive
   empty lines in any sequence of empty lines in this TextFile.

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

   @param
      pNumberOfEmptyLines is the maximum number of empty lines that will be found in any sequence of
      consecutive empty lines in this TextFile after it has been modified in place.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setMaxConsecutiveEmptyLines(long pNumberOfEmptyLines) throws Exception
      {
      this.modify("setMaxConsecutiveEmptyLines", pNumberOfEmptyLines);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method modifies a file in place to have no more than the specified number of consecutive empty
   lines in any sequence of empty lines in the file.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain
      pNumberOfEmptyLines as the first element of the list
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void setMaxConsecutiveEmptyLines(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      long  numberOfEmptyLines = (long)pArguments[0];
      /*.
      ==========================================================================================
      Read each line of the source file, count any sequences of consecutive empty lines and
      replace each sequence with itself or with the specified number of empty lines, whichever
      is fewer.
      ------------------------------------------------------------------------------------------ */
      XString  trimmedLine = null;
      XString  line        = null;
      long     count       = 0;
      while (pIter.hasNext())
         {
         line        = pIter.next();
         trimmedLine = line.trim();
         if (trimmedLine.isEmpty())
            {
            count = UMath.min(numberOfEmptyLines, count + 1);
            }
         else
            {
            while (count > 0)
               {
               count--;
               pOutput.println();
               }
            pOutput.println(line);
            }
         }
      /*.
      ==========================================================================================
      When the EOF is encountered, we end up here without having output the current sequence of
      empty lines. Output this last sequence now.
      ------------------------------------------------------------------------------------------ */
      while (count-- > 0) pOutput.println();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method modifies this TextFile in place to have its content put into columns. The lines in the
   file are counted and divided by the number of columns. The result is the number of lines it puts
   into each column. Each line is taken in full as one string and placed into one of the columns. The
   columns are vertically aligned and seperated by one space.

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

   @param
      pColumns is the number of columns to put the content of the file into
   *//*
  ---------------------------------------------------------------------------------------------------- */
   public void columnize(int pColumns) throws Exception
      {
      this.columnize(pColumns, " ");
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method modifies this TextFile in place to have its content put into columns. The lines in the
   file are counted and divided by the number of columns. The result is the number of lines it puts
   into each column. Each line is taken in full as one string and placed into one of the columns. The
   columns are vertically aligned and seperated by the spacer string.

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

   @param
      pColumns is the number of columns to put the content of the file into
   @param
      pSpacer is the string to seperate the columns with
   *//*
  ---------------------------------------------------------------------------------------------------- */
   public <Type1> void columnize(int pColumns, Type1 pSpacer) throws Exception
      {
      this.modify("columnize", pColumns, XString.toXString(pSpacer));
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method modifies this TextFile in place to have its content put into columns. The lines in the
   file are counted and divided by the number of columns. The result is the number of lines it puts
   into each column. Each line is taken in full as one string and placed into one of the columns. The
   columns are vertically aligned and seperated by the spacer string.

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

   @param
      pIter is an XStringsIterator for reading this TextFile
   @param
      pOutput is a TextWriter for writing the modified content of this TextFile to the output file.
   @param
      pArguments is a variable sized list of arguments and this method expects it to contain:
         <BLOCKQUOTE>
            pArguments[0]: int colCount
               <BLOCKQUOTE>
                  Is the number of columns to put the content of the file into
               </BLOCKQUOTE>
            pArguments[1]: XString spacer
               <BLOCKQUOTE>
                  Is the string to seperate the columns with
               </BLOCKQUOTE>
         </BLOCKQUOTE>
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void columnize(XStringsIterator pIter, TextWriter pOutput, Object... pArguments) throws Exception
      {
      /*.
      ==========================================================================================
      Get the arguments
      ------------------------------------------------------------------------------------------ */
      int      colCount = (int)pArguments[0];
      XString  spacer   = new XString(pArguments[1]);
      /*.
      ==========================================================================================
      each line of the file will be stored as an element in an ArrayList. read and store each
      line of the file
      ------------------------------------------------------------------------------------------ */
      ArrayList<XString>  lines = new ArrayList<XString>();
      while (pIter.hasNext())
         {
         lines.add(pIter.next());
         }
      /*.
      ==========================================================================================
      figure out how many rows to put in each column
      ------------------------------------------------------------------------------------------ */
      int  rowCount  = lines.size() / colCount;
      int  rowExcess = lines.size() % colCount;
      /*.
      ==========================================================================================
      feed the lines into columns
      ------------------------------------------------------------------------------------------ */
      ArrayList<ArrayList<XString>>  columns  = new ArrayList<ArrayList<XString>>();
      XStringsIterator               lineIter = new XStringsIterator(lines);
      for (int colIdx=0; colIdx<colCount; colIdx++)
         {
         if (!lineIter.hasNext()) break;
         ArrayList<XString>  col = new ArrayList<XString>();
         columns.add(col);
         int  maxRowIdx = rowCount + ((--rowExcess >= 0)? 1 : 0);
         for (int rowIdx=0; rowIdx<maxRowIdx; rowIdx++)
            {
            if (!lineIter.hasNext()) break;
            col.add(lineIter.next());
            }
         }
      lines = null;
      /*.
      ==========================================================================================
      figure out the max width of each column and pad each string in the column to that max
      width
      ------------------------------------------------------------------------------------------ */
      for (int colIdx=0; colIdx<colCount; colIdx++)
         {
         ArrayList<XString>  col = columns.get(colIdx);
         int  max = 0;
         for (XString  str : col)
            {
            max = UMath.max(max, str.length());
            }
         for (int rowIdx=0; rowIdx<col.size(); rowIdx++)
            {
            col.set(rowIdx, col.get(rowIdx).alignLeft(max, ' '));
            }
         }
      /*.
      ==========================================================================================
      write out the columns to the file
      ------------------------------------------------------------------------------------------ */
      for (int  rowIdx=0; rowIdx<columns.get(0).size(); rowIdx++)
         {
         for (int  colIdx=0; colIdx<colCount; colIdx++)
            {
            ArrayList<XString>  col = columns.get(colIdx);
            if (rowIdx < col.size())
               {
               XString  str = col.get(rowIdx);
               if (colIdx > 0)
                  {
                  pOutput.print(spacer);
                  }
               pOutput.print(str);
               }
            }
         pOutput.println();
         }
      }



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

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

   @return
      A reference to this object

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



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   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="TextFile.java.html#089">View source</A>

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

      this.testFixHtmlElementIndentation();

      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This is a test method that formats certain HTML elements if they are found in this TextFile.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void testFixHtmlElementIndentation() throws Exception
      {
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.unwrapBlockDelimiters("<BLOCKQUOTE>", "</BLOCKQUOTE>");
      this.indentDelimitedBlockFromPriorText("<BLOCKQUOTE>", "</BLOCKQUOTE>");
      this.indentDelimitedBlockContentFromDelimiters("<BLOCKQUOTE>", "</BLOCKQUOTE>");
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.unwrapBlockDelimiters("<PRE>", "</PRE>");
      this.indentDelimitedBlockFromPriorText("<PRE>", "</PRE>");
      this.indentDelimitedBlockContentFromDelimiters("<PRE>", "</PRE>");
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.unwrapBlockDelimiters("<PRE id=\"unindent\">", "</PRE>");
      this.indentDelimitedBlockFromPriorText("<PRE id=\"unindent\">", "</PRE>");
      this.indentDelimitedBlockContentFromDelimiters("<PRE id=\"unindent\">", "</PRE>");
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.unwrapBlockDelimiters("<DL>", "</DL>");
      this.indentDelimitedBlockFromPriorText("<DL>", "</DL>");
      this.indentDelimitedBlockContentFromDelimiters("<DL>", "</DL>");
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.unwrapBlockDelimiters("<DT>", "</DT>");
      this.indentDelimitedBlockFromPriorText("<DT>", "</DT>");
      this.indentDelimitedBlockContentFromDelimiters("<DT>", "</DT>");
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.unwrapBlockDelimiters("<DD>", "</DD>");
      this.indentDelimitedBlockContentFromDelimiters("<DD>", "</DD>");
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.unwrapBlockDelimiters("<B>", "</B>");
      this.indentDelimitedBlockFromPriorText("<B>", "</B>");
      this.indentDelimitedBlockContentFromDelimiters("<B>", "</B>");
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      this.unwrapBlockDelimiters("<A href=\"mailto:sourcecode.v01@cosmicabyss.com\">", "</A>");
      this.indentDelimitedBlockFromPriorText("<A href=\"mailto:sourcecode.v01@cosmicabyss.com\">", "</A>");
      this.indentDelimitedBlockContentFromDelimiters("<A href=\"mailto:sourcecode.v01@cosmicabyss.com\">", "</A>");
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      String  thisFileName = this.getName();
      String  sourceLink   = "<A href=\"" + thisFileName + ".html\">";
      this.unwrapBlockDelimiters(sourceLink, "</A>");
      this.indentDelimitedBlockFromPriorText(sourceLink, "</A>");
      this.indentDelimitedBlockContentFromDelimiters(sourceLink, "</A>");
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  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(TextFile.class.getName());
   private static final int      DFLT_LINE_LEN = ConsoleMessage.defaultLineLength();
   /*.
   ==========================================================================================
   This classes's constants
   ------------------------------------------------------------------------------------------ */
   private static int      sTabSize    = 3;
   private static XString  sDelimiters = new XString(" ");
   private static XString  sSeparator  = new XString("  ");
   /*.
   ==========================================================================================
   Class variables
      cOut : console output.
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();



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



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



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   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.TextFile
            </DD>
         </DT>
      </DL>

   <P><B>Implementation: </B><A HREF="TextFile.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
      {
      /*.
      ==========================================================================================
      Greetings !
      ------------------------------------------------------------------------------------------ */
      cOut.banner(CLASS_NAME);
      /*.
      ==========================================================================================
      Create a TextFile to play with
      ------------------------------------------------------------------------------------------ */
      TextFile  tf   = new TextFile(pArgs[0]);
      tf.test();
      }



   }  // class TextFile



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