context: /* --------------------------------------------------------------------------
   * Copyrights
   *
   * Portions created by or assigned to Cursive Systems, Inc. are
   * Copyright (c) 2002-2008 Cursive Systems, Inc.  All Rights Reserved.  Contact
   * information for Cursive Systems, Inc. is available at
   * http://www.cursive.net/.
   *
   * License
   *
   * Jabber-Net is licensed under the LGPL.
   * See LICENSE.txt for details.
   * --------------------------------------------------------------------------*/
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.IO;
  using System.Xml;
  using Jabber.Protocol;
  
  namespace Jabber.Connection
  {
      /// <summary>
      /// A dictionary backed into a file.  Any modification to the dictionary re-writes the file, so 
      /// writes are somewhat costly.  Reads are cached lazily.
      /// </summary>
      public class FileMap<T>
          where T : Element
      {
          private const string NS = "http://cursive.net/xml/FileMap";
  
          private string m_fileName = null;
          private ElementFactory m_factory = null;
          private Dictionary<string, T> m_cache = null;
  
          /// <summary>
          /// Create a file map.
          /// </summary>
          /// <param name="fileName">Valid file name, either absoulte, or relative 
          /// to the current working directory.</param>
          /// <param name="cacheDurationDays">Duration of the cache</param>
          /// <param name="factory">Element factory to create elements of a given type.  If null,
          /// Elements will always be created, and T MUST be Element.</param>
          public FileMap(string fileName, Int32 cacheDurationDays, ElementFactory factory)
          {
              this.Factory = factory;
              this.CacheDurationDays = cacheDurationDays;
              this.FileName = fileName;
          }
  
          /// <summary>
          /// The ElementFactory that determines the type of the elements being stored.
          /// </summary>
          public ElementFactory Factory
          {
              get { return m_factory; }
              set
              {
                  if (value == null)
                  {
                      if (!typeof(T).Equals(typeof(Element)))
                          throw new ArgumentException("Factory must not be null if type is not XmlElement", "factory");
                      m_factory = new ElementFactory();
                  }
                  else
                      m_factory = value;
              }
          }
  
          /// <summary>
          /// The name of the file to manage.
          /// </summary>
          public string FileName
          {
              get { return m_fileName; }
              set 
              {
                  if (value == null)
                      throw new ArgumentNullException("fileName");
                  if (value == "")
                      throw new ArgumentOutOfRangeException("fileName");
  
                  lock (this)
                  {
                      if (value == null)
                          throw new ArgumentNullException("FileName");
                      if (m_fileName == value)
                          return;
                      m_fileName = value;
                      Read();
                  }
              }
          }
  
          public DateTime? CreationDate
          {
              get
              {
                  XmlElement fm = null;
  
                  try
                  {
                      XmlDocument doc = new XmlDocument();
                      doc.Load(m_fileName);
                      fm = doc.DocumentElement;
                  }
                  catch (Exception ex)
                  {
                      Debug.WriteLine("WARNING: " + ex.ToString());
                  }
  
                  DateTime date = DateTime.MinValue;
                  Boolean parsed = false;
  
                  if (fm != null && fm.HasAttribute("creationDate"))
                      parsed = DateTime.TryParse(fm.GetAttribute("creationDate"), out date);
  
                  return !parsed || date == DateTime.MinValue ? null : (DateTime?)date;
              }
          }
  
          public Int32 CacheDurationDays { get; set; }
  
          private void Flush()
          {
              Debug.Assert(m_cache != null, "m_cache should be initialized in constructor");
  
              XmlDocument doc = new XmlDocument();
              XmlElement fm = doc.CreateElement("fm", NS);
  
              if (!this.CreationDate.HasValue)
                  fm.SetAttribute("creationDate", DateTime.Now.ToShortDateString());
              else
                  fm.SetAttribute("creationDate", this.CreationDate.Value.ToShortDateString());
  
              doc.AppendChild(fm);
  
              XmlElement val;
              lock (this)
              {
                  foreach (KeyValuePair<string, T> kv in m_cache)
                  {
                      val = doc.CreateElement("val", NS);
                      val.SetAttribute("name", kv.Key);
                      if (kv.Value != null)
                          val.AppendChild(doc.ImportNode(kv.Value, true));
                      fm.AppendChild(val);
                  }
              }
              XmlWriter xw = new XmlTextWriter(m_fileName, System.Text.Encoding.UTF8);
              doc.WriteTo(xw);
              xw.Close();
          }
  
          private void Read()
          {
              lock (this)
              {
                  m_cache = new Dictionary<string, T>();
                  try
                  {
                      if (this.CreationDate.HasValue &&
                          this.CreationDate.Value.AddDays(this.CacheDurationDays) < DateTime.Now)
                      {
                          File.Open(m_fileName, FileMode.Truncate);
                      }
  
                      XmlDocument doc = new XmlDocument();
                      doc.Load(m_fileName);
                      XmlElement fm = doc.DocumentElement;
  
                      foreach (XmlElement val in fm.GetElementsByTagName("val"))
                      {
                          string name = val.GetAttribute("name");
  
                          if (String.IsNullOrEmpty(name))
                              continue;
  
                          foreach (XmlNode child in val.ChildNodes)
                          {
                              if (child is XmlElement)
                              {
                                  m_cache[name] = (T)Element.AddTypes((XmlElement)child, m_factory);
                                  break;
                              }
                          }
                      }
                  }
                  catch (Exception ex)
                  {
                      Debug.WriteLine("WARNING: " + ex.ToString());
                  }
              }
          }
  
          /// <summary>
          /// Remove the specified key and value
          /// </summary>
          /// <param name="key"></param>
          /// <returns></returns>
          public bool Remove(string key)
          {
  
gt:             bool ret = m_cache.Remove(key);