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; } } }