public IEnumerable <SearchResult> Search(string search, bool ignoreAccents, bool ignoreCase) { string searchString; if (string.IsNullOrEmpty(search)) { searchString = "%"; } else { searchString = "%" + search.Replace("%", "%%") + "%"; } var srs = new List <SearchResult>(); Metrics.Measure("Search for " + search, delegate { using (var reader = SelectReader( @"TYPES Integer, String, Integer; SELECT PhraseID, Phrase, GetMatch(?, ?, Phrase, ?) AS i FROM Phrases WHERE Section = ? AND i != -1" , ignoreCase ? 1 : 0, ignoreAccents ? 1 : 0, search, section)) { while (reader.Read()) { var sr = GetSearchResult(search, reader.GetString(1), reader.GetInt32(0), (MatchType)reader.GetInt32(2)); if (sr != null) { srs.Add(sr); } } } }); return(srs); }
public void BackgroundSaveWorker() { try { Metrics.Measure("Saving dictionary: " + System.IO.Path.GetFileName(Path), delegate { if (Path != null) { try { string tempFile = P.GetTempFileName(); string backupFile = P.GetTempFileName(); // Write the new version to a temporary file, first. If that fails, the actual dictionary file // is not modified. Write(tempFile); // Try to make a backup of the file. If that isn't possible for some reason, just // delete it, since we've written the replacement file. try { CloseFile(); DestroyCache(); File.Move(Path, backupFile); } catch (IOException) { File.Delete(Path); } catch (UnauthorizedAccessException) { File.Delete(Path); } // Overwrite the dictionary file with the new version. File.Move(tempFile, Path); // Try to open the file again, so that other people can't modify it. // If it fails, we can try again later. try { OpenFile(true); } catch (IOException) { } catch (UnauthorizedAccessException) { } File.Delete(backupFile); SaveCache(); } catch (IOException ex) { throw new DictionarySaveException(ex.Message, ex); } catch (UnauthorizedAccessException ex) { throw new DictionarySaveException(ex.Message, ex); } catch (ArgumentException ex) { throw new DictionarySaveException(ex.Message, ex); } } else { throw new InvalidOperationException("Can't save a dictionary that doesn't have a Path"); } }); } catch (DictionarySaveException e) { ProgramLog.Default.AddMessage(LogType.Error, "{0}", e.Message); } catch (InvalidOperationException e) { ProgramLog.Default.AddMessage(LogType.Error, "{0}", e.Message); } }
private void PreloadPartialEntries() { Metrics.Measure(string.Format("Preload loading entries of {0} before writing", this.Name), delegate { foreach (var e in forwards) { e.FullyLoad(); } foreach (var e in backwards) { e.FullyLoad(); } }); }
private void InitTable() { Metrics.Measure("Initializing accent removal table", delegate { bmp = new char[65536]; isAccent = new bool[65536]; for (int i = 0; i < 65536; ++i) { var c = (char)i; var category = char.GetUnicodeCategory(c); if (category == UnicodeCategory.NonSpacingMark) { isAccent[i] = true; continue; } if (category == UnicodeCategory.Surrogate) { continue; } // Normalize() throws exceptions on some characters because they aren't valid on their own. try { string denorm = new string(c, 1).Normalize(NormalizationForm.FormD); foreach (char x in denorm) { if (char.GetUnicodeCategory(x) != UnicodeCategory.NonSpacingMark) { bmp[i] = x; break; } } } catch (ArgumentException) { // This character isn't a valid code point on its own } } }); Debug.Assert(bmp != null); Debug.Assert(isAccent != null); }
// Saves the dictionary cache, on a separate thread. void SaveCache() { string path = CachePath(); string name = Name ?? string.Empty, author = Author ?? string.Empty, url = Url ?? string.Empty, firstLanguage = FirstLanguage ?? string.Empty, firstLanguageCode = FirstLanguageCode ?? string.Empty, secondLanguage = SecondLanguage ?? string.Empty, secondLanguageCode = SecondLanguageCode ?? string.Empty; // We have to save a copy of the list of entries in case it gets modified while the thread is running. var forwardsEntries = new List <Entry>(forwards.HeadWords); bool haveTags = true; foreach (var entry in forwards) { forwardsEntries.Add(entry.Clone()); if (entry.Tag.Data == null) { haveTags = false; } } var backwardsEntries = new List <Entry>(backwards.HeadWords); foreach (var entry in backwards) { backwardsEntries.Add(entry.Clone()); if (entry.Tag.Data == null) { haveTags = false; } } if (!haveTags) { // Can't write the dictionary cache because there aren't any entry tags. return; } new Thread(new ThreadStart(delegate { Metrics.Measure(string.Format("Saving dictionary cache for {0}", this.Name), delegate { try { using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None)) { using (var writer = new BinaryWriter(stream)) { writer.Write(CacheFormatVersion); writer.Write(this.revisionID ?? ""); foreach (var str in new[] { name, author, url, firstLanguage, firstLanguageCode, secondLanguage, secondLanguageCode }) { writer.Write(str); } foreach (var section in new[] { forwardsEntries, backwardsEntries }) { writer.Write(section.Count); foreach (var entry in section) { writer.Write(entry.Phrase); writer.Write((long)entry.Tag.Data); } } } } } catch (IOException e) { // If we can't save the cache, it's not a huge problem. ProgramLog.Default.AddMessage(LogType.Error, "Can't save cache for dictionary {0}: {1}", name, e.Message); } }); })).Start(); }
/// <summary> /// Writes all entries to a file. There's no way to tell if the dictionary entries have been modified, /// as of yet, so currently we must write the file whether it's necessary or not. /// </summary> /// <param name="path">The file name (relative or absolute) to write to.</param> protected void Write(string path) { // Update the revision ID. Instead of incrementing a number, pick a random GUID and use that. // If incrementing were used, then if a dictionary were copied to a different computer, modified, // and copied back to the original computer, where the dictionary had also been modified, the // cache revision ID on the original computer would still match that of the dictionary file, despite // being invalid. revisionID = Guid.NewGuid().ToString(); // If we're not writing cautiously, we can't read the entries on-demand while the temporary output file // is written. if (path == Path) { CloseFile(); } PreloadPartialEntries(); Metrics.Measure(string.Format("Writing dictionary {0} to file", this.Name), delegate { // Now dispose of the reader so that we can unlock the file. // The file needs to be truncated anyway, so it's not possible to share the file stream // with the Line Reader. if (reader != null) { reader.Dispose(); reader = null; } using (var stream = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None)) { using (var writer = new LineWriter(stream)) { writer.WriteLine(magicNumber); if (Name != null) { writer.WriteLine("name " + Uri.EscapeDataString(Name)); } if (Author != null) { writer.WriteLine("author " + Uri.EscapeDataString(Author)); } if (Url != null) { writer.WriteLine("url " + Uri.EscapeDataString(Url)); } writer.WriteLine("revision-id " + Uri.EscapeDataString(revisionID)); // TODO: WritePairedProperty writer.WriteLine(string.Format("headwords-hint {0} {1}", forwards.HeadWords, backwards.HeadWords)); if (FirstLanguage != null || SecondLanguage != null) { writer.WriteLine(string.Format("languages {0} {1}", Uri.EscapeDataString(FirstLanguage), Uri.EscapeDataString(SecondLanguage))); } if (FirstLanguageCode != null || SecondLanguageCode != null) { writer.WriteLine(string.Format("language-codes {0} {1}", Uri.EscapeDataString(FirstLanguageCode), Uri.EscapeDataString(SecondLanguageCode))); } writer.WriteLine("sorted"); foreach (Entry entry in ForwardsSection) { WriteEntry(writer, "f ", entry); } foreach (Entry entry in ReverseSection) { WriteEntry(writer, "b ", entry); } } } }); }