image
 
image
HtmlFile.java


/*::.
==================================================================================================================================
=================================================¦ Copyright © 2014 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       HtmlFile.java
Originator: Allen Baker (2014.06.05 07:15)
LayoutRev:  5
================================================================================================================================== */



package cosmicabyss.com.lib;

import java.io.*;
import java.util.*;
import java.net.*;



/*::
======================================================================================================================== *//**
Instances of this class represent an HTML file. Salient characteristics of an HtmlFile are:
   <BLOCKQUOTE>
      => it contains text in HTML format and is intended for consumption by a web browser,<BR>
      => it can be optimized (compressed) and still be interpreted correctly by a browser,
         <BLOCKQUOTE>
            -> this class defines a public instance method - optimize() - that will remove all the comments and delete
               All the Unnecessary whitespace from the file.
         </BLOCKQUOTE>
   </BLOCKQUOTE>

This class is derived from the TextFile class. The abstraction it layers on top of TextFile includes the following:
   <BLOCKQUOTE>
      => it contains only HTML-encoded text.
   </BLOCKQUOTE>

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



   /*:                                    :METHOD:000:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the HtmlFile class to represent the file named by the pFile
   parameter.

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

   @param
      pFile is an XFile that the instance created by this constructor will assume is a text file in
      HTML format.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public HtmlFile(XFile pFile) throws Exception
      {
      super(pFile.getCanonicalPath());
      }



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

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

   @param
      pFile is a TextFile that the instance created by this constructor will assume is a text file in
      HTML format.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public HtmlFile(TextFile pFile) throws Exception
      {
      super(pFile.getCanonicalPath());
      }



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

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

   @param
      pFile is an HTMLFile that the instance created by this constructor will assume is a text file in
      HTML format.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public HtmlFile(HtmlFile pFile) throws Exception
      {
      super(pFile.getCanonicalPath());
      }



   /*:                                    :METHOD:003:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the HtmlFile class by converting the given file: URI into an
   abstract pathname. The exact form of a file: URI is system-dependent, hence the transformation
   performed by this constructor is also system-dependent.<P>

   For a given abstract pathname it is guaranteed that
      <BLOCKQUOTE>
         <PRE id="unindent">
            new File( f.toURI()).equals( f.getAbsoluteFile())
         </PRE>
      </BLOCKQUOTE>
   So long as the original abstract pathname, the URI, and the new abstract pathname are All created in
   (possibly different invocations of) the same Java virtual machine. This relationship typically does
   not hold, however, when a file: URI that is created in a virtual machine on one operating system is
   converted into an abstract pathname in a virtual machine on a different operating system.

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

   @param
      pUri is an n absolute, hierarchical URI with a scheme equal to "file", a non-empty path
      component, and undefined authority, query, and fragment components.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public HtmlFile(URI pUri) throws Exception
      {
      super(pUri);
      }



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

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

   @param
      pFile is a File that the instance created by this constructor will assume is a text file in HTML
      format.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public HtmlFile(File pFile) throws Exception
      {
      super(pFile.getCanonicalPath());
      }



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

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

   @param
      pFile is an Object whose String representation is the name of a file. The instance created by
      this constructor will assume that the named file is a text file in HTML format.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> HtmlFile(Type pFile) throws Exception
      {
      super(pFile);
      }



   /*:                                    :METHOD:006:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the HtmlFile class from a parent abstract pathname and a child
   pathname string. If parent is null then the new File instance is created as if by invoking the
   single-argument File constructor on the given child pathname string.<P>

   Otherwise the parent abstract pathname is taken to denote a directory, and the child pathname string
   is taken to denote either a directory or a file. If the child pathname string is absolute then it is
   converted into a relative pathname in a system-dependent way. If parent is the empty abstract
   pathname then the new File instance is created by converting child into an abstract pathname and
   resolving the result against a system-dependent default directory. Otherwise each pathname string is
   converted into an abstract pathname and the child abstract pathname is resolved against the parent.

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

   @param
      pParent is the parent abstract pathname
   @param
      pChild is the child pathname string
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> HtmlFile(File pParent, Type pChild) throws Exception
      {
      super(pParent,pChild);
      }



   /*:                                    :METHOD:007:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the HtmlFile class from a parent pathname string and a child
   pathname string.<P>

   If parent is null then the new File instance is created as if by invoking the single-argument File
   constructor on the given child pathname string.<P>

   Otherwise the parent pathname string is taken to denote a directory, and the child pathname string
   is taken to denote either a directory or a file. If the child pathname string is absolute then it is
   converted into a relative pathname in a system-dependent way. If parent is the empty string then the
   new File instance is created by converting child into an abstract pathname and resolving the result
   against a system-dependent default directory. Otherwise each pathname string is converted into an
   abstract pathname and the child abstract pathname is resolved against the parent.

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

   @param
      pParent is the parent pathname string
   @param
      pChild is the child pathname string
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> HtmlFile(Type1 pParent, Type2 pChild) throws Exception
      {
      super(pParent,pChild);
      }



   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Protected Instance Constructors  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Private Instance Constructors  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Initialization  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Private Class Initialization  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */



   static
      {
      /*.
      ==========================================================================================
      Make sure all the class constants and variables are initialized the first time the JVM
      loads this class code and before anything else in this class is accessed
      ------------------------------------------------------------------------------------------ */
      initializeClassConstantsAndVariables();
      }



   /*
   =====================================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Methods  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Public Class Methods  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */



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

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

   <P><B>Implementation: </B><A HREF="HtmlFile.java.html#008">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);
      /*.
      ==========================================================================================
      Constructor test
      ------------------------------------------------------------------------------------------ */
      /*.
      ==========================================================================================
      Create an object and send its output to the ConsoleStream
      ------------------------------------------------------------------------------------------ */
      HtmlFile  obj = new HtmlFile(pArgs[0]);
      /*.
      ==========================================================================================
      Test code
      ------------------------------------------------------------------------------------------ */
      obj.test();
      }



   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Public Instance Methods  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */



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

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

   @return
      A reference to this object

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



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

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

   @return
      A reference to this object
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public HtmlFile 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.optimize();
      return this;
      }



   /*:                                    :METHOD:011:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This metod removes everything from the HTML file that can be removed (comments, uneccessary
   whitespace, etc.) without rendering the HTML file uniterpretable by a browser.

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

   @return
      A reference to this object
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public HtmlFile optimize() throws Exception
      {
      /*.
      ==========================================================================================
      This method makes no sense on directory files, this must be a normal file
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory()) return this;
      /*.
      ==========================================================================================
      Do the optimizations to the file in place
      ------------------------------------------------------------------------------------------ */
      this.compress();
      /*.
      ==========================================================================================
      Return a reference to this (now optimized) file.
      ------------------------------------------------------------------------------------------ */
      return this;
      }



   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Protected Class Methods  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Protected Instance Methods  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Private Class Methods  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */



   /*:                                    :METHOD:012:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method initializes all the class constants and variables

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private static void initializeClassConstantsAndVariables()
      {
      }



   /*:                                    :METHOD:013:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method packs a String of HTML by removing all the trailing whitespace, comments, newlines,
   multiple consecutive spaces, and other unneeded spaces from it.

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

   @return
      This method returns a String of HTML with all the trailing whitespace, comments, newlines,
      multiple consecutive spaces, and other unneeded spaces removed from it.

   @param
      pStr is the Object whose String representation is packed.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private static <Type1> String pack(Type1 pStr)
      {
      XString str = new XString(pStr);

      str.detab(3);
      str.trim();
      str = HtmlFile.deleteComments(str);

      str = str.iteratingReplaceIgnoreCase(("\n"),                               (" ")   );   // delete newlines
      str = str.iteratingReplaceIgnoreCase(("                                "), (" ")   );   // delete 32 consecutive blanks
      str = str.iteratingReplaceIgnoreCase(("                "),                 (" ")   );   // delete 16 consecutive blanks
      str = str.iteratingReplaceIgnoreCase(("        "),                         (" ")   );   // delete  8 consecutive blanks
      str = str.iteratingReplaceIgnoreCase(("    "),                             (" ")   );   // delete  4 consecutive blanks
      str = str.iteratingReplaceIgnoreCase(("  "),                               (" ")   );   // delete  2 consecutive blanks
      str = str.iteratingReplaceIgnoreCase(("> <"),                              ("><")  );   // delete space between tags
      str = str.iteratingReplaceIgnoreCase((" </"),                              ("</")  );   // delete space before end tags
      str = str.iteratingReplaceIgnoreCase(("\"> "),                             ("\">") );   // delete space after start tags
      str = str.iteratingReplaceIgnoreCase((" <br>"),                            ("<br>"));   // delete space before br tags
      str = str.iteratingReplaceIgnoreCase(("<br> "),                            ("<br>"));   // delete space after br tags

      return str.toString();
      }



   /*:                                    :METHOD:014:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method packs a String of HTML by removing all the trailing whitespace, comments, newlines,
   multiple consecutive spaces, and other unneeded spaces from it.<P>

   This method does not compress sections of the String representation of pStr that are delimited by
   PRE or CODE tags.

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

   @return
      An ArrayList into which this method puts the compressed chunks it generates from the String.

   @param
      pStr is the object for which a String representation is compressed.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private static <Type1> ArrayList<String> compress(Type1 pStr)
      {
      /*.
      ==========================================================================================
      This is where the resulting compressed chunks go to be returned from this method.
      ------------------------------------------------------------------------------------------ */
      ArrayList<String>  results = new ArrayList<String>();
      /*.
      ==========================================================================================
      Turn the parameter into a String
      ------------------------------------------------------------------------------------------ */
      String  str = UString.toString(pStr);
      /*.
      ==========================================================================================
      Use an upper-cased version of the string as a way of finding delimiters in a
      case-insensitive way.<P>
      ------------------------------------------------------------------------------------------ */
      String  strUpr = str.toUpperCase();
      /*.
      ==========================================================================================
      Initialize the delimiter data (for recursive calls of this method, recreating this local
      delimiterList is necessary because the string referenced by strUpr is a different region
      of the original string with each recursive call). Adding the DelimiterData objects to the
      DelimiterList causes the first occurrence of each DataDelimiter's startStr to be located
      in strUpr and recorded within the DelimiterData object.
      ------------------------------------------------------------------------------------------ */
      DelimiterDataList  delimiterList = new DelimiterDataList();
      delimiterList.add(new DelimiterData(strUpr, Const.REGEX_PRE_START_TAG,  Const.REGEX_PRE_END_TAG  ));
      delimiterList.add(new DelimiterData(strUpr, Const.REGEX_CODE_START_TAG, Const.REGEX_CODE_END_TAG ));
      /*.
      ==========================================================================================
      We do not compress sections of the input String that are delimited by any of the pairs of
      delimiters that are in delimiterList. So, find the first one and compress the chunks of
      text surrounding it.
      ------------------------------------------------------------------------------------------ */
      String  startDelimiterPattern  = delimiterList.startDelimiterPattern();
      String  endDelimiterPattern    = delimiterList.endDelimiterPattern();
      int     startDelimiterBeginIdx = delimiterList.startDelimiterBeginIdx();
      int     endDelimiterEndIdx     = 0;
      int     startDelimiterLength   = delimiterList.startDelimiterLength();
      int     endDelimiterLength     = 0;
      /*.
      ==========================================================================================
      If there is a start delimiter ...
      ------------------------------------------------------------------------------------------ */
      if (startDelimiterPattern != null)
         {
         if (endDelimiterPattern.equals(Const.REGEX_PRE_END_TAG)) endDelimiterLength = "<PRE>".length();
         if (endDelimiterPattern.equals(Const.REGEX_CODE_END_TAG)) endDelimiterLength = "<CODE>".length();
         /*.
         ==========================================================================================
         Find the delimited section and recursively compress the chunks of text surrounding it.
         ------------------------------------------------------------------------------------------ */
         endDelimiterEndIdx = (int)UString.indexAfterDelimitedSpanRegex(strUpr, startDelimiterPattern, endDelimiterPattern, startDelimiterBeginIdx + startDelimiterLength, true);
         /*.
         ==========================================================================================
         Now we are finished with the upper-cased string so release it to the gargage collector (it
         could be very big).
         ------------------------------------------------------------------------------------------ */
         strUpr = null;
         /*.
         ==========================================================================================
         Only recursively compress the chunks of text surrounding a properly formed delimited
         section. If there is an end delimiter (meaning we have a well-formed delimited section),
         then ...
         ------------------------------------------------------------------------------------------ */
         if (endDelimiterEndIdx != (-1))
            {
            /*.
            ==========================================================================================
            Break the String into 3 chunks:
               <BLOCKQUOTE>
                  <PRE id="unindent">
                     1) the chunk from the start of the string up to an including the start delimiter
                        (this chunk will always include at least a start delimiter),
                     2) the chunk between (but excluding) the start and end delimiters,
                     3) and the chunk including the end delimiter and the remainder of the string after
                        the end delimiter (this chunk will always include at least an end delimiter).
                  </PRE>
               </BLOCKQUOTE>
            ------------------------------------------------------------------------------------------ */
            String  chunk1 = str.substring(0,                                             startDelimiterBeginIdx + startDelimiterLength);
            String  chunk2 = str.substring(startDelimiterBeginIdx + startDelimiterLength, endDelimiterEndIdx     - endDelimiterLength  );
            String  chunk3 = str.substring(endDelimiterEndIdx     - endDelimiterLength,   str.length());
            /*.
            ==========================================================================================
            Chunk1 does not contain any start delimiters so it can be packed now
            ------------------------------------------------------------------------------------------ */
            chunk1 = HtmlFile.pack(chunk1);
            /*.
            ==========================================================================================
            Chunk1 is now packed and we are not going to pack chunk2, so they can both be put into the
            results ArrayList to be returned.
            ------------------------------------------------------------------------------------------ */
            results.add(chunk1);
            results.add(chunk2);
            /*.
            ==========================================================================================
            Chunk3 may or may not contain another delimited section, so we recursively call this
            method on it and add all the processed chunks it returns to our results ArrayList to be
            returned.
            ------------------------------------------------------------------------------------------ */
            results.addAll(HtmlFile.compress(chunk3));
            }
         }
      /*.
      ==========================================================================================
      Else, there is no start delimiter ...
         There are no delimited sections to skip over when packing, so just pack the whole
         string and put it into the results ArrayList as one big chunk.
      ------------------------------------------------------------------------------------------ */
      else
         {
         str = HtmlFile.pack(str);
         results.add(str);
         }
      /*.
      ==========================================================================================
      Return the completed results ArrayList.
      ------------------------------------------------------------------------------------------ */
      return results;
      }



   /*:                                    :METHOD:015:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method packs a file of HTML by removing all the trailing whitespace, comments, newlines,
   multiple consecutive spaces, and other unneeded spaces from it.

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

   @param
      pFileIn is the name of the file that will be copied to the output file in compressed form.
      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.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private static <Type1,Type2> void compress(Type1  pFileIn, Type2  pFileOut) throws Exception
      {
      /*.
      ==========================================================================================
      Check the parameters.
      ------------------------------------------------------------------------------------------ */
      if (pFileIn == null)
         {
         throw new Exception("pFileIn is null");
         }
      if (pFileOut == null)
         {
         throw new Exception("pFileOut is null");
         }
      if (pFileIn.equals(pFileOut))
         {
         throw new Exception("pFileIn and pFileOut are the same file");
         }
      /*.
      ==========================================================================================
      Convert the filename parameters to XStrings
      ------------------------------------------------------------------------------------------ */
      XString  fileIn  = new XString(pFileIn);
      XString  fileOut = new XString(pFileOut);
      /*.
      ==========================================================================================
      Open the in and out file from their XString filenames
      ------------------------------------------------------------------------------------------ */
      XStringsIterator  iter   = new XStringsIterator(fileIn);
      PrintWriter       output = (new TextFile(fileOut)).printWriter();
      /*.
      ==========================================================================================
      Read each line of the source file into one big buffer
      ------------------------------------------------------------------------------------------ */
      StringBuilder  theWholeFile = new StringBuilder(2 * UMath.ONE_MEG);
      while (iter.hasNext())
         {
         XString  s = iter.next();
         s = s.concat("\n");
         theWholeFile.append(s.toString());
         }
      iter = null;
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      ArrayList<String> chunks = HtmlFile.compress(theWholeFile);
      /*.
      ==========================================================================================
      Write the buffer out to the output file
      ------------------------------------------------------------------------------------------ */
      for (String chunk : chunks)
         {
         output.print(chunk);
         }
      /*.
      ==========================================================================================
      Flush the output buffers
      ------------------------------------------------------------------------------------------ */
      output.flush();
      /*.
      ==========================================================================================
      Close the files
      ------------------------------------------------------------------------------------------ */
      output.close();
      }



   /*:                                    :METHOD:016:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method finds and removes all the HTML comments from a string.

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

   @return
      This method returns an XString containing the input string with all it's comments removed.

   @param
      pStr is the Object whose String representation will have its comments removed.
   ---------------------------------------------------------------------------------------------------- */
   private static <Type1> XString deleteComments(Type1 pStr)
      {
      String        str    = UString.toString(pStr);
      StringBuffer  strBuf = new StringBuffer(str);

      int startIdx = 0;
      int endIdx   = 0;
      while (true)
         {
         if (startIdx >= strBuf.length()) break;
         startIdx = strBuf.indexOf("<!--", startIdx);
         if (startIdx == (-1)) break;
         endIdx   = strBuf.indexOf("-->", startIdx + 4);
         if (endIdx == (-1)) break;
         endIdx = endIdx + 3;
         strBuf.delete(startIdx, endIdx);
         }
      return (new XString(strBuf.toString()));
      }



   /*:                                    :METHOD:017:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method finds and removes all the HTML comments from a file

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

   @param
      pFileIn is the name of the file that will be copied to the output file with comments removed.
      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.
   ---------------------------------------------------------------------------------------------------- */
   private static <Type1,Type2> void deleteComments(Type1  pFileIn, Type2  pFileOut) throws Exception
      {
      /*.
      ==========================================================================================
      Check the parameters.
      ------------------------------------------------------------------------------------------ */
      if (pFileIn == null)
         {
         throw new Exception("pFileIn is null");
         }
      if (pFileOut == null)
         {
         throw new Exception("pFileOut is null");
         }
      if (pFileIn.equals(pFileOut))
         {
         throw new Exception("pFileIn and pFileOut are the same file");
         }
      /*.
      ==========================================================================================
      Convert the filename parameters to XStrings
      ------------------------------------------------------------------------------------------ */
      XString  fileIn  = new XString(pFileIn);
      XString  fileOut = new XString(pFileOut);
      /*.
      ==========================================================================================
      Open the in and out file from their XString filenames
      ------------------------------------------------------------------------------------------ */
      XStringsIterator  iter   = new XStringsIterator(fileIn);
      PrintWriter       output = (new TextFile(fileOut)).printWriter();
      /*.
      ==========================================================================================
      Read each line of the source file into one big buffer
      ------------------------------------------------------------------------------------------ */
      StringBuilder  theWholeFile = new StringBuilder(2 * UMath.ONE_MEG);
      while (iter.hasNext())
         {
         XString  s = iter.next();
         s = s.concat("\n");
         theWholeFile.append(s.toString());
         }
      iter = null;
      /*.
      ==========================================================================================
      Delete all the comments from the StringBuffer
      ------------------------------------------------------------------------------------------ */
      String  wholeFile = HtmlFile.deleteComments(theWholeFile).toString();
      /*.
      ==========================================================================================
      Write the buffer out to the output file
      ------------------------------------------------------------------------------------------ */
      theWholeFile = null;
      output.print(wholeFile);
      /*.
      ==========================================================================================
      Flush the output buffers
      ------------------------------------------------------------------------------------------ */
      output.flush();
      /*.
      ==========================================================================================
      Close the files
      ------------------------------------------------------------------------------------------ */
      output.close();
      }



   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Private Instance Methods  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */



   /*:                                    :METHOD:018:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method initializes all the instance constants and variables. The intent of this method is to
   encapsulate all the common instance initializations in one place that can be called from all the
   constructors.

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

   @return
      True if the initialization succeeded, flase otherwise.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private boolean initializeInstanceConstantsAndVariables() throws Exception
      {
      boolean  successCode = true;

      if (successCode)
         {
         }
      return successCode;
      }



   /*:                                    :METHOD:019:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method packs this HtmlFile by removing all the trailing whitespace, comments, newlines,
   multiple consecutive spaces, and other unneeded spaces from it.

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

   @return
      A reference to this object
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private HtmlFile compress() throws Exception
      {
      /*.
      ==========================================================================================
      This method makes no sense on directory files, this must be a normal file
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory()) return this;
      /*.
      ==========================================================================================
      We'll output the compressed version of the file to a temp file so if the compressing
      process fails midway through, we don't corrupt the original file
      ------------------------------------------------------------------------------------------ */
      XFile  iFile = new XFile(this.getCanonicalPath());
      XFile  oFile = new XFile(File.createTempFile("compress-",".tmp"));
      /*.
      ==========================================================================================
      Compress it
      ------------------------------------------------------------------------------------------ */
      HtmlFile.compress(iFile.getCanonicalPath(), oFile.getCanonicalPath());
      /*.
      ==========================================================================================
      Delete the original file
      ------------------------------------------------------------------------------------------ */
      if ( ! iFile.delete())
         {
         throw new Exception("Cannot delete " + iFile.getCanonicalPath());
         }
      /*.
      ==========================================================================================
      And replace it with the compressed file.
      ------------------------------------------------------------------------------------------ */
      if ( ! oFile.renameTo(iFile))
         {
         throw new Exception("Cannot rename " + oFile.getCanonicalPath());
         }
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      return this;
      }



   /*:                                    :METHOD:020:BOOKMARK:
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method finds and removes all the HTML comments from this HtmlFile

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

   @return
      A reference to this object
   ---------------------------------------------------------------------------------------------------- */
   private HtmlFile deleteComments() throws Exception
      {
      /*.
      ==========================================================================================
      This method makes no sense on directory files, this must be a normal file
      ------------------------------------------------------------------------------------------ */
      if (this.isDirectory()) return this;
      /*.
      ==========================================================================================
      We'll output the comment-deleted version of the file to a temp file so if the delete
      process fails midway through, we don't corrupt the original file
      ------------------------------------------------------------------------------------------ */
      XFile  iFile = new XFile(this.getCanonicalPath());
      XFile  oFile = new XFile(File.createTempFile("deleteComments-",".tmp"));
      /*.
      ==========================================================================================
      Delete the comments
      ------------------------------------------------------------------------------------------ */
      HtmlFile.deleteComments(iFile.getCanonicalPath(), oFile.getCanonicalPath());
      /*.
      ==========================================================================================
      Delete the original file
      ------------------------------------------------------------------------------------------ */
      if ( ! iFile.delete())
         {
         throw new Exception("Cannot delete " + iFile.getCanonicalPath());
         }
      /*.
      ==========================================================================================
      And replace it with the comment-deleted file.
      ------------------------------------------------------------------------------------------ */
      if ( ! oFile.renameTo(iFile))
         {
         throw new Exception("Cannot rename " + oFile.getCanonicalPath());
         }
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      return this;
      }



   /*
   =====================================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Variables  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Public Class Variables  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Public Instance Variables  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Protected Class Variables  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Protected Instance Variables  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Private Class Variables  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */



   /*.
   ==========================================================================================
   cOut : console output.
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();



   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Private Instance Variables  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Constants  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Public Class Constants  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Protected Class Constants  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  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(HtmlFile.class.getName());
   private static final int      DFLT_LINE_LEN = ConsoleMessage.defaultLineLength();



   /*
   =====================================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Nested-Types  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Public Class Nested-Types  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Protected Class Nested-Types  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Private Class Nested-Types  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ===================================================================================================================== */



   /*::
   ======================================================================================================================== *//**
   Instances of this class store the definition of a matched pair of delimiter Strings as well as the location of the first
   occurrence of the starting delimiter within another specified String.
   *//*
   ======================================================================================================================== */
   private static class DelimiterData
      {
      private String   iStartDelimiterPattern;
      private String   iEndDelimiterPattern;
      private int      iStartDelimiterBeginIdx;
      private int      iStartDelimiterEndIdx;
      private int      iStartDelimiterLength;
      private String   iStartDelimiterStr;

      private DelimiterData(String pContainingStr, String pStartDelimiterPattern, String pEndDelimiterPattern)
         {
         iStartDelimiterPattern  = pStartDelimiterPattern;
         iEndDelimiterPattern    = pEndDelimiterPattern;

         int[] searchResults     = UString.indexOfRegexIgnoreCase(pContainingStr, iStartDelimiterPattern, 0);
         iStartDelimiterBeginIdx = searchResults[0];
         iStartDelimiterEndIdx   = searchResults[1];
         iStartDelimiterLength   = searchResults[2];

         if (iStartDelimiterBeginIdx == (-1)) iStartDelimiterStr = null;
         else                                 iStartDelimiterStr = pContainingStr.substring(iStartDelimiterBeginIdx, iStartDelimiterEndIdx);

         if (iStartDelimiterBeginIdx == (-1)) iStartDelimiterBeginIdx = (Integer.MAX_VALUE);
         }
      }  // End of Class DelimiterData


   /*::
   ======================================================================================================================== *//**
   Instances of this class store a list of DelimiterData objects and provide methods to fetch the start and end delimiter
   String of the earliest occuring delimiter in the list and the index at which it occurs.
   *//*
   ======================================================================================================================== */
   private static class DelimiterDataList extends ArrayList<DelimiterData>
      {
      private int  iIdxOfFirstDelimiter;

      private String startDelimiterPattern()
         {
         this.set();
         if (iIdxOfFirstDelimiter == (-1)) return null;
         return this.get(iIdxOfFirstDelimiter).iStartDelimiterPattern;
         }

      private String endDelimiterPattern()
         {
         this.set();
         if (iIdxOfFirstDelimiter == (-1)) return null;
         return this.get(iIdxOfFirstDelimiter).iEndDelimiterPattern;
         }

      private int startDelimiterBeginIdx()
         {
         this.set();
         if (iIdxOfFirstDelimiter == (-1)) return (Integer.MAX_VALUE);
         return this.get(iIdxOfFirstDelimiter).iStartDelimiterBeginIdx;
         }

      private int startDelimiterEndIdx()
         {
         this.set();
         if (iIdxOfFirstDelimiter == (-1)) return (Integer.MAX_VALUE);
         return this.get(iIdxOfFirstDelimiter).iStartDelimiterEndIdx;
         }

      private int startDelimiterLength()
         {
         this.set();
         if (iIdxOfFirstDelimiter == (-1)) return (Integer.MAX_VALUE);
         return this.get(iIdxOfFirstDelimiter).iStartDelimiterLength;
         }

      private String startDelimiterStr()
         {
         this.set();
         if (iIdxOfFirstDelimiter == (-1)) return null;
         return this.get(iIdxOfFirstDelimiter).iStartDelimiterStr;
         }

      private void set()
         {
         iIdxOfFirstDelimiter = (-1);
         int  currentMinStartIdx = (Integer.MAX_VALUE);
         for (int idx = 0; idx < this.size(); idx++)
            {
            DelimiterData  dd = this.get(idx);
            if (dd.iStartDelimiterBeginIdx < currentMinStartIdx)
               {
               currentMinStartIdx   = dd.iStartDelimiterBeginIdx;
               iIdxOfFirstDelimiter = idx;
               }
            }
         }
      }  // End of Class DelimiterDataList



   }  // End of Class HtmlFile



   /*
   =====================================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Notes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ===================================================================================================================== */