/*::.
==================================================================================================================================
=================================================¦ Copyright © 2003 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       XStringsIterator.java
Originator: Allen Baker (2003.01.01)
LayoutRev:  5
================================================================================================================================== */



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



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



/*::
======================================================================================================================== *//**
Instances of this class are iterators over collections or Files of XStrings.<P>

If an XStringsIterator instance is set to be non-case-sensitive, its methods will return instances of a case-insensitive
subclass of XString, namely an NCString.<P>

The advantage of using a XStringsIterator instead of a TextReaderIterator or Iterator 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">
                  =========================================================================================
                  We're going to create XStringIterators over a TextFile, an ArrayList,
                  and a HashSet.
                  -----------------------------------------------------------------------------------------
                  TextFile  file  = new TextFile("tmp.tmp");
                  ArrayList array = new ArrayList(400);
                  HashSet   set   = new HashSet(400);

                  =========================================================================================
                  We'll use the same XStringIterator variable to store the
                  XStringIterator for each of them.
                  -----------------------------------------------------------------------------------------
                  XStringsIterator iter;

                  =========================================================================================
                  First, iterate over the lines in the TextFile. Use this iteration
                  to populate the ArrayList and the HashSet.
                  -----------------------------------------------------------------------------------------
                  iter = new XStringsIterator(file);
                  while (iter.hasNext())
                     {
                     XString str = iter.next();
                     System.out.println(str);
                     array.add(str);
                     set.add(str);
                     }

                  =========================================================================================
                  Now iterate over the ArrayList
                  -----------------------------------------------------------------------------------------
                  iter = new XStringsIterator(array);
                  while (iter.hasNext())
                     {
                     XString str = iter.next();
                     System.out.println(str);
                     }

                  =========================================================================================
                  Now iterate over the HashSet
                  -----------------------------------------------------------------------------------------
                  iter = new XStringsIterator(set);
                  while (iter.hasNext())
                     {
                     XString str = iter.next();
                     System.out.println(str);
                     }
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="XStringsIterator.java.html">
               xStringsIterator.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class XStringsIterator
   {



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

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

   @param
      pFile is the file to read from.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XStringsIterator(File pFile) throws Exception
      {
      iIter = new TextReaderIterator(pFile);
      iFile = pFile;
      }



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

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

   @param
      pInputStream is the InputStream to read from.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XStringsIterator(InputStream pInputStream) throws Exception
      {
      iIter        = new TextReaderIterator(pInputStream);
      iInputStream = pInputStream;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a new XStringsIterator, given a Collection to read from.

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

   @param
      pCollection is the Collection to read from.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XStringsIterator(Collection pCollection)
      {
      iIter       = pCollection.iterator();
      iCollection = pCollection;
      }



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

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

   @param
      pFileName is the name of the file to read from.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> XStringsIterator(Type pFileName) throws Exception
      {
      iIter     = new TextReaderIterator(pFileName);
      iFileName = new XString(pFileName);
      }


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

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

   @param
      pFile is the file to read from.
   @param
      pCaseSensitive sets the case-sensitivity of the instance created by this constructor. If
      pCaseSensitive is set to true, then normal XStrings are returned from this instance's next() and
      peek() methods. If set to false, then instances of a subclass of XString, namely NCStrings (No
      Case Strings: objects that are not case sensitive) are returned from this instance's next() and
      peek() methods.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XStringsIterator(File pFile, boolean pCaseSensitive) throws Exception
      {
      iIter          = new TextReaderIterator(pFile);
      iCaseSensitive = pCaseSensitive;
      }



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

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

   @param
      pInputStream is the InputStream to read from.
   @param
      pCaseSensitive sets the case-sensitivity of the instance created by this constructor. If
      pCaseSensitive is set to true, then normal XStrings are returned from this instance's next() and
      peek() methods. If set to false, then instances of a subclass of XString, namely NCStrings (No
      Case Strings: objects that are not case sensitive) are returned from this instance's next() and
      peek() methods.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XStringsIterator(InputStream pInputStream, boolean pCaseSensitive) throws Exception
      {
      iIter          = new TextReaderIterator(pInputStream);
      iCaseSensitive = pCaseSensitive;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a new XStringsIterator, given a Collection to read from.

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

   @param
      pCollection is the Collection to read from.
   @param
      pCaseSensitive sets the case-sensitivity of the instance created by this constructor. If
      pCaseSensitive is set to true, then normal XStrings are returned from this instance's next() and
      peek() methods. If set to false, then instances of a subclass of XString, namely NCStrings (No
      Case Strings: objects that are not case sensitive) are returned from this instance's next() and
      peek() methods.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XStringsIterator(Collection pCollection, boolean pCaseSensitive)
      {
      iIter          = pCollection.iterator();
      iCaseSensitive = pCaseSensitive;
      }



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

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

   @param
      pFileName is the name of the file to read from.
   @param
      pCaseSensitive sets the case-sensitivity of the instance created by this constructor. If
      pCaseSensitive is set to true, then normal XStrings are returned from this instance's next() and
      peek() methods. If set to false, then instances of a subclass of XString, namely NCStrings (No
      Case Strings: objects that are not case sensitive) are returned from this instance's next() and
      peek() methods.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> XStringsIterator(Type pFileName, boolean pCaseSensitive) throws Exception
      {
      iIter          = new TextReaderIterator(pFileName);
      iCaseSensitive = pCaseSensitive;
      }



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

   @return
      True if the iterator has more elements.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean hasNext()
      {
      return ((iPushBackQueue.size() > 0) || iIter.hasNext());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   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 this XStringsIterator is set to be non-case-sensitive, this method will return an instance of a
   case-insensitive subclass of XString, namely an NCString.<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="XStringsIterator.java.html#009">View source</A>

   @return
      The next element in the iteration.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString peek() throws NoSuchElementException, UnsupportedOperationException
      {
      XString  xstr = null;
//      /*.
//      ==========================================================================================
//      for now, we don't support peeking at anything but a file.
//      ------------------------------------------------------------------------------------------ */
//      if ( ! Util.className(iIter).equals("cosmicabyss.com.utl.TextReaderIterator"))
//         {
//         throw new UnsupportedOperationException();
//         }
      /*.
      ==========================================================================================
      If there's something on the pushback queue, it's the next thing in the iteration so peek
      at it.
      ------------------------------------------------------------------------------------------ */
      if (iPushBackQueue.size() > 0)
         {
         xstr = iPushBackQueue.peek();
         }
      /*.
      ==========================================================================================
      Otherwise, do a real peek
      ------------------------------------------------------------------------------------------ */
      else
         {
         xstr = (XString)((TextReaderIterator)iIter).peek();
         }
      /*.
      ==========================================================================================
      If we're not being case sensitive, return an NCString
      ------------------------------------------------------------------------------------------ */
      if ( ! iCaseSensitive)
         {
         xstr = new NCString(xstr);
         }
      return  xstr;
      }



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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void cleanup()
      {
      /*.
      ==========================================================================================
      Assume we're iterating over a file and call its cleanup()
      ------------------------------------------------------------------------------------------ */
      try
         {
         ((TextReaderIterator)iIter).cleanup();
         }
      /*.
      ==========================================================================================
      If the assumption was wrong and were iterating over something else, an exception will be
      thrown and we end up here. Nothing needs to be done to close an iterator over anything
      else.
      ------------------------------------------------------------------------------------------ */
      catch (Exception e)
         {
         }
      }



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

   If this XStringsIterator is set to be non-case-sensitive, this method will return an instance of a
   case-insensitive subclass of XString, namely an NCString.<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="XStringsIterator.java.html#011">View source</A>

   @return
      The next element in the iteration.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString next() throws Exception
      {
      XString  xstr = null;
      /*.
      ==========================================================================================
      If there's something on the pushback queue, it's the next thing in the iteration.
      ------------------------------------------------------------------------------------------ */
      if (iPushBackQueue.size() > 0)
         {
         xstr = iPushBackQueue.next();
         }
      /*.
      ==========================================================================================
      Otherwise, do a real next
      ------------------------------------------------------------------------------------------ */
      else
         {
         xstr = new XString(iIter.next());
         }
      /*.
      ==========================================================================================
      If we're not being case sensitive, return an NCString
      ------------------------------------------------------------------------------------------ */
      if ( ! iCaseSensitive)
         {
         xstr = new NCString(xstr);
         }
      return  xstr;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method puts an XString into the iteration as the next XString that will be returned by the
   iterator if the iterator is reading from a TextFile. It's used to support a look ahead capability by
   putting an XString that the iterator served up through next() back to be served up again on the next
   call to next(). It is also a mechanism for inserting XStrings that would not otherwise be there into
   the XString stream being emitted by the iterator.


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

   @param
      pStr is the object whose XString representation will be placed into the iteration stream.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1> void inject(Type1 pStr)
      {
//      /*.
//      ==========================================================================================
//      for now, we don't support injecting unless we're iterating over a file
//      ------------------------------------------------------------------------------------------ */
//      if ( ! Util.className(iIter).equals("cosmicabyss.com.utl.TextReaderIterator"))
//         {
//         throw new UnsupportedOperationException();
//         }
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      XString  str = XString.toXString(pStr);
      iPushBackQueue.add(str);
      }



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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void remove() throws UnsupportedOperationException
      {
      iIter.remove();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method resets the iterator to the beginning of whatever it was iterating over.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void reset() throws Exception
      {
      /*.
      ==========================================================================================
      clean up and free the current iterator.
      ------------------------------------------------------------------------------------------ */
      this.cleanup();
      this.iIter = null;
      /*.
      ==========================================================================================
      free and recreate the pushback queue
      ------------------------------------------------------------------------------------------ */
      this.iPushBackQueue = new Queue<XString>();
      /*.
      ==========================================================================================
      recreate the iterator
      ------------------------------------------------------------------------------------------ */
      if (this.iFile != null)
         {
         this.iIter = new TextReaderIterator(this.iFile);
         }
      else if (this.iInputStream != null)
         {
         this.iIter = new TextReaderIterator(this.iInputStream);
         }
      else if (this.iCollection != null)
         {
         this.iIter = this.iCollection.iterator();
         }
      else if (this.iFileName != null)
         {
         this.iIter = new TextReaderIterator(this.iFileName);
         }
      }



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

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

   @return
      A reference to this object

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

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



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



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



   /*.
   ==========================================================================================
   Class Constants
      CLASS_NAME:
         The name of this class
      DFLT_LINE_LEN:
         The default line length for word wrapping
   ------------------------------------------------------------------------------------------ */
   private static final XString  CLASS_NAME    = new XString(XStringsIterator.class.getName());
   private static final int      DFLT_LINE_LEN = ConsoleMessage.defaultLineLength();
   /*.
   ==========================================================================================
   Class variables
      cOut : console output.
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();
   /*.
   ==========================================================================================
   Instance variables
   ------------------------------------------------------------------------------------------ */
   private Queue<XString>  iPushBackQueue  = new Queue<XString>();
   private boolean         iCaseSensitive  = true;
   private Iterator        iIter           = null;
   private XString         iFileName       = null;
   private Collection      iCollection     = null;
   private InputStream     iInputStream    = null;
   private File            iFile           = null;



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

   <P><B>Implementation: </B><A HREF="XStringsIterator.java.html#017">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
      ------------------------------------------------------------------------------------------ */
      XStringsIterator  obj = new XStringsIterator("c:/temp/test.txt");
      /*.
      ==========================================================================================
      Test code
      ------------------------------------------------------------------------------------------ */
      obj.test();
      /*.
      ==========================================================================================
      We're going to create XStringIterators over a TextFile, an ArrayList, and a HashSet.
      ------------------------------------------------------------------------------------------ */
      TextFile            file  = new TextFile("c:/temp/test.txt");
      ArrayList<XString>  array = new ArrayList<XString>();
      HashSet<XString>    set   = new HashSet<XString>();
      /*.
      ==========================================================================================
      We'll use the same XStringIterator variable to store the XStringIterator for each of them.
      ------------------------------------------------------------------------------------------ */
      XStringsIterator  iter;
      /*.
      ==========================================================================================
      First, iterate over the lines in the TextFile. Use this iteration to populate the
      ArrayList and the HashSet.
      ------------------------------------------------------------------------------------------ */
      iter = new XStringsIterator(file);
      while (iter.hasNext())
         {
         XString  str = iter.next();
         System.out.println(str);
         array.add(str);
         set.add(str);
         }
      /*.
      ==========================================================================================
      Now iterate over the ArrayList
      ------------------------------------------------------------------------------------------ */
      iter = new XStringsIterator(array);
      while (iter.hasNext())
         {
         XString  str = iter.next();
         System.out.println(str);
         }
      /*.
      ==========================================================================================
      Now iterate over the HashSet
      ------------------------------------------------------------------------------------------ */
      iter = new XStringsIterator(set);
      while (iter.hasNext())
         {
         XString  str = iter.next();
         System.out.println(str);
         }



      }



   }  // class XStringsIterator