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



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



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



/*::
======================================================================================================================== *//**
Instances of this class read a file in this format:
   <BLOCKQUOTE>
      <PRE id="unindent">
         [string1]
         key1=value1
         key2=value2
         [string2]
         key1=value1
         key2=value2
         key3=value3
      </PRE>
   </BLOCKQUOTE>

And create a TreeMap in which each node is keyed with an XString that is enclosed within [ ]'s in the file. The value
for the key is the data that is between the line of the file with the key and the next line of the file containing
another key.<P>

Each node in the TreeMap is a HashMap made up of the key/value pairs that appear following the TreeMap key.<P>

Blank lines and comments in the file are ignored. Comments are any line that starts with the XString "//".<P>

For example, if a file named "sequence.txt" contained these lines:
   <BLOCKQUOTE>
      <PRE id="unindent">
         [0000.00.00]
         other   = None

         [1955.10.25]
         city    = Roswell, NM
         address = First Street

         [1959.07.01]
         city    = Roswell, NM
         address = 19 Wildy Dr.

         [1965.07.01]
         city    = North Las Vegas, NV
         address = East Piper

         // this is a commment and it is ignored
      </PRE>
   </BLOCKQUOTE>

Then this class would create a TreeMap containing the following keys:
   <BLOCKQUOTE>
      <PRE id="unindent">
         "0000.00.00"
         "1955.10.25"
         "1959.07.01"
         "1965.07.01"
      </PRE>
   </BLOCKQUOTE>

And the value for the node with the key of "1959.07.01" would be a HashMap containing the following key/value pairs:
   <BLOCKQUOTE>
      <PRE id="unindent">
         key          value
         ---------    --------------
         "city"       "Roswell, NM"
         "address"    "19 Wildy Dr."
      </PRE>
   </BLOCKQUOTE>

<P>
   <DL>
      <DT>
         <B>
            Example usage:
         </B>
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  TreeMapOfHashMaps tm = new TreeMapOfHashMaps("sequence.txt");
                  System.out.println(tm.get("1967.03.03","city"));
                  System.out.println(tm.get("1967.03.03","address"));
                  System.out.println(tm.get("2003.02.16","other"));
                  System.out.println(tm.get("1978.08.16","address"));
                  System.out.println(tm.get("1978.08.16","other"));
                  System.out.println(tm.get("1973.07.13","city"));
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="TreeMapOfHashMaps.java.html">
               TreeMapOfHashMaps.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class TreeMapOfHashMaps extends TreeMap<XString,HashMap>
   {



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

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

   @param
      pFileName is the file to read.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> TreeMapOfHashMaps(Type pFileName) throws Exception
      {
      TextFile                  file    = new TextFile(pFileName);
      XStringsIterator          iter    = file.xstringsIterator();
      HashMap<XString,XString>  map     = null;
      int                       lineNum = 0;
      /*.
      ==========================================================================================
      Read each line of the file
      ------------------------------------------------------------------------------------------ */
      while (iter.hasNext())
         {
         /*.
         ==========================================================================================
         Get the next line from the file and trim the whitespace from both ends of it
         ------------------------------------------------------------------------------------------ */
         XString  line = iter.next().trim();
         lineNum++;
         /*.
         ==========================================================================================
         If it's an empty line skip it
         ------------------------------------------------------------------------------------------ */
         if (line.equals("")) continue;
         /*.
         ==========================================================================================
         If it's a comment line skip it
         ------------------------------------------------------------------------------------------ */
         if (line.startsWith("//")) continue;
         /*.
         ==========================================================================================
         If it's a line in this format "[@@@]" create a new node in the TreeMap that is keyed with
         the "@@@" that is between the "[]". The value of the new node is a new HashMap.
         ------------------------------------------------------------------------------------------ */
         if (line.startsWith("[") && line.endsWith("]"))
            {
            ArrayList<XString>  tokens = line.tokenizedString("[]");
            if (tokens.size()==1)
               {
               map = new HashMap<XString,XString>();
               this.put((XString)tokens.get(0),map);
               }
            }
         /*.
         ==========================================================================================
         Otherwise, it should be a line in this format: "key=value".
         ------------------------------------------------------------------------------------------ */
         else
            {
            /*.
            ==========================================================================================
            If there is an active HashMap ...
            ------------------------------------------------------------------------------------------ */
            if (map != null)
               {
               /*.
               ==========================================================================================
               The line must be in this format "key=value"
               ------------------------------------------------------------------------------------------ */
               ArrayList<XString>  tokens = line.tokenizedString("=");
               if (tokens.size() < 2)
                  {
                  throw new Exception("Invalid TreeMapOfHashMaps file format on line " + lineNum);
                  }
               /*.
               ==========================================================================================
               The key is everything up to the first "="
               ------------------------------------------------------------------------------------------ */
               XString  key = (XString)tokens.get(0);
               /*.
               ==========================================================================================
               The value is everything after the first "="
               ------------------------------------------------------------------------------------------ */
               XString  value = new XString("");
               for (int i=1; i<tokens.size(); i++)
                  {
                  value = value.concat((XString)tokens.get(i));
                  }
               /*.
               ==========================================================================================
               Trim the leading and trailing whitespace from the key and value
               ------------------------------------------------------------------------------------------ */
               key   = key.trim();
               value = value.trim();
               /*.
               ==========================================================================================
               Put the key value pair into the current HashMap
               ------------------------------------------------------------------------------------------ */
               map.put(key,value);
               }
            }
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a XString from the TreeMapofHashMaps identified by the pair of keys -- pKey and
   pHashMapKey.<P>

   The returned XString is the one from the HashMap associated with the largest key that is less than
   or equal to pKey.<P>

   For example, if the TreeMap contained these key value pairs:
      <BLOCKQUOTE>
         <PRE id="unindent">
            [0000.00.00]
            other   = None

            [1955.10.25]
            city    = Roswell, NM
            address = First Street

            [1959.07.01]
            city    = Roswell, NM
            address = 19 Wildy Dr.

            [1965.07.01]
            city    = North Las Vegas, NV
            address = East Piper
         </PRE>
      </BLOCKQUOTE>

   And this method was called with this syntax:
      <BLOCKQUOTE>
         <PRE id="unindent">
            Get("1958.07.19","address");
         </PRE>
      </BLOCKQUOTE>

   Then this method would return the XString
      <BLOCKQUOTE>
         <PRE id="unindent">
            "First Street"
         </PRE>
      </BLOCKQUOTE>

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

   @return
      The the value associated with pHashMapKey from the HashMap associated with pKey
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> XString get(Type1 pKey, Type2 pHashMapKey)
      {
      XString   aKey        = new XString(pKey);
      XString   aHashMapKey = new XString(pHashMapKey);
      Set       set  = this.keySet();
      Iterator  iter = set.iterator();
      XString   key  = null;
      while (iter.hasNext())
         {
         XString  newKey     = (XString)iter.next();
         int      comparison = newKey.compareTo(aKey);
         if (comparison > 0) break;
         key = newKey;
         }
      if (key == null) return null;
      HashMap  map = (HashMap)this.get(key);
      if (map == null)
         {
         return null;
         }
      return (XString)map.get(aHashMapKey);
      }



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



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



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



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



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method allows the TreeMapOfHashMaps class file to be unit tested as a stand-alone 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.TreeMapOfHashMaps
            </DD>
         </DT>
      </DL>

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

   @param
      pArgs contains the command line arguments with which the class was invoked as an application.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static void main(String[] pArgs) throws Exception
      {
      TreeMapOfHashMaps tm = new TreeMapOfHashMaps("C:/adb/Protected/Personal/HISTORY/!sequence.txt");
      System.out.println(tm.get("1967.03.03","city"));
      System.out.println(tm.get("1967.03.03","address"));
      System.out.println(tm.get("2003.02.16","other"));
      System.out.println(tm.get("1978.08.16","address"));
      System.out.println(tm.get("1978.08.16","other"));
      System.out.println(tm.get("1973.07.13","city"));
      }



   }  // class TreeMapOfHashMaps