namespace JWLMerge.BackupFileServices.Models.Database { using System; using System.Collections.Generic; using System.Linq; using Exceptions; public class Database { private readonly Dictionary _bookmarkSlots = new Dictionary(); private Lazy> _notesGuidIndex; private Lazy> _notesIdIndex; private Lazy> _userMarksGuidIndex; private Lazy> _userMarksIdIndex; private Lazy> _locationsIdIndex; private Lazy> _locationsValueIndex; private Lazy> _tagsNameIndex; private Lazy> _tagsIdIndex; private Lazy> _tagMapIndex; private Lazy> _blockRangeUserMarkIdIndex; private Lazy> _bookmarksIndex; public Database() { ReinitializeIndexes(); } public void InitBlank() { LastModified = new LastModified(); Locations = new List(); Notes = new List(); Tags = new List(); TagMaps = new List(); BlockRanges = new List(); Bookmarks = new List(); UserMarks = new List(); } public void CheckValidity() { ReinitializeIndexes(); CheckBlockRangeValidity(); CheckBookmarkValidity(); CheckNoteValidity(); CheckTagMapValidity(); CheckUserMarkValidity(); } public LastModified LastModified { get; set; } public List Locations { get; set; } public List Notes { get; set; } public List Tags { get; set; } public List TagMaps { get; set; } public List BlockRanges { get; set; } public List Bookmarks { get; set; } public List UserMarks { get; set; } public Note FindNote(string guid) { return _notesGuidIndex.Value.TryGetValue(guid, out var note) ? note : null; } public Note FindNote(int noteId) { return _notesIdIndex.Value.TryGetValue(noteId, out var note) ? note : null; } public UserMark FindUserMark(string guid) { return _userMarksGuidIndex.Value.TryGetValue(guid, out var userMark) ? userMark : null; } public UserMark FindUserMark(int userMarkId) { return _userMarksIdIndex.Value.TryGetValue(userMarkId, out var userMark) ? userMark : null; } public Tag FindTag(string tagName) { return _tagsNameIndex.Value.TryGetValue(tagName, out var tag) ? tag : null; } public Tag FindTag(int tagId) { return _tagsIdIndex.Value.TryGetValue(tagId, out var tag) ? tag : null; } public TagMap FindTagMap(int tagId, int noteId) { return _tagMapIndex.Value.TryGetValue(GetTagMapKey(tagId, noteId), out var tag) ? tag : null; } public Location FindLocation(int locationId) { return _locationsIdIndex.Value.TryGetValue(locationId, out var location) ? location : null; } public Location FindLocationByValues(Location locationValues) { var key = GetLocationByValueKey(locationValues); return _locationsValueIndex.Value.TryGetValue(key, out var location) ? location : null; } public BlockRange FindBlockRange(int userMarkId) { // note that we find a block range by userMarkId. The BlockRange.UserMarkId column // isn't marked as a unique index, but we assume it should be. return _blockRangeUserMarkIdIndex.Value.TryGetValue(userMarkId, out var range) ? range : null; } public Bookmark FindBookmark(int locationId, int publicationLocationId) { string key = GetBookmarkKey(locationId, publicationLocationId); return _bookmarksIndex.Value.TryGetValue(key, out var bookmark) ? bookmark : null; } public int GetNextBookmarkSlot(int publicationLocationId) { // there are only 10 slots available for each publication... if (_bookmarkSlots.TryGetValue(publicationLocationId, out var slot)) { ++slot; } _bookmarkSlots[publicationLocationId] = slot; return slot; } private Dictionary NoteIndexValueFactory() { return Notes.ToDictionary(note => note.Guid); } private Dictionary NoteIdIndexValueFactory() { return Notes.ToDictionary(note => note.NoteId); } private Dictionary UserMarkIndexValueFactory() { return UserMarks.ToDictionary(userMark => userMark.UserMarkGuid); } private Dictionary UserMarkIdIndexValueFactory() { return UserMarks.ToDictionary(userMark => userMark.UserMarkId); } private Dictionary LocationsIndexValueFactory() { return Locations.ToDictionary(location => location.LocationId); } private Dictionary LocationsByValueIndexValueFactory() { var result = new Dictionary(); foreach (var location in Locations) { var key = GetLocationByValueKey(location); if (!result.ContainsKey(key)) { result.Add(key, location); } } return result; } private Dictionary BlockRangeIndexValueFactory() { return BlockRanges.ToDictionary(range => range.UserMarkId); } private string GetBookmarkKey(int locationId, int publicationLocationId) { return $"{locationId}-{publicationLocationId}"; } private string GetLocationByValueKey(Location location) { return $"{location.KeySymbol}|{location.IssueTagNumber}|{location.MepsLanguage}|{location.Type}|{location.BookNumber ?? -1}|{location.ChapterNumber ?? -1}|{location.DocumentId ?? -1}|{location.Track ?? -1}"; } private Dictionary BookmarkIndexValueFactory() { var result = new Dictionary(); foreach (var bookmark in Bookmarks) { string key = GetBookmarkKey(bookmark.LocationId, bookmark.PublicationLocationId); if (!result.ContainsKey(key)) { result.Add(key, bookmark); } } return result; } private Dictionary TagIndexValueFactory() { return Tags.ToDictionary(tag => tag.Name); } private Dictionary TagIdIndexValueFactory() { return Tags.ToDictionary(tag => tag.TagId); } private string GetTagMapKey(int tagId, int noteId) { return $"{tagId}-{noteId}"; } private Dictionary TagMapIndexValueFactory() { var result = new Dictionary(); foreach (var tagMap in TagMaps) { string key = GetTagMapKey(tagMap.TagId, tagMap.TypeId); result.Add(key, tagMap); } return result; } private void CheckUserMarkValidity() { foreach (var userMark in UserMarks) { if (FindLocation(userMark.LocationId) == null) { throw new BackupFileServicesException($"Could not find location for user mark {userMark.UserMarkId}"); } } } private void CheckTagMapValidity() { foreach (var tagMap in TagMaps) { if (tagMap.Type == 1) { if (FindTag(tagMap.TagId) == null) { throw new BackupFileServicesException($"Could not find tag for tag map {tagMap.TagMapId}"); } if (FindNote(tagMap.TypeId) == null) { throw new BackupFileServicesException($"Could not find note for tag map {tagMap.TagMapId}"); } } } } private void CheckNoteValidity() { foreach (var note in Notes) { if (note.UserMarkId != null && FindUserMark(note.UserMarkId.Value) == null) { throw new BackupFileServicesException($"Could not find user mark Id for note {note.NoteId}"); } if (note.LocationId != null && FindLocation(note.LocationId.Value) == null) { throw new BackupFileServicesException($"Could not find location for note {note.NoteId}"); } } } private void CheckBookmarkValidity() { foreach (var bookmark in Bookmarks) { if (FindLocation(bookmark.LocationId) == null || FindLocation(bookmark.PublicationLocationId) == null) { throw new BackupFileServicesException($"Could not find location for bookmark {bookmark.BookmarkId}"); } } } private void CheckBlockRangeValidity() { foreach (var range in BlockRanges) { if (FindUserMark(range.UserMarkId) == null) { throw new BackupFileServicesException($"Could not find user mark Id for block range {range.BlockRangeId}"); } } } private void ReinitializeIndexes() { _notesGuidIndex = new Lazy>(NoteIndexValueFactory); _notesIdIndex = new Lazy>(NoteIdIndexValueFactory); _userMarksGuidIndex = new Lazy>(UserMarkIndexValueFactory); _userMarksIdIndex = new Lazy>(UserMarkIdIndexValueFactory); _locationsIdIndex = new Lazy>(LocationsIndexValueFactory); _locationsValueIndex = new Lazy>(LocationsByValueIndexValueFactory); _tagsNameIndex = new Lazy>(TagIndexValueFactory); _tagsIdIndex = new Lazy>(TagIdIndexValueFactory); _tagMapIndex = new Lazy>(TagMapIndexValueFactory); _blockRangeUserMarkIdIndex = new Lazy>(BlockRangeIndexValueFactory); _bookmarksIndex = new Lazy>(BookmarkIndexValueFactory); } } }