*** empty log message ***
[m17n/m17n-lib-cs.git] / MDatabase.cs
index e8ca8e5..01d5755 100644 (file)
@@ -2,6 +2,8 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
+using System.Xml;
+
 using M17N;
 using M17N.Core;
 
@@ -48,11 +50,13 @@ namespace M17N.Core
       int len = pattern.Length;
       int i;
     
+      M17n.DebugPrint ("Listing {0} in {1} ...", pattern, dirinfo);
       for (i = 0; i < len && pattern[i] != sep; i++);
       try {
        if (i == len)
          {
            FileInfo[] listing = dirinfo.GetFiles (pattern);
+           i = listing.Length;
            foreach (FileInfo elt in listing)
              files.Add (elt);
          }
@@ -61,11 +65,14 @@ namespace M17N.Core
            string tail = pattern.Substring (i + 1);
            pattern = pattern.Substring (0, i);
            DirectoryInfo[] listing = dirinfo.GetDirectories (pattern);
+
+           i = listing.Length;
            foreach (DirectoryInfo elt in listing)
              list (ref files, elt, tail);
          }
       } catch {
       }
+      M17n.DebugPrint (" found {0} files\n", i);
     }
   }
 
@@ -137,7 +144,7 @@ namespace M17N.Core
     }
   }
 
-  public class MDatabase : IComparable<MDatabase>
+  public partial class MDatabase : IComparable<MDatabase>
   {
     /// Identifier of a MDatabase.
     public struct Tag : IEquatable<Tag>
@@ -233,7 +240,7 @@ namespace M17N.Core
       }
     }
 
-    public delegate object Loader (Tag tag, object extra_info);
+    public delegate MPlist Loader (Tag tag, object extra_info);
 
     internal class MDatabaseInfo
     {
@@ -270,9 +277,10 @@ namespace M17N.Core
 
       public MDatabaseInfo (MPlist plist)
       {
-       Format = MSymbol.plist;
+       Format = MSymbol.nil;
        if (plist.IsMText)
          {
+           Format = MSymbol.plist;
            Filename = plist.Text.ToString ();
            plist = plist.Next;
          }
@@ -281,22 +289,22 @@ namespace M17N.Core
            MPlist p = plist.Plist;
 
            if (p.IsMText)
-             Filename = p.Text.ToString ();
-           p = p.Next;
-           if (! p.IsEmpty)
              {
-               if (p.IsSymbol)
-                 Format = p.Symbol;
+               Filename = p.Text.ToString ();
+               p = p.Next;
+             }
+           if (p.IsSymbol)
+             {
+               Format = p.Symbol;
                p = p.Next;
-               if (! p.IsEmpty)
-                 {
-                   if (p.IsSymbol)
-                     Schema = p.Symbol;
-                   p = p.Next;
-                   if (p.IsMText)
-                     SchemaFile = p.Text.ToString ();
-                 }                 
              }
+           if (p.IsSymbol)
+             {
+               Schema = p.Symbol;
+               p = p.Next;
+             }
+           if (p.IsMText)
+             SchemaFile = p.Text.ToString ();
            plist = plist.Next;
          }
 
@@ -361,10 +369,13 @@ namespace M17N.Core
 
     private static DateTime LastUpdateTime = new DateTime (0);
 
-    private static readonly MSymbol Mversion = MSymbol.Of ("version");
-    private static readonly MSymbol Mwildcard = MSymbol.Of ("*");
-    private static readonly MSymbol Mchar_table = MSymbol.Of ("char-table");
-    private static readonly MSymbol Mcharset = MSymbol.Of ("charset");
+    private static readonly MSymbol Mversion = "version";
+    private static readonly MSymbol Mwildcard = "*";
+    private static readonly MSymbol Mchar_table = "char-table";
+    private static readonly MSymbol Mcharset = "charset";
+    private static readonly MSymbol Mxml = "xml";
+
+    private static TimeSpan CheckInterval = new TimeSpan (50000000);
 
     /// Type of database
     private enum MDBType
@@ -401,7 +412,7 @@ namespace M17N.Core
        // current system, or the validation was failed.
        DISABLED,
        // The database is deleted by the modificaiton of mdb.dir, or
-       // is overwritten by a new explicit definition..
+       // is overwritten by a new explicit definition.
        INVALID,
       };
 
@@ -417,10 +428,22 @@ namespace M17N.Core
     private MDBType DBType;
     private MDBStatus DBStatus;
     internal MDatabaseInfo Info;
-    // File in which the database contents is stored.
+    // File in which the database contents is stored.  This is null
+    // when DBStatus is NOT_READY.
     internal FileInfo FileInfo;
-    // When the database file is checked (or validated).
-    internal DateTime CheckedTime;
+    // When the database file is loaded last.
+    internal DateTime LastLoaded = DateTime.Now;
+
+    public enum LoadStatus
+    {
+      None,
+      InvalidLoadMethod,
+      NotAvailable,
+      NotReadable,
+      InvalidContents,
+    };
+
+    public LoadStatus LastLoadStatus = LoadStatus.None;
 
     static MDatabase ()
     {
@@ -441,19 +464,23 @@ namespace M17N.Core
       }
       DBDirs[2] = new MDatabaseDir (null);
       DBDirs[3] = new MDatabaseDir (Path.Combine (share_dir, "m17n"));
-      update_all ();
+      update_all (true);
     }
 
     public static string ApplicationDir
     {
       get { return (DBDirs[1].Dirname); }
-      set { DBDirs[2] = new MDatabaseDir (value); update_all (); }
+      set { DBDirs[2] = new MDatabaseDir (value); update_all (true); }
     }
 
-    private static bool update_all ()
+    // Update all listing and directories.  Return true iff some are
+    // really updated.
+    private static bool update_all (bool force)
     {
-      bool updated = false;
+      if (! force && DateTime.Now - LastUpdateTime < CheckInterval)
+       return false;
 
+      bool updated = false;
       for (int i = 1; i < 4; i++)
        if (DBDirs[i].Dirname != null)
          {
@@ -469,13 +496,14 @@ namespace M17N.Core
                updated = true;
              }
          }
-      LastUpdateTime = DateTime.Now;
+      if (updated)
+       LastUpdateTime = DateTime.Now;
       return updated;
     }
 
     public static void Dump ()
     {
-      update_all ();
+      update_all (false);
       Console.WriteLine ("[DBDirs]");
       for (int i = 1; i < 4; i++)
        if (DBDirs[i].Dirname != null)
@@ -497,7 +525,7 @@ namespace M17N.Core
        foreach (MDatabase mdb in kv.Value)
          Console.WriteLine (mdb);
 
-      Console.WriteLine ("[NDITCT]");
+      Console.WriteLine ("[NDICT]");
       foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
        foreach (MDatabase mdb in kv.Value)
          Console.WriteLine (mdb);
@@ -542,10 +570,6 @@ namespace M17N.Core
            mdb = mdbs[i];
            if (mdb.ListIndex == list_idx && mdb.DirIndex >= dir_idx)
              {
-               if (mdb.DBStatus == MDBStatus.INVALID)
-                 M17n.DebugPrint ("registering: {0}\n", mdb);
-               else
-                 M17n.DebugPrint ("updating: {0}\n", mdb);
                mdb.DirIndex = dir_idx;
                if (dict == wdict)
                  {
@@ -563,6 +587,10 @@ namespace M17N.Core
                    mdb.DBStatus = MDBStatus.READY;
                  }
                mdb.Info = info;
+               if (mdb.DBStatus == MDBStatus.INVALID)
+                 M17n.DebugPrint ("registering: {0}\n", mdb);
+               else
+                 M17n.DebugPrint ("updating: {0}\n", mdb);
                return;
              }
          }
@@ -616,14 +644,16 @@ namespace M17N.Core
       this.tag = tag;
       this.Info = info;
       DBType = this.tag.HasWildcard ? MDBType.WILDCARD : MDBType.AUTO;
-      if (this.tag[0] == Mchar_table || this.tag[0] == Mcharset)
-       Info.Format = this.tag[0];
+      if (tag[0] == Mchar_table || tag[0] == Mcharset)
+       Info.Format = tag[0];
       ListIndex = list_idx;
       DirIndex = dir_idx;
       if (Path.IsPathRooted (Info.Filename))
        DBStatus = MDBStatus.READY;
       else
        DBStatus = MDBStatus.NOT_READY;
+      if (Info.Format == Mchar_table)
+       MCharProp.Define (tag[2], this);
     }
 
     public override String ToString () {
@@ -665,8 +695,7 @@ namespace M17N.Core
       MPlist plist = null;
       using (FileStream stream = File.OpenRead (dblist.FullName))
        {
-         MStreamReader reader = new MStreamReader (stream);
-         plist = new MPlist (reader);
+         plist = new MPlist (stream);
        }
       if (plist == null)
        return;
@@ -708,39 +737,91 @@ namespace M17N.Core
            if (mdb.DBStatus == MDBStatus.READY)
              {
                M17n.DebugPrint ("re-expanding: {0}\n", mdb);
-               register_files (DBDirs[dir_idx].Dirname, mdb.ListIndex,
-                               dir_idx, mdb.Info);
+               register_files (DBDirs[dir_idx].Dirname, dir_idx, mdb);
              }
     }
 
-    private static void register_files (string dir, int list_idx, int dir_idx,
-                                       MDatabaseInfo base_info)
+    private static bool parse_plist_header (FileInfo fileinfo, MDatabase mdb,
+                                           out Tag tag, out MDatabaseInfo info)
     {
+      MPlist plist = null;
+
+      tag = new Tag (MSymbol.nil);
+      info = null;
+      using (FileStream stream = fileinfo.OpenRead ())
+       {
+         try { plist = new MPlist (stream, 1); } catch { }
+       }
+      if (plist == null || ! plist.IsPlist)
+       return false;
+      plist = plist.Plist;
+      tag = new Tag (ref plist);
+      if (tag.HasWildcard || ! tag.Match (mdb.tag))
+       return false;
+      info = new MDatabaseInfo (plist);
+      return true;
+    }
+
+    private static bool parse_xml_header (FileInfo fileinfo, MDatabase mdb,
+                                         out Tag tag, out MDatabaseInfo info)
+    {
+      tag = new Tag (MSymbol.nil);
+      info = null;
+      using (FileStream stream = fileinfo.OpenRead ())
+       {
+         try {
+           MPlist plist = new MPlist ();
+           XmlTextReader reader = new XmlTextReader (stream);
+           
+           reader.WhitespaceHandling = WhitespaceHandling.None;
+           do {
+             reader.Read ();
+           } while (reader.NodeType != XmlNodeType.Element);
+           plist.Add (MSymbol.symbol, (MSymbol) reader.Name);
+           reader.Read ();
+           if (reader.NodeType == XmlNodeType.Element && reader.Name == "tags")
+             {
+               reader.Read ();
+               while (reader.NodeType == XmlNodeType.Element)
+                 {
+                   reader.Read ();
+                   plist.Add (MSymbol.symbol, (MSymbol) reader.Value);
+                   reader.Read ();
+                   reader.Read ();
+                 }
+               tag = new Tag (ref plist);
+               if (tag.HasWildcard || ! tag.Match (mdb.tag))
+                 return false;
+               info = new MDatabaseInfo (plist);
+               return true;
+             }
+         } catch (Exception e) {
+           Console.WriteLine ("error {0}", e);
+         }
+       }
+      return false;
+    }
+
+    private static void register_files (string dir, int dir_idx, MDatabase mdb)
+    {
+      int list_idx = mdb.ListIndex;
       List<FileInfo> files = new List<FileInfo> ();
-      MGlob.FileList (ref files, dir, base_info.Filename);
+      MGlob.FileList (ref files, dir, mdb.Info.Filename);
       foreach (FileInfo fileinfo in files)
        {
-         MPlist plist = null;
-         using (FileStream stream = fileinfo.OpenRead ())
-           {
-             MStreamReader reader = new MStreamReader (stream);
-             plist = new MPlist (reader, 1);
-           }
-         if (plist != null && plist.IsPlist)
-           {
-             plist = plist.Plist;
-             Tag tag = new Tag (ref plist);
+         Tag tag;
+         MDatabaseInfo info;
 
-             if (! tag.HasWildcard && tag.Match (tag))
-               {
-                 MDatabaseInfo info = new MDatabaseInfo (plist);
-                 info.Merge (base_info);
-                 if (Path.IsPathRooted (base_info.Filename))
-                   info.Filename = fileinfo.FullName;
-                 else
-                   info.Filename = fileinfo.Name;
-                 register (list_idx, dir_idx, tag, info);
-               }
+         if (mdb.Info.Format == MSymbol.plist
+             ? parse_plist_header (fileinfo, mdb, out tag, out info)
+             : parse_xml_header (fileinfo, mdb, out tag, out info))
+           {
+             info.Merge (mdb.Info);
+             if (Path.IsPathRooted (mdb.Info.Filename))
+               info.Filename = fileinfo.FullName;
+             else
+               info.Filename = fileinfo.Name;
+             register (list_idx, dir_idx, tag, info);
            }
        }
     }
@@ -750,11 +831,11 @@ namespace M17N.Core
       M17n.DebugPrint ("expanding: {0}\n", this);
 
       if (DirIndex == 0)
-       register_files (null, ListIndex, DirIndex, Info);
+       register_files (null, DirIndex, this);
       else
        for (int i = 1; i < 4; i++)
          if (DBDirs[i].DirInfo != null)
-           register_files (DBDirs[i].DirInfo.FullName, ListIndex, i, Info);
+           register_files (DBDirs[i].DirInfo.FullName, i, this);
       DBStatus = MDBStatus.READY;
     }
 
@@ -774,30 +855,40 @@ namespace M17N.Core
        }
     }
 
+    // Update the status.  Return true iff the database file is
+    // readable but changed.
+
     private bool update_status ()
     {
       if (DBType == MDBType.UNKNOWN)
        return true;
+      update_all (false);
       if (DBStatus == MDBStatus.INVALID)
        return false;
-      if (DBStatus == MDBStatus.READY)
+      if (DBStatus != MDBStatus.NOT_READY)
        {
-         if (DirIndex > 0
-             || File.Exists (FileInfo.FullName))
-           return true;
-         DBStatus = MDBStatus.INVALID;
-         return false;
-       }
+         try {
+           FileInfo.Refresh ();
+         } catch {
+           DBStatus = MDBStatus.INVALID;
+           return false;
+         }
+         if (LastLoaded >= FileInfo.LastWriteTime)
+           return false;
+         DBStatus = MDBStatus.READY;
+         return true;
+       }         
       for (int i = 1; i < 4; i++)
-       if (DBDirs[i] != null)
+       if (DBDirs[i] != null && DBDirs[i].Dirname != null)
          {
            string filename = Path.Combine (DBDirs[i].Dirname, Info.Filename);
            if (File.Exists (filename))
              {
                FileInfo = new FileInfo (filename);
                DirIndex = i;
+               DBStatus = MDBStatus.READY;
                return true;
-           }
+             }
          }
       return false;
     }
@@ -822,14 +913,18 @@ namespace M17N.Core
                break;
            }
        }
-      if (! update_all () && mdb != null)
+      if (! update_all (false) && mdb != null)
        return mdb;
       maybe_expand_wildcard (tag);
       if (! ndict.TryGetValue (tag, out mdbs))
        return null;
       for (int i = 0; i < mdbs.Count; i++)
-       if ((mdb = mdbs[i]).update_status ())
-         return mdb;
+       {
+         mdb = mdbs[i];
+         mdb.update_status ();
+         if (mdb.DBStatus == MDBStatus.READY)
+           return mdb;
+       }
       return null;
     }
 
@@ -837,7 +932,7 @@ namespace M17N.Core
     {
       List<MDatabase> list = new List<MDatabase> ();
 
-      update_all ();
+      update_all (false);
       maybe_expand_wildcard (tag);
 
       if (tag.HasWildcard)
@@ -845,44 +940,287 @@ namespace M17N.Core
          foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
            if (kv.Key.Match (tag))
              foreach (MDatabase mdb in kv.Value)
-               if (mdb.update_status ())
-                 {
-                   list.Add (mdb);
-                   break;
-                 }
+               {
+                 mdb.update_status ();
+                 if (mdb.DBStatus == MDBStatus.READY)
+                   {
+                     list.Add (mdb);
+                     break;
+                   }
+               }
        }
       else
        {
          List<MDatabase> mdbs;
          if (ndict.TryGetValue (tag, out mdbs))
            foreach (MDatabase mdb in mdbs)
-             if (mdb.update_status ())
-               {
-                 list.Add (mdb);
-                 break;
-               }
+             {
+               mdb.update_status ();
+               if (mdb.DBStatus == MDBStatus.READY)
+                 {
+                   list.Add (mdb);
+                   break;
+                 }
+             }
        }
       return list;
     }
 
-    public object Load ()
+    private FileStream get_stream ()
     {
-      return (loader != null ? loader (tag, ExtraInfo)
-             : load (MSymbol.nil, MSymbol.nil));
+      if (DBStatus != MDBStatus.READY)
+       {
+         LastLoadStatus = LoadStatus.NotAvailable;
+         return null;
+       }
+
+      FileStream stream = null;
+      try {
+       stream = FileInfo.OpenRead ();
+      } catch {
+       LastLoadStatus = LoadStatus.NotReadable;          
+      }
+      return stream;
     }
 
-    public object Load (MSymbol key, MSymbol stop)
+    public MPlist Load ()
     {
       if (loader != null)
+       return loader (tag, ExtraInfo);
+      if (Info.Format != MSymbol.plist)
+       throw new Exception ("Not a plist database");
+
+      FileStream stream = get_stream ();
+      if (stream == null)
        return null;
-      return load (key, stop);
+      MPlist plist = null;
+      try {
+       plist = new MPlist (stream);
+       LastLoaded = DateTime.Now;
+      } catch {
+       LastLoadStatus = LoadStatus.InvalidContents;
+      } finally {
+       stream.Dispose ();
+      }
+      return plist;
     }
 
-    private object load (MSymbol key, MSymbol stop)
+    public MPlist Load (MSymbol key, MSymbol stop)
     {
-      return null;
+      if (loader != null)
+       throw new Exception ("Partial load is impossible");
+      if (Info.Format != MSymbol.plist)
+       throw new Exception ("Not a plist database");
+
+      FileStream stream = get_stream ();
+      if (stream == null)
+       return null;
+      try {
+       MPlist plist = new MPlist (stream, key, stop);
+       LastLoaded = DateTime.Now;
+       return plist;
+      } catch {
+       LastLoadStatus = LoadStatus.InvalidContents;
+       return null;
+      } finally {
+       stream.Dispose ();
+      }
+    }
+
+    public MPlist Load (MSymbol stop)
+    {
+      if (loader != null)
+       throw new Exception ("Partial load is impossible");
+      if (Info.Format != MSymbol.plist)
+       throw new Exception ("Not a plist database");
+
+      FileStream stream = get_stream ();
+      if (stream == null)
+       return null;
+      try {
+       MPlist plist = new MPlist (stream, stop);
+       LastLoaded = DateTime.Now;
+       return plist;
+      } catch (Exception e) {
+       Console.WriteLine (e);
+       LastLoadStatus = LoadStatus.InvalidContents;
+       return null;
+      } finally {
+       stream.Dispose ();
+      }
+    }
+
+    private XmlTextReader get_reader (XmlDocument doc)
+    {
+      XmlTextReader reader;
+
+      try {
+       if (doc.NameTable != null)
+         reader = new XmlTextReader (FileInfo.FullName, doc.NameTable);
+       else
+         reader = new XmlTextReader (FileInfo.FullName);
+       reader.WhitespaceHandling = WhitespaceHandling.None;
+      } catch {
+       LastLoadStatus = LoadStatus.NotReadable;
+       reader = null;
+      }
+      return reader;
+    }
+
+    public bool Load (XmlDocument doc)
+    {
+      if (Info.Format != Mxml)
+       throw new Exception ("Not an XML database");
+      XmlTextReader reader = get_reader (doc);
+      if (reader == null)
+       return false;
+      try {
+       doc.Load (reader);
+       LastLoaded = DateTime.Now;          
+       return true;
+      } catch (Exception e) {
+       Console.WriteLine (e);
+       LastLoadStatus = LoadStatus.InvalidContents;
+       return false;
+      } finally {
+       reader.Close ();
+      }
+    }
+
+    public bool Load (XmlDocument doc, MSymbol key, MSymbol stop)
+    {
+      if (Info.Format != Mxml)
+       throw new Exception ("Not an XML database");
+      XmlTextReader reader = get_reader (doc);
+      if (reader == null)
+       return false;
+      try {
+       reader.Read ();
+       while (reader.NodeType != XmlNodeType.Element)
+         reader.Read ();
+       doc.LoadXml ("<" + reader.Name + "></" + reader.Name + ">");
+       reader.Read ();
+       XmlNode node = doc.DocumentElement;
+       while (reader.NodeType == XmlNodeType.Element
+              ? reader.Name != stop.Name
+              : reader.NodeType != XmlNodeType.EndElement)
+         if (reader.NodeType == XmlNodeType.Element
+             && reader.Name == key.Name)
+           node = doc.DocumentElement.InsertAfter (doc.ReadNode (reader),
+                                                   node);
+       return true;
+      } catch (Exception e) {
+       Console.WriteLine (e);
+       return false;
+      } finally {
+       reader.Close ();
+      }
     }
 
+    public bool Load (XmlDocument doc, MSymbol stop)
+    {
+      if (Info.Format != Mxml)
+       throw new Exception ("Not an XML database");
+      XmlTextReader reader = get_reader (doc);
+      if (reader == null)
+       return false;
+      try {
+       reader.Read ();
+       while (reader.NodeType != XmlNodeType.Element)
+         reader.Read ();
+       doc.LoadXml ("<" + reader.Name + "></" + reader.Name + ">");
+       reader.Read ();
+       XmlNode node = doc.DocumentElement;
+       while (reader.NodeType == XmlNodeType.Element
+              ? reader.Name != stop.Name
+              : reader.NodeType != XmlNodeType.EndElement)
+         node = doc.DocumentElement.InsertAfter (doc.ReadNode (reader), node);
+       return true;
+      } catch (Exception e) {
+       Console.WriteLine (e);
+       return false;
+      } finally {
+       reader.Close ();
+      }
+    }
+
+    public XmlNode Load (XmlDocument doc, string id, params string[] nodes)
+    {
+      if (Info.Format != Mxml)
+       throw new Exception ("Not an XML database");
+      XmlTextReader reader = get_reader (doc);
+      if (reader == null)
+       return null;
+      try {
+       int len = nodes.Length;
+       do {
+         reader.Read ();
+       } while (reader.NodeType != XmlNodeType.Element);
+       if (reader.Name != nodes[0])
+         return null;
+
+       string ns = reader.GetAttribute ("xmlns");
+       XmlNode top = doc.CreateNode (XmlNodeType.Element, nodes[0], ns);
+       XmlNode node = top;
+       int i;
+
+       for (i = 1; i + 1 < len; i++)
+         {
+           if (! reader.ReadToDescendant (nodes[i]))
+             return null;
+           node = node.InsertAfter (doc.CreateNode (XmlNodeType.Element,
+                                                    nodes[i], ns), null);
+         }
+       if (! reader.ReadToDescendant (nodes[i]))
+         return null;
+       XmlNode ref_node = null;
+       while (reader.NodeType != XmlNodeType.EndElement)
+         {
+           if (reader.NodeType == XmlNodeType.Element)
+             {
+               if (reader.Name == nodes[i]
+                   && (id == null || id == reader.GetAttribute ("id")))
+                 ref_node = node.InsertAfter (doc.ReadNode (reader), ref_node);
+               else
+                 reader.Skip ();
+             }
+           else
+             reader.Read ();
+         }
+       return top;
+      } catch (Exception e) {
+       Console.WriteLine (e);
+       return null;
+      } finally {
+       reader.Close ();
+      }
+    }
+
+    /// <summary>Return a list of currently available database
+    /// directory names</summary>.
+    public static string[] DirectoryList ()
+    {
+      List<string> dirs = new List<string> ();
+
+      for (int i = 1; i < 4; i++)
+       if (DBDirs[i].Dirname != null)
+         dirs.Add (DBDirs[i].Dirname);
+      return dirs.ToArray ();
+    }
+
+    public MSymbol Format { get { return Info.Format; } }
+
+    public static bool Changed (DateTime time)
+    {
+      update_all (false);
+      return (time < LastUpdateTime);
+    }
+
+    public bool NeedReload ()
+    {
+      return update_status ();
+    }  
+
     // For IComparable<MDatabase>
     public int CompareTo (MDatabase other)
     {
@@ -891,4 +1229,4 @@ namespace M17N.Core
              : ListIndex - other.ListIndex);
     }
   }
-}
\ No newline at end of file
+}