Logo Search packages:      
Sourcecode: galaxium version File versions  Download package

IndexedConversationLogArchive.cs

/*
 * Galaxium Messenger
 * Copyright (C) 2008 Ben Motmans <ben.motmans@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.IO;
using System.Text;
using System.Collections.Generic;

using Galaxium.Core;
using Anculus.Core;

namespace Galaxium.Protocol
{
      internal sealed class IndexedConversationLogArchive : IComparable<IndexedConversationLogArchive>
      {
            private IndexedConversationLog _log;
            
            private Stream _stream;
            private BinaryReader _reader;
            private BinaryWriter _writer;

            private string _absoluteFilename;
            private string _filename;
            private int _archiveIndex;
            
            private int _messageCount;
            private int[] _indices = new int[] { 0, 0, 0, 0, 0, 0 };
            private List<int> _indexQueue;
            
            private const int _flagMessage      = 0x01;
            private const int _flagEvent  = 0x02;
            private const int _flagSource = 0x10;
            
            private object _sync = new object ();
            
            internal IndexedConversationLogArchive (IndexedConversationLog log, BinaryReader reader)
            {
                  _log = log;
                  _indexQueue = new List<int> (50);
                  
                  ReadArchiveMetaData (reader);
                  
                  _absoluteFilename = Path.Combine (log.Directory, _filename);
            }
            
            internal IndexedConversationLogArchive (IndexedConversationLog log, string absoluteFilename, string filename, int archiveIndex)
            {
                  _log = log;
                  _absoluteFilename = absoluteFilename;
                  _filename = filename;
                  _archiveIndex = archiveIndex;
                  _indexQueue = new List<int> (50);
                  
                  _absoluteFilename = Path.Combine (log.Directory, filename);
            }
            
            internal string Filename
            {
                  get { return _filename; }
            }
            
            internal string AbsoluteFilename
            {
                  get { return _absoluteFilename; }
            }
            
            internal int ArchiveIndex
            {
                  get { return _archiveIndex; }
                  set { _archiveIndex = value; }
            }
            
            internal int MessageCount
            {
                  get { return _messageCount; }
            }
            
            internal int[] Indices
            {
                  get { return _indices; }
            }
            
            public int CompareTo (IndexedConversationLogArchive other)
            {
                  return _archiveIndex.CompareTo (other._archiveIndex);
            }
            
            internal void Close ()
            {
                  lock (_sync) {
                        ReleaseStream (true);
                  }
            }
            
            internal void LogMessage (ITextMessage msg)
            {
                  lock (_sync) {
                        RequestWriter ();
                        
                        bool hasSource = msg.Source != null;
                        int flags = _flagMessage | (hasSource ? _flagSource : 0);

                        string text = msg.GetText ();

                        int len = 16; //4 (len) + 8 (time) + 4 (flags)
                        
                        len += GetTotalStringSize (msg.Source.UniqueIdentifier);
                        len += GetTotalStringSize (msg.Source.DisplayName);
                        len += GetTotalStringSize (text);

                        _writer.Write (len);
                        _writer.Write (msg.TimeStamp.Ticks);
                        _writer.Write (flags);

                        if (hasSource) {
                              _writer.Write (msg.Source.UniqueIdentifier);
                              _writer.Write (msg.Source.DisplayName);
                        }
                        _writer.Write (text);
                        
                        UpdateIndices (len);
                        ReleaseWriter (false);
                  }
            }

            internal void LogEvent (DateTime timestamp, string description)
            {
                  lock (_sync) {
                        RequestWriter ();
                        
                        int len = 16; //4 (len) + 8 (time) + 4 (flags)
                        len += GetTotalStringSize (description);
                        
                        _writer.Write (len);
                        _writer.Write (timestamp.Ticks);
                        _writer.Write (_flagEvent);
                        
                        _writer.Write (description);
                        
                        UpdateIndices (len);
                        ReleaseWriter (false);
                  }
            }
            
            private void UpdateIndices (int diff)
            {
                  _messageCount++;

                  bool pop = false;
                  if (_messageCount > 50) {
                        _indices[0] += _indexQueue[0];
                        pop = true;
                  }
                  
                  if (_messageCount > 40)
                        _indices[1] += _indexQueue[_indexQueue.Count - 40];
                  
                  if (_messageCount > 30)
                        _indices[2] += _indexQueue[_indexQueue.Count - 30];
                  
                  if (_messageCount > 20)
                        _indices[3] += _indexQueue[_indexQueue.Count - 20];
                  
                  if (_messageCount > 10)
                        _indices[4] += _indexQueue[_indexQueue.Count - 10];
                  
                  if (_messageCount > 5)
                        _indices[5] += _indexQueue[_indexQueue.Count - 5];
                  
                  if (pop)
                        _indexQueue.RemoveAt (0);
                  _indexQueue.Add (diff);

                  _log.MetaData.WriteIndex ();
            }
            
            internal IEnumerable<ConversationLogEntry> GetNLastEntries (int n)
            {
                  lock (_sync) {
                        RequestReader ();

                        int remaining = 0;
                        SeekToNLastEntry (n, out remaining);
                        if (remaining > n)
                              SkipNEntries (remaining - n);
                        
                        List<ConversationLogEntry> entries = new List<ConversationLogEntry> ();
                        
                        ConversationLogEntry entry = ReadConversationLogEntry ();
                        while (entry != null) {
                              entries.Add (entry);
                              
                              entry = ReadConversationLogEntry ();
                        }
                        
                        ReleaseReader (false);
                        
                        return entries;
                  }
            }

            internal IEnumerable<ConversationLogEntry> SearchAll (string keyword)
            {
                  lock (_sync) {
                        RequestReader ();
                        _stream.Seek (0, SeekOrigin.Begin);
                  
                        List<ConversationLogEntry> entries = new List<ConversationLogEntry> ();
                        
                        ConversationLogEntry entry = ReadConversationLogEntry ();
                        while (entry != null) {
                              if (entry.Message != null && entry.Message.Contains (keyword))
                                    entries.Add (entry);
                        
                              entry = ReadConversationLogEntry ();
                        }
                  
                        ReleaseReader (false);
                        
                        return entries;
                  }
            }
            
            internal ConversationLogEntry Search (string keyword)
            {
                  lock (_sync) {
                        RequestReader ();
                        _stream.Seek (0, SeekOrigin.Begin);
                  
                        ConversationLogEntry entry = ReadConversationLogEntry ();
                        while (entry != null) {
                              if (entry.Message != null && entry.Message.Contains (keyword))
                                    return entry;
                              
                              entry = ReadConversationLogEntry ();
                        }
                  
                        ReleaseReader (false);
                  }
                  
                  return null;
            }
            
            internal ConversationLogEntry SearchNext (string keyword, ConversationLogEntry entry)
            {
                  lock (_sync) {
                        RequestReader ();
                        _stream.Seek (entry.StreamIndex, SeekOrigin.Begin);
                  
                        ConversationLogEntry next = ReadConversationLogEntry ();
                        while (next != null) {
                              if (next.Message != null && next.Message.Contains (keyword))
                                    return next;
                              
                              next = ReadConversationLogEntry ();
                        }
                  
                        ReleaseReader (false);
                  }
                  
                  return null;
            }
            
            internal void ReadArchiveMetaData (BinaryReader reader)
            {
                  _filename = reader.ReadString ();
                  _archiveIndex = reader.ReadInt32 ();
                  
                  _messageCount = reader.ReadInt32 ();
                  
                  for (int i=0; i < _indices.Length; i++)
                        _indices[i] = reader.ReadInt32 ();
                  
                  int len = _messageCount > 50 ? 50 : _messageCount;
                  for (int i=0; i < len; i++)
                        _indexQueue.Add (reader.ReadInt32 ());
            }
            
            internal void WriteArchiveMetaData (IndexedConversationLogArchive archive, BinaryWriter writer)
            {
                  writer.Write (_filename);
                  writer.Write (_archiveIndex);
                  
                  writer.Write (_messageCount);
                  
                  for (int i=0; i < _indices.Length; i++)
                        writer.Write (_indices[i]);

                  int len = _indexQueue.Count;
                  for (int i=0; i < len; i++)
                        writer.Write (_indexQueue[i]);
            }
            
            internal bool IsMaximumSizeReached ()
            {
                  FileInfo fi = new FileInfo (_absoluteFilename);
                  if (fi.Length >= _log.MetaData.MaximumArchiveSize)
                        return true;
                  return false;
            }
            
            private void SkipNEntries (int n)
            {
                  while (n-- >= 0) {
                        int len = _reader.ReadInt32 ();
                        _stream.Seek (len, SeekOrigin.Current);
                  }
            }
            
            private void SeekToNLastEntry (int n, out int remaining)
            {
                  int index = -1;
                  if (n > 50) {
                        index = -1;
                        remaining = _messageCount;
                  } else if (n > 40) {
                        index = 0;
                        remaining = _messageCount > 50 ? 50 : _messageCount;
                  } else if (n > 30) {
                        index = 1;
                        remaining = _messageCount > 40 ? 40 : _messageCount;
                  } else if (n > 20) {
                        index = 2;
                        remaining = _messageCount > 30 ? 30 : _messageCount;
                  } else if (n > 10) {
                        index = 3;
                        remaining = _messageCount > 20 ? 20 : _messageCount;
                  } else if (n > 5) {
                        index = 4;
                        remaining = _messageCount > 10 ? 10 : _messageCount;
                  } else {
                        index = 5;
                        remaining = _messageCount > 5 ? 5 : _messageCount;
                  }
                  
                  if (index >= 0) {
                        int position = _indices[index];
                        _stream.Seek (position, SeekOrigin.Begin);
                  } else {
                        _stream.Seek (0, SeekOrigin.Begin);
                  }
            }
            
            private void RequestStream ()
            {
                  if (_stream == null)
                        _stream = new FileStream (_absoluteFilename, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            }
            
            private void ReleaseStream (bool force)
            {
                  if (!_log.KeepAlive || force) {
                        if (_stream != null) {
                              _stream.Close ();
                              _stream.Dispose ();
                              _stream = null;
                        }
                  }
            }
            
            private void RequestReader ()
            {
                  if (_writer != null) //make sure the writer doesn't lock the stream
                        ReleaseWriter (true);
                  
                  if (_reader == null) {
                        RequestStream ();

                        _reader = new BinaryReader (_stream, Encoding.UTF8);
                  }
            }
            
            private void ReleaseReader (bool force)
            {
                  if (!_log.KeepAlive || force) {
                        if (_reader != null)
                              _reader.Close ();
                        else if (_stream != null)
                              ReleaseStream (true);
                        
                        _reader = null;
                        _stream = null; //the stream is closed automatically
                  }
            }
            
            private void RequestWriter ()
            {
                  if (_reader != null) //make sure the reader doesn't lock the stream
                        ReleaseReader (true);
                  
                  if (_writer == null) {
                        RequestStream ();
                        _stream.Seek (_stream.Length, SeekOrigin.Begin);
                  
                        _writer = new BinaryWriter (_stream, Encoding.UTF8);
                  }
            }
            
            private void ReleaseWriter (bool force)
            {
                  if (!_log.KeepAlive || force) {
                        if (_writer != null)
                              _writer.Close ();
                        else if (_stream != null)
                              ReleaseStream (true);
                        
                        _writer = null;
                        _stream = null; //the stream is closed automatically
                  } else {
                        _writer.Flush ();
                  }
            }
            
            private ConversationLogEntry ReadConversationLogEntry ()
            {
                  if (_stream.Position >= _stream.Length)
                        return null;
                  
                  int index = (int)_stream.Position;
                  _reader.ReadInt32 (); //the length of the entry
                  DateTime timestamp = new DateTime (_reader.ReadInt64 ());
                  int flags = _reader.ReadInt32 ();

                  if ((flags & _flagMessage) > 0) {
                        string uid = null;
                        string displayName = null;
                        
                        if ((flags & _flagSource) > 0) {
                              uid = _reader.ReadString ();
                              displayName = _reader.ReadString ();
                        }
                        string msg = _reader.ReadString ();
                        return new ConversationLogEntry (_log, _archiveIndex, index, timestamp, uid, displayName, msg);
                  } else if ((flags & _flagEvent) > 0) {
                        string msg = _reader.ReadString ();
                        return new ConversationLogEntry (_log, _archiveIndex, index, timestamp, msg);
                  } else {
                        Log.Error ("Incorrect log file format.");
                        return null;
                  }
            }
            
            private static int GetTotalStringSize (string str)
            {
                  int len = Encoding.UTF8.GetBytes (str).Length;
                  int value = len;
                  int prefix = 0;
                  
                  do {
                        int high = (value >> 7) & 0x01ffffff;
                        byte b = (byte)(value & 0x7f);

                        if (high != 0) {
                              b = (byte)(b | 0x80);
                        }

                        prefix++;
                        value = high;
                  } while (value != 0);
                  
                  return len + prefix;
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index