/*::.
==================================================================================================================================
=================================================¦ Copyright © 2001 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       TextReaderIterator.java
Originator: Allen Baker (2001.11.09)
LayoutRev:  5
================================================================================================================================== */



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



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



/*::
======================================================================================================================== *//**
Instances of this class are Iterators over the lines in a text file. The text file is treated as an ordered list of
xStrings. Each XString contains one line of the text file. This provides an alternative and cleaner paradigm (an
iterator pattern) for reading a text file from the one provided by the BufferedReader or TextReader class.<P>

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

<P>
   <DL>
      <DT>
         <B>
            Example usage:
         </B>
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  =====================================================================================
                  Use a TextReaderIterator to iterate through the lines of a text file and copy the
                  contents to stdout. Notice that a TextReaderIterator is just an implementation of the
                  standard Iterator interface and so can be used like any other Iterator.
                  -------------------------------------------------------------------------------------

                  Iterator iter = new TextReaderIterator("foo.txt");

                  while (iter.hasNext())
                     {
                     XString s = (XString)iter.next();
                     System.out.println(s);
                     }
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="TextReaderIterator.java.html">
               TextReaderIterator.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class TextReaderIterator implements Iterator<XString>
   {



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a new TextReaderIterator, given the File to read from.

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

   @param
      pFile is the file to read from.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public TextReaderIterator(File pFile) throws Exception
      {
      /*.
      ==========================================================================================
      A new object of this class always opens another instance of the file for reading
      ------------------------------------------------------------------------------------------ */
      iTextReader = new TextReader(pFile);
      /*.
      ==========================================================================================
      Put the new object into its initialized state.
      ------------------------------------------------------------------------------------------ */
      initialize();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a new TextReaderIterator, given an InputStream (such as System.in) to read from.

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

   @param
      pInputStream is the InputStream to read from.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public TextReaderIterator(InputStream pInputStream) throws Exception
      {
      /*.
      ==========================================================================================
      A new object of this class always opens another instance of the file for reading
      ------------------------------------------------------------------------------------------ */
      iTextReader = new TextReader(pInputStream);
      /*.
      ==========================================================================================
      Put the new object into its initialized state.
      ------------------------------------------------------------------------------------------ */
      initialize();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a new TextReaderIterator, given the name of the file to read from.

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

   @param
      pFileName is the name of the file to read from.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> TextReaderIterator(Type pFileName) throws Exception
      {
      /*.
      ==========================================================================================
      A new object of this class always opens another instance of the file for reading
      ------------------------------------------------------------------------------------------ */
      iTextReader = new TextReader(pFileName);
      /*.
      ==========================================================================================
      Put the new object into its initialized state.
      ------------------------------------------------------------------------------------------ */
      initialize();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns true if the iteration has more elements. (In other words, returns true if next
   would return an element rather than throwing an exception.)

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

   @return
      True if the iterator has more elements.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean hasNext()
      {
      return (iNext != null);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the next element in the iteration but does not move the iterator ahead. This
   means that the next time peek() or next() is called, the same Sting is returned again.<P>

   If there's no next element, this method throws a NoSuchElementException. To avoid this exception,
   the user should always call hasNext() to test whether or not the iteration has a next element before
   calling this method.

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

   @return
      The next element in the iteration.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString peek() throws NoSuchElementException
      {
      /*.
      ==========================================================================================
      If there's no next XString, the user shouldn't have called this method. Throw an
      exception.
      ------------------------------------------------------------------------------------------ */
      if (iNext == null)
         {
         throw new NoSuchElementException();
         }
      /*.
      ==========================================================================================
      Return the previously staged XString
      ------------------------------------------------------------------------------------------ */
      return iNext;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the next element in the interation.<P>

   If there's no next element, this method throws a NoSuchElementException. To avoid this exception,
   the user should always call hasNext() to test whether or not the iteration has a next element before
   calling this method.

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

   @return
      The next element in the iteration.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString next() throws NoSuchElementException
      {
      /*.
      ==========================================================================================
      If there's no next XString, the user shouldn't have called this method. Throw an
      exception.
      ------------------------------------------------------------------------------------------ */
      if (iNext == null)
         {
         throw new NoSuchElementException();
         }
      try
         {
         /*.
         ==========================================================================================
         Get a reference to the staged XString. This is what we'll return
         ------------------------------------------------------------------------------------------ */
         XString s = iNext;
         /*.
         ==========================================================================================
         Prefetch the next XString from the file and stage it. This allows the hasNext() method to
         work. This prefetched XString will be the XString returned the next time next() is called.
         ------------------------------------------------------------------------------------------ */
         prefetch();
         /*.
         ==========================================================================================
         Return the previously staged XString
         ------------------------------------------------------------------------------------------ */
         return s;
         }
      catch (Exception e)
         {
         throw new NoSuchElementException();
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method is not implemented. It is an optional method in the Iterator interface that removes from
   the underlying collection the last element returned by the iterator. If this method is called it
   will throw an UnsupportedOperationException.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void remove() throws UnsupportedOperationException
      {
      throw new UnsupportedOperationException();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method closes the underlying file if it is still open. It is generally not needed. It is
   automatically called when the end of file is encountered. It is also automatically called during
   garbage collection when the TextReaderIterator's finalize() method is called.<P>

   It should be called by the user if the user abandons reading the file before the end of file has
   been reached (before the hasNext() method returns false).<P>

   It is harmless to call this method when the file is not open.<P>

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void cleanup() throws Exception
      {
      iNext = null;
      iOpen = false;
      if (iTextReader != null)
         {
         iTextReader.close();
         iTextReader = null;
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tells this object whether to skip blank lines or not. A blank line is a line of the text
   file that has no character in it and is represented as an empty string ("") when read from the file.

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

   @return
      A reference to this object

   @param
      pSkipBlankLines is the boolean value to turn on or off the skipping of blank lines. If
      pSkipBlankLines is true, then the TextReaderIterator will skip blank lines. If false, it will not
      skip blank lines.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public TextReaderIterator setSkipBlankLines(boolean pSkipBlankLines)
      {
      iSkipBlankLines = pSkipBlankLines;
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method tells this object whether to skip white space lines or not. A white space line is a line
   of the text file that contains only white space characters.<P>

   This method is a superset of setSkipBlankLines() because white space lines include blank lines.

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

   @return
      A reference to this object

   @param
      pSkipWhiteSpaceLines is the boolean value to turn on or off the skipping of white space lines. If
      pSkipWhiteSpaceLines is true, then the TextReaderIterator will skip white space lines. If false,
      it will not skip white space lines.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public TextReaderIterator setSkipWhiteSpaceLines(boolean pSkipWhiteSpaceLines)
      {
      iSkipWhiteSpaceLines = pSkipWhiteSpaceLines;
      return this;
      }



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

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

   @return
      A reference to this object

   @param
      pConsole is a ConsoleStream through which this objects sends its output.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public TextReaderIterator 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="TextReaderIterator.java.html#011">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public TextReaderIterator 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  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method closes the underlying file if this object is holding it open when the garbage collector
   decides to clean up the object.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   protected void finalize() throws Throwable
      {
      if (iOpen)
         {
         cleanup();
         }
      super.finalize();
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  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(TextReaderIterator.class.getName());
   private static final int      DFLT_LINE_LEN = ConsoleMessage.defaultLineLength();
   /*.
   ==========================================================================================
   Class variables
      cOut : console output.
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();
   /*.
   ==========================================================================================
   Each instance of this object creates an instance of a TextReader (iTextReader) for reading
   the underlying file. In order for the hasNext() method to work, the next line in the file
   is always prefetched and stored in iNext.
   ------------------------------------------------------------------------------------------ */
   private TextReader  iTextReader          = null;
   private XString     iNext                = null;
   private boolean     iOpen                = false;
   private boolean     iSkipBlankLines      = false;
   private boolean     iSkipWhiteSpaceLines = false;



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method puts an object into its initial state.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private void initialize() throws Exception
      {
      /*.
      ==========================================================================================
      A new object of this class always starts in the open state
      ------------------------------------------------------------------------------------------ */
      iOpen = true;
      /*.
      ==========================================================================================
      Prefetch the next String from the file and stage it. This allows the hasNext() method to
      work. This prefetched String will be the XString returned the next time next() is called.
      ------------------------------------------------------------------------------------------ */
      prefetch();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method prefetches the next String from the file and stages it. This allows the hasNext() method
   to work. The prefetched String will be the XString returned the next time next() is called.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private void prefetch() throws Exception
      {
      /*.
      ==========================================================================================
      Prefetch the next String from the file and stage it.
      ------------------------------------------------------------------------------------------ */
      if (iSkipBlankLines)
         {
         do
            {
            XString s = iTextReader.readLine();
            iNext = (s==null)? null : new XString(s);
            } while ((iNext != null) && (iNext.equals("")));
         }
      else if (iSkipWhiteSpaceLines)
         {
         do
            {
            XString s = iTextReader.readLine();
            iNext = (s==null)? null : (new XString(s));
            } while ((iNext != null) && (iNext.trim().equals("")));
         }
      else
         {
         XString s = iTextReader.readLine();
         iNext = (s==null)? null : new XString(s);
         }
      /*.
      ==========================================================================================
      If the prefetch encountered the end of file, close the file.
      ------------------------------------------------------------------------------------------ */
      if (iNext == null)
         {
         cleanup();
         }
      }



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



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



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

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

   <P><B>Implementation: </B><A HREF="TextReaderIterator.java.html#015">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);
      /*.
      ==========================================================================================
      Gotta say what file to read
      ------------------------------------------------------------------------------------------ */
      if (pArgs.length < 1)
         {
         cOut.println
            (
            "usage: java com.adbaker.util.TextReaderIterator inputFileName"
            );
         System.exit(-1);
         }
      /*.
      ==========================================================================================
      Use a TextReaderIterator to iterate through the lines of a text file and copy the contents
      to stdout.
      ------------------------------------------------------------------------------------------ */
      TextReaderIterator iter = new TextReaderIterator(pArgs[0]);
      iter.test();
      int i = 0;
      while ((++i<100) && (iter.hasNext()))
         {
         XString s = iter.next();
         System.out.println(s);;
         }
      }



   }  // class TextReaderIterator



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