/*::.
==================================================================================================================================
=================================================¦ Copyright © 2007 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       ConsoleMessage.java
Originator: Allen Baker (2007.01.21 21:12)
LayoutRev:  5
================================================================================================================================== */



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



/*.
==========================================================================================
Imports
------------------------------------------------------------------------------------------ */
import java.io.*;
import java.util.*;



/*::
======================================================================================================================== *//**
This class formats messages for display on the command line console. It word-wraps the message and the title if one is
provided. If there is a title, it builds a box around the message with the title centered in the top border of the box.
If there is no title and the message is only one line after word-wrapping, then no box is put around the message. If
message is multi-linned after word-wrapping, a box is placed around it.

<P>
   <DL>
      <DT>
         <B>
            Example usage:
         </B>
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  XString  s1 = "";

                  XString  s2 =
                     "this is a wordy, multi-lined console message it is intended to  \n" +
                     "demonstrate word wrapping and multi-line message boxing.  if    \n" +
                     "you look at it you will notice that consecutive newline         \n" +
                     "terminated lines are word wrapped as a unit and indented by the \n" +
                     "same amount as the original indentation.                        \n" +
                     "   here is a bock containing a single indented line.            \n" +
                     "                                                                \n" +
                     "here is an out-dented block made up of 2 lines in the original  \n" +
                     "XString.  they will be wrapped together.                        \n" +
                     "   and this single line block will be wrapped separately.       \n";

                  XString  s3 =
                     "this is a single line console message.                          \n";

                  XString  s4 =
                     "this is a test of the emergency broadcast system. the           \n" +
                     "broadcasters of your area in voluntary cooperation with the     \n" +
                     "federal, state and local authorities have developed this system \n" +
                     "to keep you informed in the event of an emergency. if this had  \n" +
                     "been an actual emergency, the attention signal you just heard   \n" +
                     "would have been followed by official information, news or       \n" +
                     "instructions. this station serves the (operational area name)   \n" +
                     "area. this concludes this test of the emergency broadcast       \n" +
                     "system.                                                         \n";

                  XString  t1 = "";

                  XString  t2 =
                     "Emergency Broadcast System";

                  XString  t3 =
                     "this is a really really long title.  it doesn't say anything    \n" +
                     "important, it is just here to demonstrate how word wrapping is  \n" +
                     "done for titles.  take a look and decide for yourself if you    \n" +
                     "like it or not.  thank you; and please call again soon.         \n";

                  ConsoleMessage  obj        = null;
                  int             lineLength = 100;
                  Capitalizer     capitalizer = new UString.BasicEnglishCapitalizer();

                  System.out.println();
                  obj = new ConsoleMessage(s1).setLineLength(lineLength).setCapitalizer(capitalizer);
                  for (XString s : obj.lines()) System.out.println(s);

                  System.out.println();
                  obj = new ConsoleMessage(s2).setLineLength(lineLength).setCapitalizer(capitalizer);
                  for (XString s : obj.lines()) System.out.println(s);

                  System.out.println();
                  obj = new ConsoleMessage(s3).setLineLength(lineLength).setCapitalizer(capitalizer);
                  for (XString s : obj.lines()) System.out.println(s);

                  System.out.println();
                  obj = new ConsoleMessage(s4).setLineLength(lineLength).setCapitalizer(capitalizer);
                  for (XString s : obj.lines()) System.out.println(s);

                  System.out.println();
                  obj = new ConsoleMessage(t1,s1).setLineLength(lineLength).setCapitalizer(capitalizer);
                  for (XString s : obj.lines()) System.out.println(s);

                  System.out.println();
                  obj = new ConsoleMessage(t2,s2).setLineLength(lineLength).setCapitalizer(capitalizer);
                  for (XString s : obj.lines()) System.out.println(s);

                  System.out.println();
                  obj = new ConsoleMessage(t3,s3).setLineLength(lineLength).setCapitalizer(capitalizer);
                  for (XString s : obj.lines()) System.out.println(s);

                  System.out.println();
                  obj = new ConsoleMessage(t2,s4).setLineLength(lineLength).setCapitalizer(capitalizer);
                  for (XString s : obj.lines()) System.out.println(s);
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="ConsoleMessage.java.html">
               ConsoleMessage.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class ConsoleMessage
   {



   /*.
   ==========================================================================================
   cScreenLineLength
      The width of the cmd screen minus 1. (the minus one is because cmd wraps on the
      screen-width character). There isn't a good way to get the screen width programatically
      (that I know about yet), so when the cmd window properties are changed, this constant
      also needs to change.
   cTitlePadLength
      Extra padding needed to format title lines.
   cMaxLineLength
      The maximum line length available for word wrapping.
   cMinLineLength
      The minimum line length for word wrapping
   cDefaultMaxTitleLineLength
      The default maximum line length for titles.
   cDefaultMaxMessageLineLength
      The default maximum line length for word wrapping
   cDefaultLineLength
      The default preferred line length for word wrapping
   ------------------------------------------------------------------------------------------ */
   private static int  cScreenLineLength;
   private static int  cTitlePadLength;
   private static int  cMaxLineLength;
   private static int  cMinLineLength;
   private static int  cDefaultMaxTitleLineLength;
   private static int  cDefaultMaxMessageLineLength;
   private static int  cDefaultLineLength;
   /*.
   ==========================================================================================
   cInitialized
      Is this class initialized
   ------------------------------------------------------------------------------------------ */
   private static boolean cInitialized = false;



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a ConsoleMessage class object.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> ConsoleMessage(Type pMessage)
      {
      iMessage = new XString(pMessage);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a ConsoleMessage class object.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> ConsoleMessage(Type1 pTitle, Type2 pMessage)
      {
      iTitle   = new XString(pTitle);
      iMessage = new XString(pMessage);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the preferred line length that will be used for word wrapping. All messages will be
   wrapped to this length unless there are words within either the message or its title that are longer
   than the preferred length. If that is the case, then, the message is wrapped at whatever length is
   needed to accomadate the largest words in the title or message. If there are any words longer than
   the maximum width (see setMaxLineLength()), they will be broken at the maximum width.

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

   @return
      A reference to this object

   @param
      pLineLength is the preferred line length that this ConsoleMessage will use for word wrapping the
      message.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public ConsoleMessage setLineLength(int pLineLength)
      {
      iLineLength   = UMath.forceIntoRange(pLineLength, cMinLineLength, cMaxLineLength);
      iMessageLines = null;
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the maximum line length that can be used for word wrapping. This value comes into
   play if there are individual words in the title or message that will not fit in the line length
   specified by setLineLength(). Initially, the maximum line length is set to the entire width of the
   console screen. This method is to be used if for example, a XString (e.g. A time stamp) is going to
   be prepended to each line of the console message, thereby reducing the amount of space available for
   the console message itself.

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

   @return
      A reference to this object

   @param
      pMaxLineLength is the maximum line length that this ConsoleMessage will use for word wrapping the
      message.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public ConsoleMessage setMaxLineLength(int pMaxLineLength)
      {
      iMaxMsgLineLength = UMath.forceIntoRange(pMaxLineLength,                    cMinLineLength, cMaxLineLength);
      iMaxTtlLineLength = UMath.forceIntoRange(iMaxMsgLineLength-cTitlePadLength, cMinLineLength, cMaxLineLength);
      iMessageLines     = null;
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the maximum line length that can be used for word wrapping to pReduction less than
   it currently is. This is just a simple alternative to setMaxLineLength() and way of setting the
   maximum line length without the user having to do any calculations based on the screen size etc.

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

   @return
      A reference to this object

   @param
      pReduction is the amount to reduce the maximum line length that this ConsoleMessage will use for
      word wrapping the message.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public ConsoleMessage reduceMaxLineLength(int pReduction)
      {
      return this.setMaxLineLength(iMaxMsgLineLength - pReduction);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the capitalizer that will be used for the message.

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

   @return
      A reference to this object

   @param
      pCapitalizer is the Capitalizer that this ConsoleMessage will use for the message.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public ConsoleMessage setCapitalizer(UCapitalizer pCapitalizer)
      {
      iCapitalizer  = pCapitalizer;
      iMessageLines = null;
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method retrieves the formatted message and returns it as an ArrayList of XStrings.

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

   @return
      An ArrayList of XStrings in which each XString is one line of the formatted message.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public ArrayList<XString> lines()
      {
      if (iMessageLines == null)
         {
         this.formatMessage();
         }
      return iMessageLines;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method makes a formatted banner and returns it as an ArrayList of XStrings.

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

   @return
      An ArrayList of XStrings in which each XString is one line of the formatted banner.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public ArrayList<XString> banner()
      {
      /*.
      ==========================================================================================
      Save the original message and messagelines
      ------------------------------------------------------------------------------------------ */
      XString             tmpMsg   = iMessage;
      ArrayList<XString>  tmpLines = iMessageLines;
      int                 tmpLen   = iLineLength;
      /*.
      ==========================================================================================
      Reset message and messagelines
      ------------------------------------------------------------------------------------------ */
      iMessage      = new XString("");
      iMessageLines = null;
      /*.
      ==========================================================================================
      Adjust line length for any oversized words in the title
      ------------------------------------------------------------------------------------------ */
      this.adjustTitleMessageLineLength();
      /*.
      ==========================================================================================
      Create the new message which is just a line of banner characters
      ------------------------------------------------------------------------------------------ */
      iMessage = new XString("").alignLeft(iLineLength,Const.CHAR_FULL_BLOCK);
      /*.
      ==========================================================================================
      Create the new messagelines out of the new message
      ------------------------------------------------------------------------------------------ */
      this.formatMessage();
      /*.
      ==========================================================================================
      Save the new messagelines
      ------------------------------------------------------------------------------------------ */
      ArrayList<XString>  resultLines = iMessageLines;
      /*.
      ==========================================================================================
      Restore the original message and messagelines
      ------------------------------------------------------------------------------------------ */
      iLineLength   = tmpLen;
      iMessageLines = tmpLines;
      iMessage      = tmpMsg;
      /*.
      ==========================================================================================
      Return the saved new messagelines
      ------------------------------------------------------------------------------------------ */
      return resultLines;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method allows the user to turn word wrapping on and off. By default, word wrapping is ON.

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

   @return
      A reference to this object

   @param
      pWordWrap is true if word wrapping is to be ON, false if it is to be OFF.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public ConsoleMessage setWordWrap(boolean pWordWrap)
      {
      iWordWrap = pWordWrap;
      return this;
      }



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

   <P><B>Implementation: </B><A HREF="ConsoleMessage.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 ConsoleMessage 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="ConsoleMessage.java.html#010">View source</A>

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



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



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



   /*.
   ==========================================================================================
   Class Constants
      CLASS_NAME : the name of this class
   ------------------------------------------------------------------------------------------ */
   private static final XString  CLASS_NAME = new XString(ConsoleMessage.class.getName());



   /*.
   ==========================================================================================
   Class variables
      cOut : console output.
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();
   /*.
   ==========================================================================================
   Instance variables
      <BLOCKQUOTE>
         <PRE id="unindent">
            iWordWrap         : turns word wrapping on or off
            iTitle            : the title
            iMessage          : the message
            iLineLength       : the line length for word wrapping
            iMaxMsgLineLength : the maximum line length for messages
            iMaxTtlLineLength : the maximum line length for titles
            iCapitalizer      : the capitalizer used by the word wrapper
            iMessageLines     : where the formatted message is stored one line
                                per ArrayList element
         </PRE>
      </BLOCKQUOTE>
   ------------------------------------------------------------------------------------------ */
   private boolean             iWordWrap         = true;
   private XString             iTitle            = new XString("");
   private XString             iMessage          = new XString("");
   private int                 iLineLength       = defaultLineLength();
   private int                 iMaxMsgLineLength = defaultMaxMessageLineLength();
   private int                 iMaxTtlLineLength = defaultMaxTitleLineLength();
   private UCapitalizer        iCapitalizer      = null;
   private ArrayList<XString>  iMessageLines     = null;



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method formats the top line of a message box

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private XString topLine(int pTitleWidth)
      {
      XString  line;

      line = new XString("").alignLeft(pTitleWidth+2,Const.CHAR_DOUBLE_HORIZONTAL);
      line = new XString(Const.CHAR_DOWN_SINGLE_AND_HORIZONTAL_DOUBLE + line.string() + Const.CHAR_DOWN_SINGLE_AND_HORIZONTAL_DOUBLE);
      line = line.alignCenter(iLineLength,Const.CHAR_DOUBLE_DOWN_AND_HORIZONTAL);
      return line;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method formats intermediate title lines of a message box

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private <Type> XString intermediateTitleLine(int pTitleWidth, Type pStr)
      {
      XString  line;

      line = new XString(pStr).alignCenter(pTitleWidth+2,' ');
      line = new XString(Const.CHAR_LIGHT_VERTICAL + line.string() + Const.CHAR_LIGHT_VERTICAL);
//      line = new XString(Const.CHAR_VERTICAL_SINGLE_AND_LEFT_DOUBLE + line.string() + Const.CHAR_VERTICAL_SINGLE_AND_RIGHT_DOUBLE);
//      line = line.alignCenter(iLineLength,Const.CHAR_DOUBLE_VERTICAL_AND_HORIZONTAL);
      line = line.alignCenter(iLineLength,Const.CHAR_DOUBLE_VERTICAL);
      return line;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method formats the bottom title line of a message box

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private <Type> XString bottomTitleLine(int pTitleWidth, Type pStr)
      {
      XString  line;

      line = new XString(pStr).alignCenter(pTitleWidth+2,' ');
      line = new XString(Const.CHAR_VERTICAL_SINGLE_AND_LEFT_DOUBLE + line.string() + Const.CHAR_VERTICAL_SINGLE_AND_RIGHT_DOUBLE);
      line = line.alignCenter(iLineLength,Const.CHAR_DOUBLE_UP_AND_HORIZONTAL);
      return line;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method formats the bottom of a title box within a message box

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private XString bottomTitleBox(int pTitleWidth)
      {
      XString  line;

      line = new XString("").alignLeft(pTitleWidth+2,Const.CHAR_LIGHT_HORIZONTAL);
      line = new XString(Const.CHAR_LIGHT_UP_AND_RIGHT + line.string() + Const.CHAR_LIGHT_UP_AND_LEFT);
      line = line.alignCenter(iLineLength,' ');
      return line;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method formats the bottom line of a message box

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private XString bottomLine()
      {
      XString  line;

      line = new XString("").alignLeft(iLineLength,Const.CHAR_LIGHT_HORIZONTAL);
      return line;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method formats the message.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private void formatMessage()
      {
      /*.
      ==========================================================================================
      Adjust for any oversized words in the title or message
      ------------------------------------------------------------------------------------------ */
      this.adjustTitleMessageLineLength();
      /*.
      ==========================================================================================
      Here's where things go while we're building
      ------------------------------------------------------------------------------------------ */
      ArrayList<XString>  titleLines   = new ArrayList<XString>();
      ArrayList<XString>  messageLines = new ArrayList<XString>();
      ArrayList<XString>  bottomLines  = new ArrayList<XString>();
      XString             border       = null;
      /*.
      ==========================================================================================
      Parse the message into separate lines. If wordwrapping is turned off, just parse on the
      newlines.
      ------------------------------------------------------------------------------------------ */
      if (iWordWrap)
         {
         messageLines = iMessage.wordWrap(iLineLength,iCapitalizer);
         }
      else
         {
         ArrayList<XString>  temp = iMessage.tokenizedString("\n");
         for (XString str : temp)
            {
            messageLines.add
               (
               new XString
                  (
                  (iCapitalizer!=null)? iCapitalizer.capitalize(str) : str
                  )
               );
            }
         }
      /*.
      ==========================================================================================
      If there is no title and if the message wrapped to multiple lines, make a box to put
      around the message.
      ------------------------------------------------------------------------------------------ */
      if ((iTitle == null) || (iTitle.equals("")))
         {
         if  (messageLines.size() > 1)
            {
            border = new XString("").alignLeft(iLineLength,Const.CHAR_DOUBLE_DOWN_AND_HORIZONTAL);
            titleLines.add(border);
            border = new XString("").alignLeft(iLineLength,Const.CHAR_DOUBLE_UP_AND_HORIZONTAL);
            titleLines.add(border);
            border = bottomLine();
            bottomLines.add(border);
            }
         }
      /*.
      ==========================================================================================
      Otherwise, if there is a title . . .
      ------------------------------------------------------------------------------------------ */
      else
         {
         /*.
         ==========================================================================================
         Wrap the title and figure out what the longest line in it is.
         ------------------------------------------------------------------------------------------ */
         ArrayList<XString>  wrappedTitleLines = iTitle.wordWrap(iLineLength - 12,iCapitalizer);
         int                 maxLen            = UString.longestStringLength(wrappedTitleLines);
         /*.
         ==========================================================================================
         Add a box top
         ------------------------------------------------------------------------------------------ */
         titleLines.add(topLine(maxLen));
         /*.
         ==========================================================================================
         Add the wrapped title lines
         ------------------------------------------------------------------------------------------ */
         int count = 0;
         for (XString title : wrappedTitleLines)
            {
            count++;
            if (count < wrappedTitleLines.size())
               {
               titleLines.add(intermediateTitleLine(maxLen,title));
               }
            else
               {
               titleLines.add(bottomTitleLine(maxLen,title));
               }
            }
         /*.
         ==========================================================================================
         Add the bottom line of the top of the title box
         ------------------------------------------------------------------------------------------ */
         titleLines.add(bottomTitleBox(maxLen));
         /*.
         ==========================================================================================
         Add the bottom line of the message box
         ------------------------------------------------------------------------------------------ */
         bottomLines.add(bottomLine());
         }
      /*.
      ==========================================================================================
      Mak a new ArrayList to put it all toghether in.
      ------------------------------------------------------------------------------------------ */
      iMessageLines = new ArrayList<XString>();
      /*.
      ==========================================================================================
      Add the box top, wrapped title lines, message and box bottom.
      ------------------------------------------------------------------------------------------ */
      for (XString s : titleLines  ) iMessageLines.add(s);
      for (XString s : messageLines) iMessageLines.add(s);
      for (XString s : bottomLines ) iMessageLines.add(s);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method adjusts the title, message, and line length if any words in the title or message are too
   big to fit in the specified line length.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private void adjustTitleMessageLineLength()
      {
      /*.
      ==========================================================================================
      Make sure there will be enough room for the title to wrap properly
      ------------------------------------------------------------------------------------------ */
      int  ttlLen;
      while ((ttlLen = XString.longestStringLength(iTitle.tokenizedString(" \n"))) > iMaxTtlLineLength)
         {
         /*.
         ==========================================================================================
         BUG or MY-STUPID ? Don't know why this has to be called in a loop, but it does; I tested
         it. BreakBigWords() is supposed to loop until all the big words are broken.
         ------------------------------------------------------------------------------------------ */
         iTitle = this.breakBigWords(iTitle,iMaxTtlLineLength);
         }
      /*.
      ==========================================================================================
      The longest title line is the length of the longest title word plus the length of the
      title pad
      ------------------------------------------------------------------------------------------ */
      ttlLen += cTitlePadLength;
      /*.
      ==========================================================================================
      If the title length is bigger than the current line length, up the line length to
      accomadate it.
      ------------------------------------------------------------------------------------------ */
      iLineLength = UMath.max(iLineLength,ttlLen);
      /*.
      ==========================================================================================
      Make sure there will be enough room for the message to wrap properly
      ------------------------------------------------------------------------------------------ */
      int  msgLen;
      while ((msgLen = XString.longestStringLength(iMessage.tokenizedString(" \n"))) > iMaxMsgLineLength)
         {
         /*.
         ==========================================================================================
         BUG ? Don't know why this has to be called in a loop, but it does; I tested it.
         BreakBigWords() is supposed to loop until all the big words are broken.
         ------------------------------------------------------------------------------------------ */
         iMessage = this.breakBigWords(iMessage,iMaxMsgLineLength);
         }
      /*.
      ==========================================================================================
      If the message length is bigger than the current line length, up the line length to
      accomadate it.
      ------------------------------------------------------------------------------------------ */
      iLineLength = UMath.max(iLineLength,msgLen);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method breaks up any words in pStr that are too long to fit into a space pMax characters
   wide.<P>

      BUG ? Don't know why this has to be called in a loop, but it does; I tested it. BreakBigWords()
      is supposed to loop until all the big words are broken.

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

   @return
      The new XString with broken up words.

   @param
      pStr is the XString to find words in that are too big.
   @param
      pMax is the maximum size that a word is allowed to be.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private XString breakBigWords(XString pStr, int pMax)
      {
      /*.
      ==========================================================================================
      While there are any words in pStr that are too big to wrap in a line length limited to
      pMax ...
      ------------------------------------------------------------------------------------------ */
      ArrayList<XString>  words = pStr.tokenizedString(" \n");
      int                 len;
      while ((len = XString.longestStringLength(words)) > pMax)
         {
         /*.
         ==========================================================================================
         Find the offending words and break them at pMax
         ------------------------------------------------------------------------------------------ */
         for (XString wrd : words)
            {
            if (wrd.length() > pMax)
               {
               XString       tmpStr = null;
               StringBuffer  tmpBuf = new StringBuffer(UString.toString(wrd));
               tmpBuf.insert(pMax," ");
               tmpStr = new XString(tmpBuf);
               pStr   = pStr.replace((CharSequence)wrd,(CharSequence)tmpStr);
               }
            }
         words = pStr.tokenizedString(" \n");
         }
      return pStr;
      }



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



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



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method initializes the class variables.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static void initialize()
      {
      if (! cInitialized)
         {
         /*.
         ==========================================================================================
         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
         ------------------------------------------------------------------------------------------ */
         cScreenLineLength            = ConsoleInfo.width()-1;
         cTitlePadLength              = 10;
         cMaxLineLength               = cScreenLineLength - (int)(0.08 * cScreenLineLength);
         cMinLineLength               = UMath.forceIntoRange(cTitlePadLength+15,             cTitlePadLength, cMaxLineLength);
         cDefaultMaxTitleLineLength   = UMath.forceIntoRange(cMaxLineLength-cTitlePadLength, cMinLineLength,  cMaxLineLength);
         cDefaultMaxMessageLineLength = UMath.forceIntoRange(cScreenLineLength,              cMinLineLength,  cMaxLineLength);
         cDefaultLineLength           = UMath.forceIntoRange(cDefaultMaxMessageLineLength,   cMinLineLength,  cMaxLineLength);
         cInitialized = true;
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   These methods return the class variables.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static int screenLineLength()
      {
      initialize();
      return cScreenLineLength;
      }

   public static int titlePadLength()
      {
      initialize();
      return cTitlePadLength;
      }

   public static int maxLineLength()
      {
      initialize();
      return cMaxLineLength;
      }

   public static int minLineLength()
      {
      initialize();
      return cMinLineLength;
      }

   public static int defaultMaxTitleLineLength()
      {
      initialize();
      return cDefaultMaxTitleLineLength;
      }

   public static int defaultMaxMessageLineLength()
      {
      initialize();
      return cDefaultMaxMessageLineLength;
      }

   public static int defaultLineLength()
      {
      initialize();
      return cDefaultLineLength;
      }



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

   <P><B>Implementation: </B><A HREF="ConsoleMessage.java.html#021">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 an object and send its output to the ConsoleStream
      ------------------------------------------------------------------------------------------ */
      ConsoleMessage  obj = new ConsoleMessage("","");
      /*.
      ==========================================================================================
      Test code
      ------------------------------------------------------------------------------------------ */
      obj.test();
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      XString  s0 = new XString
         (
         "ThisIsAReallyLongUnbrokenWord__rogeghegweowhgcongcnhtvgnghiopggoie" +
         "hrghguhguehgphghtghpohprhpihpiww[fw[wh[hgeghvrogeghegweowhgcongcnh" +
         "tvgnghiopggoiehrghguhguehgphghtghpohprhpihpiwweghvrogeghegweowhgco" +
         "ngcnhtvgnghiopggoiehrghguhguehgphghtghpohprhpihpiww[fw[wh[hgecrfff" +
         "ngcnhtvgnghiopthcijefimfjfeghv__ThisHasBeenAReallyLongUnbrokenWord"
         );

      XString  s1 = new XString("");

      XString  s2 = new XString
         (
         "this is a wordy, multi-lined console message it is intended to  \n" +
         "demonstrate word wrapping and multi-line message boxing.  if    \n" +
         "you look at it you will notice that consecutive newline         \n" +
         "terminated lines are word wrapped as a unit and indented by the \n" +
         "same amount as the original indentation.                        \n" +
         "   here is a bock containing a single indented line.            \n" +
         "                                                                \n" +
         "here is an out-dented block made up of 2 lines in the original  \n" +
         "XString.  they will be wrapped together.                        \n" +
         "   and this single line block will be wrapped separately.       \n"
         );

      XString  s3 = new XString
         (
         "this is a single line console message.                          \n"
         );

      XString  s4 = new XString
         (
         s0                                                                   +
         "                                                                \n" +
         "                                                                \n" +
         "    "                                                               +
         "this is a test"
         );

      XString  s5 = new XString
         (
         "<B>Example usage:</B><PRE>\n" +
         "*   // ===============================================\n" +
         "*   // copy to sysout the names of all the files that match the file\n" +
         "*   // specifier - \"/test/testin/*.iwa\".\n" +
         "*   // -----------------------------------------------\n" +
         "*      FileNameList a    = new FileNameList(\"/test/testin/*.iwa\");\n" +
         "*      Iterator     iter = a.iterator();\n" +
         "*\n" +
         "*      System.out.println(\"<\" + a.getDirectoryName() + \"><\" + a.getFileNamePattern() + \">\");\n" +
         "*      while (iter.hasNext())\n" +
         "*         {\n" +
         "*         System.out.println(iter.next());\n" +
         "*         }\n" +
         "</PRE>\n"
         );

      XString  t1 = new XString("");

      XString  t2 = new XString
         (
         "Emergency Broadcast System"
         );

      XString  t3 = new XString
         (
         "this is a really really long title.  it doesn't say anything    \n" +
         "important, it is just here to demonstrate how word wrapping is  \n" +
         "done for titles.  take a look and decide for yourself if you    \n" +
         "like it or not.  thank you; and please call again soon.         \n"
         );

      int             lineLength  = 64;
      boolean         wordWrap    = true;
//      boolean         wordWrap    = false;
      UCapitalizer    capitalizer = new UString.BasicEnglishCapitalizer();

      System.out.println();
      obj = new ConsoleMessage(s1).setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.lines()) System.out.println(s);

      System.out.println();
      obj = new ConsoleMessage(s2).setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.lines()) System.out.println(s);

      System.out.println();
      obj = new ConsoleMessage(s3).setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.lines()) System.out.println(s);

      System.out.println();
      obj = new ConsoleMessage(s4).setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.lines()) System.out.println(s);

      System.out.println();
      obj = new ConsoleMessage(t1,s1).setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.lines()) System.out.println(s);

      System.out.println();
      obj = new ConsoleMessage(t2,s2).setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.lines()) System.out.println(s);

      System.out.println();
      obj = new ConsoleMessage(t3,s3).setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.lines()) System.out.println(s);

      System.out.println();
      obj = new ConsoleMessage(t2,s4).setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.lines()) System.out.println(s);

      System.out.println();
      obj = new ConsoleMessage(s0,s4).setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.lines()) System.out.println(s);

      System.out.println();
      obj = new ConsoleMessage("tuna",s5).setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.lines()) System.out.println(s);

      System.out.println();
      obj = new ConsoleMessage(s0,s4).setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.banner()) System.out.println(s);

      System.out.println();
      obj = new ConsoleMessage("Regular Title","").setLineLength(lineLength).setCapitalizer(capitalizer).setWordWrap(wordWrap);
      for (XString s : obj.banner()) System.out.println(s);
      }



   }  // class ConsoleMessage



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