using System;
using System.Collections.Generic;
using System.Text;
namespace ScrewTurn.Wiki.SearchEngine {
///
/// Implements a base class for an index storer.
///
/// Instance and static members are thread-safe.
public abstract class IndexStorerBase : IDisposable {
///
/// Indicates whether the object was disposed.
///
protected bool disposed = false;
///
/// The index bound to this storer.
///
protected IInMemoryIndex index = null;
///
/// true if the index data seems corrupted.
///
protected bool dataCorrupted = false;
///
/// Contains the exception occurred during index setup.
///
protected Exception reasonForDataCorruption = null;
///
/// The event handler for the of the bound index.
///
protected EventHandler indexChangedHandler;
///
/// Initializes a new instance of the class.
///
/// The index to manage.
/// If is null.
public IndexStorerBase(IInMemoryIndex index) {
if(index == null) throw new ArgumentNullException("index");
this.index = index;
indexChangedHandler = new EventHandler(IndexChangedHandler);
this.index.IndexChanged += indexChangedHandler;
}
///
/// Gets the index.
///
public IInMemoryIndex Index {
get {
lock(this) {
return index;
}
}
}
///
/// Loads the index from the data store the first time.
///
/// The dumped documents.
/// The dumped words.
/// The dumped word mappings.
protected abstract void LoadIndexInternal(out DumpedDocument[] documents, out DumpedWord[] words, out DumpedWordMapping[] mappings);
///
/// Gets the approximate size, in bytes, of the search engine index.
///
public abstract long Size { get; }
///
/// Gets a value indicating whether the index data seems corrupted and cannot be used.
///
public bool DataCorrupted {
get {
lock(this) {
return dataCorrupted;
}
}
}
///
/// Gets the exception that caused data corruption.
///
public Exception ReasonForDataCorruption {
get { return reasonForDataCorruption; }
}
///
/// Loads the index from the data store the first time.
///
public void LoadIndex() {
lock(this) {
DumpedDocument[] documents = null;
DumpedWord[] words = null;
DumpedWordMapping[] mappings = null;
dataCorrupted = false;
try {
LoadIndexInternal(out documents, out words, out mappings);
}
catch(Exception ex) {
reasonForDataCorruption = ex;
dataCorrupted = true;
}
if(!dataCorrupted) {
try {
index.InitializeData(documents, words, mappings);
}
catch(Exception ex) {
reasonForDataCorruption = ex;
dataCorrupted = true;
}
}
}
}
///
/// Handles the events.
///
/// The sender.
/// The event arguments.
protected void IndexChangedHandler(object sender, IndexChangedEventArgs e) {
lock(this) {
if(disposed) return;
switch(e.Change) {
case IndexChangeType.IndexCleared:
InitDataStore(e.State);
break;
case IndexChangeType.DocumentAdded:
if(!dataCorrupted) {
IndexStorerResult result = SaveData(e.ChangeData, e.State);
e.Result = result;
}
break;
case IndexChangeType.DocumentRemoved:
if(!dataCorrupted) DeleteData(e.ChangeData, e.State);
break;
default:
throw new NotSupportedException("Invalid Change Type");
}
}
}
///
/// Initializes the data storage.
///
/// A state object passed from the index.
protected abstract void InitDataStore(object state);
///
/// Deletes data from the data storage.
///
/// The data to delete.
/// A state object passed from the index.
protected abstract void DeleteData(DumpedChange data, object state);
///
/// Stores new data into the data storage.
///
/// The data to store.
/// A state object passed by the index.
/// The storer result, if any.
/// When saving a new document, the document ID in data.Mappings must be
/// replaced with the currect document ID, generated by the concrete implementation of
/// this method. data.Words should have IDs numbered from uint.MaxValue downwards.
/// The method re-numbers the words appropriately.
protected abstract IndexStorerResult SaveData(DumpedChange data, object state);
///
/// Disposes the current object.
///
public void Dispose() {
lock(this) {
if(!disposed) {
disposed = true;
index.IndexChanged -= indexChangedHandler;
}
}
}
///
/// Determines whether a is contained in a list.
///
/// The mapping.
/// The list.
/// true if the mapping is contained in the list, false otherwise.
protected static bool Find(DumpedWordMapping mapping, IEnumerable list) {
foreach(DumpedWordMapping m in list) {
if(m.WordID == mapping.WordID &&
m.DocumentID == mapping.DocumentID &&
m.FirstCharIndex == mapping.FirstCharIndex &&
m.WordIndex == mapping.WordIndex &&
m.Location == mapping.Location) return true;
}
return false;
}
///
/// Determines whether a is contained in a list.
///
/// The word.
/// The list.
/// true if the word is contained in the list, false orherwise.
protected static bool Find(DumpedWord word, IEnumerable list) {
foreach(DumpedWord w in list) {
if(w.ID == word.ID && w.Text == word.Text) return true;
}
return false;
}
}
}