analyzer warnings

This commit is contained in:
AntonyCorbett
2021-05-24 12:24:36 +01:00
parent 7e699af3c3
commit 6787ba7172
87 changed files with 910 additions and 734 deletions

View File

@@ -2,3 +2,21 @@
# S112: General exceptions should never be thrown
dotnet_diagnostic.S112.severity = silent
# U2U1202: Use LINQ Count methods efficiently
dotnet_diagnostic.U2U1202.severity = silent
# RCS1037: Remove trailing white-space.
dotnet_diagnostic.RCS1037.severity = silent
# U2U1108: StringBuilders should be initialized with capacity
dotnet_diagnostic.U2U1108.severity = silent
# U2U1009: Async or iterator methods should avoid state machine generation for early exits (throws or synchronous returns)
dotnet_diagnostic.U2U1009.severity = silent
# U2U1212: Capture intermediate results in lambda expressions
dotnet_diagnostic.U2U1212.severity = silent
# U2U1201: Local collections should be initialized with capacity
dotnet_diagnostic.U2U1201.severity = silent

View File

@@ -7,13 +7,13 @@
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using JWLMerge.BackupFileServices.Events;
using JWLMerge.BackupFileServices.Exceptions;
using JWLMerge.BackupFileServices.Helpers;
using JWLMerge.BackupFileServices.Models;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using JWLMerge.BackupFileServices.Models.ManifestFile;
using JWLMerge.ExcelServices;
using Events;
using Exceptions;
using Helpers;
using Models;
using Models.DatabaseModels;
using Models.ManifestFile;
using ExcelServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
@@ -33,7 +33,7 @@
_merger.ProgressEvent += MergerProgressEvent;
}
public event EventHandler<ProgressEventArgs> ProgressEvent;
public event EventHandler<ProgressEventArgs>? ProgressEvent;
/// <inheritdoc />
public BackupFile Load(string backupFilePath)
@@ -53,18 +53,16 @@
var filename = Path.GetFileName(backupFilePath);
ProgressMessage($"Loading {filename}");
using (var archive = new ZipArchive(File.OpenRead(backupFilePath), ZipArchiveMode.Read))
using var archive = new ZipArchive(File.OpenRead(backupFilePath), ZipArchiveMode.Read);
var manifest = ReadManifest(filename, archive);
var database = ReadDatabase(archive, manifest.UserDataBackup.DatabaseName);
return new BackupFile
{
var manifest = ReadManifest(filename, archive);
var database = ReadDatabase(archive, manifest.UserDataBackup.DatabaseName);
return new BackupFile
{
Manifest = manifest,
Database = database,
};
}
Manifest = manifest,
Database = database,
};
}
catch (UnauthorizedAccessException)
{
@@ -137,7 +135,7 @@
/// <inheritdoc />
public int RemoveNotesByTag(
BackupFile backup,
int[] tagIds,
int[]? tagIds,
bool removeUntaggedNotes,
bool removeAssociatedUnderlining,
bool removeAssociatedTags)
@@ -147,10 +145,7 @@
throw new ArgumentNullException(nameof(backup));
}
if (tagIds == null)
{
tagIds = Array.Empty<int>();
}
tagIds ??= Array.Empty<int>();
var tagIdsHash = tagIds.ToHashSet();
@@ -199,17 +194,14 @@
}
/// <inheritdoc />
public int RemoveUnderliningByColour(BackupFile backup, int[] colorIndexes, bool removeAssociatedNotes)
public int RemoveUnderliningByColour(BackupFile backup, int[]? colorIndexes, bool removeAssociatedNotes)
{
if (backup == null)
{
throw new ArgumentNullException(nameof(backup));
}
if (colorIndexes == null)
{
colorIndexes = Array.Empty<int>();
}
colorIndexes ??= Array.Empty<int>();
var userMarkIdsToRemove = new HashSet<int>();
@@ -229,7 +221,7 @@
BackupFile backup,
int colorIndex,
bool anyColor,
string publicationSymbol,
string? publicationSymbol,
bool anyPublication,
bool removeAssociatedNotes)
{
@@ -277,48 +269,45 @@
}
ProgressMessage("Writing merged database file");
using (var memoryStream = new MemoryStream())
using var memoryStream = new MemoryStream();
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
Log.Logger.Debug("Created ZipArchive");
var tmpDatabaseFileName = ExtractDatabaseToFile(originalJwlibraryFilePathForSchema);
try
{
Log.Logger.Debug("Created ZipArchive");
backup.Manifest.UserDataBackup.Hash = GenerateDatabaseHash(tmpDatabaseFileName);
var tmpDatabaseFileName = ExtractDatabaseToFile(originalJwlibraryFilePathForSchema);
try
var manifestEntry = archive.CreateEntry(ManifestEntryName);
using (var entryStream = manifestEntry.Open())
using (var streamWriter = new StreamWriter(entryStream))
{
backup.Manifest.UserDataBackup.Hash = GenerateDatabaseHash(tmpDatabaseFileName);
var manifestEntry = archive.CreateEntry(ManifestEntryName);
using (var entryStream = manifestEntry.Open())
using (var streamWriter = new StreamWriter(entryStream))
{
streamWriter.Write(
JsonConvert.SerializeObject(
backup.Manifest,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
}));
}
streamWriter.Write(
JsonConvert.SerializeObject(
backup.Manifest,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
}));
}
AddDatabaseEntryToArchive(archive, backup.Database, tmpDatabaseFileName);
}
finally
{
Log.Logger.Debug("Deleting {tmpDatabaseFileName}", tmpDatabaseFileName);
File.Delete(tmpDatabaseFileName);
}
AddDatabaseEntryToArchive(archive, backup.Database, tmpDatabaseFileName);
}
using (var fileStream = new FileStream(newDatabaseFilePath, FileMode.Create))
finally
{
ProgressMessage("Finishing");
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
Log.Logger.Debug("Deleting {tmpDatabaseFileName}", tmpDatabaseFileName);
File.Delete(tmpDatabaseFileName);
}
}
using var fileStream = new FileStream(newDatabaseFilePath, FileMode.Create);
ProgressMessage("Finishing");
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
/// <inheritdoc />
@@ -407,7 +396,7 @@
}
}
int countRemoved = 0;
var countRemoved = 0;
foreach (var userMark in Enumerable.Reverse(database.UserMarks))
{
if (!userMarksToRetain.Contains(userMark.UserMarkId))
@@ -457,7 +446,7 @@
ProgressMessage($"Merging {files.Count} backup files");
int fileNumber = 1;
var fileNumber = 1;
var originals = new List<BackupFile>();
foreach (var file in files)
{
@@ -547,10 +536,9 @@
}
}
notesToWrite.Add(new ExcelServices.Models.BibleNote
notesToWrite.Add(new ExcelServices.Models.BibleNote(
location.BookNumber.Value, BibleBookNames.GetName(location.BookNumber.Value))
{
BookNumber = location.BookNumber.Value,
BookName = BibleBookNames.GetName(location.BookNumber.Value),
ChapterNumber = location.ChapterNumber,
VerseNumber = note.BlockIdentifier,
NoteTitle = note.Title?.Trim(),
@@ -573,13 +561,14 @@
private static string GetTagsAsCsv(ILookup<int?, TagMap> tags, int noteId, Database database)
{
var t = tags[noteId]?.ToArray();
if (t == null || !t.Any())
var t = tags[noteId].ToArray();
if (!t.Any())
{
return string.Empty;
}
return string.Join(", ", t.Select(x => database.FindTag(x.TagId).Name));
var tagNames = t.Select(x => database.FindTag(x.TagId)?.Name).Where(x => !string.IsNullOrEmpty(x));
return string.Join(", ", tagNames);
}
private static bool SupportDatabaseVersion(int version)
@@ -641,7 +630,7 @@
}
private static bool ShouldRemoveUnderlining(
UserMark mark, Database database, int colorIndex, bool anyColor, string publicationSymbol, bool anyPublication)
UserMark mark, Database database, int colorIndex, bool anyColor, string? publicationSymbol, bool anyPublication)
{
if (!anyColor && mark.ColorIndex != colorIndex)
{
@@ -654,7 +643,7 @@
}
var location = database.FindLocation(mark.LocationId);
return location.KeySymbol == publicationSymbol;
return location != null && location.KeySymbol == publicationSymbol;
}
private static int RemoveUnderlining(Database database, HashSet<int> userMarkIdsToRemove, bool removeAssociatedNotes)
@@ -797,20 +786,18 @@
private string ExtractDatabaseToFile(string jwlibraryFile)
{
Log.Logger.Debug("Opening ZipArchive {jwlibraryFile}", jwlibraryFile);
using (var archive = new ZipArchive(File.OpenRead(jwlibraryFile), ZipArchiveMode.Read))
{
var manifest = ReadManifest(Path.GetFileName(jwlibraryFile), archive);
var databaseEntry = archive.Entries.FirstOrDefault(x => x.Name.Equals(manifest.UserDataBackup.DatabaseName, StringComparison.OrdinalIgnoreCase));
using var archive = new ZipArchive(File.OpenRead(jwlibraryFile), ZipArchiveMode.Read);
var manifest = ReadManifest(Path.GetFileName(jwlibraryFile), archive);
var databaseEntry = archive.Entries.FirstOrDefault(x => x.Name.Equals(manifest.UserDataBackup.DatabaseName, StringComparison.OrdinalIgnoreCase));
#pragma warning disable S5445 // Insecure temporary file creation methods should not be used
var tmpFile = Path.GetTempFileName();
var tmpFile = Path.GetTempFileName();
#pragma warning restore S5445 // Insecure temporary file creation methods should not be used
databaseEntry.ExtractToFile(tmpFile, overwrite: true);
databaseEntry.ExtractToFile(tmpFile, overwrite: true);
Log.Logger.Information("Created temp file: {tmpDatabaseFileName}", tmpFile);
return tmpFile;
}
Log.Logger.Information("Created temp file: {tmpDatabaseFileName}", tmpFile);
return tmpFile;
}
private Manifest ReadManifest(string filename, ZipArchive archive)
@@ -822,32 +809,81 @@
{
throw new BackupFileServicesException($"Could not find manifest entry in jwlibrary file: {filename}");
}
using (var stream = new StreamReader(manifestEntry.Open()))
{
var fileContents = stream.ReadToEnd();
Log.Logger.Debug("Parsing manifest");
dynamic data = JObject.Parse(fileContents);
using var stream = new StreamReader(manifestEntry.Open());
var fileContents = stream.ReadToEnd();
Log.Logger.Debug("Parsing manifest");
dynamic data = JObject.Parse(fileContents);
int manifestVersion = data.version ?? 0;
if (!SupportManifestVersion(manifestVersion))
{
throw new WrongManifestVersionException(filename, ManifestVersionSupported, manifestVersion);
}
int manifestVersion = data.version ?? 0;
if (!SupportManifestVersion(manifestVersion))
{
throw new WrongManifestVersionException(filename, ManifestVersionSupported, manifestVersion);
}
int databaseVersion = data.userDataBackup.schemaVersion ?? 0;
if (!SupportDatabaseVersion(databaseVersion))
{
throw new WrongDatabaseVersionException(filename, DatabaseVersionSupported, databaseVersion);
}
int databaseVersion = data.userDataBackup?.schemaVersion ?? 0;
if (!SupportDatabaseVersion(databaseVersion))
{
throw new WrongDatabaseVersionException(filename, DatabaseVersionSupported, databaseVersion);
}
var result = JsonConvert.DeserializeObject<Manifest>(fileContents);
var result = JsonConvert.DeserializeObject<Manifest>(fileContents);
var prettyJson = JsonConvert.SerializeObject(result, Formatting.Indented);
Log.Logger.Debug("Parsed manifest {manifestJson}", prettyJson);
ValidateManifest(filename, result);
return result;
var prettyJson = JsonConvert.SerializeObject(result, Formatting.Indented);
Log.Logger.Debug("Parsed manifest {manifestJson}", prettyJson);
return result!;
}
private static void ValidateManifest(string filename, Manifest? result)
{
if (result == null)
{
throw new BackupFileServicesException($"Could not deserialize manifest entry from jwlibrary file: {filename}");
}
if (string.IsNullOrEmpty(result.Name))
{
throw new BackupFileServicesException($"Could not retrieve manifest name from jwlibrary file: {filename}");
}
if (string.IsNullOrEmpty(result.CreationDate))
{
throw new BackupFileServicesException($"Could not retrieve manifest creation date from jwlibrary file: {filename}");
}
ValidateUserDataBackup(filename, result.UserDataBackup);
}
private static void ValidateUserDataBackup(string filename, UserDataBackup userDataBackup)
{
if (userDataBackup == null)
{
throw new BackupFileServicesException($"Could not retrieve UserDataBackup element from jwlibrary file: {filename}");
}
if (string.IsNullOrEmpty(userDataBackup.DatabaseName))
{
throw new BackupFileServicesException($"DatabaseName element empty in UserDataBackup from jwlibrary file: {filename}");
}
if (string.IsNullOrEmpty(userDataBackup.DeviceName))
{
throw new BackupFileServicesException($"DeviceName element empty in UserDataBackup from jwlibrary file: {filename}");
}
if (string.IsNullOrEmpty(userDataBackup.Hash))
{
throw new BackupFileServicesException($"Hash element empty in UserDataBackup from jwlibrary file: {filename}");
}
if (string.IsNullOrEmpty(userDataBackup.LastModifiedDate))
{
throw new BackupFileServicesException($"LastModifiedDate element empty in UserDataBackup from jwlibrary file: {filename}");
}
}
@@ -862,21 +898,18 @@
{
ProgressMessage("Generating database hash");
using (var fs = new FileStream(databaseFilePath, FileMode.Open))
{
using (var bs = new BufferedStream(fs))
using (var sha1 = new SHA256Managed())
{
byte[] hash = sha1.ComputeHash(bs);
var sb = new StringBuilder(2 * hash.Length);
foreach (byte b in hash)
{
sb.Append($"{b:x2}");
}
using var fs = new FileStream(databaseFilePath, FileMode.Open);
using var bs = new BufferedStream(fs);
using var sha1 = new SHA256Managed();
return sb.ToString();
}
byte[] hash = sha1.ComputeHash(bs);
var sb = new StringBuilder(2 * hash.Length);
foreach (byte b in hash)
{
sb.Append($"{b:x2}");
}
return sb.ToString();
}
private void AddDatabaseEntryToArchive(
@@ -904,7 +937,7 @@
private void OnProgressEvent(string message)
{
OnProgressEvent(new ProgressEventArgs { Message = message });
OnProgressEvent(new ProgressEventArgs(message));
}
private void ProgressMessage(string logMessage)

View File

@@ -4,6 +4,11 @@
public class ProgressEventArgs : EventArgs
{
public string Message { get; set; }
public ProgressEventArgs(string msg)
{
Message = msg;
}
public string Message { get; }
}
}

View File

@@ -4,7 +4,9 @@
using System.Runtime.Serialization;
[Serializable]
#pragma warning disable RCS1194 // Implement exception constructors.
public class WrongDatabaseVersionException : BackupFileServicesException
#pragma warning restore RCS1194 // Implement exception constructors.
{
public WrongDatabaseVersionException(string filename, int expectedVersion, int foundVersion)
: base($"Wrong database version found ({foundVersion}) in {filename}. Expecting {expectedVersion}")
@@ -20,7 +22,7 @@
{
}
public string Filename { get; }
public string? Filename { get; }
public int ExpectedVersion { get; }

View File

@@ -4,7 +4,9 @@
using System.Runtime.Serialization;
[Serializable]
#pragma warning disable RCS1194 // Implement exception constructors.
public class WrongManifestVersionException : BackupFileServicesException
#pragma warning restore RCS1194 // Implement exception constructors.
{
public WrongManifestVersionException(string filename, int expectedVersion, int foundVersion)
: base($"Wrong manifest version found ({foundVersion}) in {filename}. Expecting {expectedVersion}")
@@ -20,7 +22,7 @@
{
}
public string Filename { get; }
public string? Filename { get; }
public int ExpectedVersion { get; }

View File

@@ -4,18 +4,18 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using JWLMerge.BackupFileServices.Models;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using Models;
using Models.DatabaseModels;
public class BibleNotesFile
{
private const int MaxTitleLength = 50;
private const string BibleKeySymbolToken = @"[BibleKeySymbol";
private const string MepsLanguageIdToken = @"[MepsLanguageId";
private const string BibleKeySymbolToken = "[BibleKeySymbol";
private const string MepsLanguageIdToken = "[MepsLanguageId";
private readonly List<BibleNote> _notes = new List<BibleNote>();
private readonly string _path;
private string _bibleKeySymbol;
private string _bibleKeySymbol = null!;
private int _mepsLanguageId;
private bool _initialised;
@@ -61,15 +61,17 @@
ParseNotes(lines);
}
private void RemoveParamLines(string[] lines)
private static void RemoveParamLines(string[] lines)
{
RemoveLineStarting(BibleKeySymbolToken, lines);
RemoveLineStarting(MepsLanguageIdToken, lines);
}
private void RemoveLineStarting(string token, string[] lines)
private static void RemoveLineStarting(string token, string[] lines)
{
for (int n = 0; n < lines.Length; ++n)
#pragma warning disable U2U1015 // Do not index an array multiple times within a loop body
for (var n = 0; n < lines.Length; ++n)
#pragma warning restore U2U1015 // Do not index an array multiple times within a loop body
{
if (lines[n].Trim().StartsWith(token, StringComparison.OrdinalIgnoreCase))
{
@@ -98,7 +100,7 @@
{
var linesInNote = new List<string>();
BibleNotesVerseSpecification currentVerseSpec = null;
BibleNotesVerseSpecification? currentVerseSpec = null;
foreach (var line in lines)
{
@@ -122,7 +124,7 @@
StoreNote(linesInNote, currentVerseSpec);
}
private void StoreNote(IReadOnlyList<string> lines, BibleNotesVerseSpecification currentVerseSpec)
private void StoreNote(IReadOnlyList<string> lines, BibleNotesVerseSpecification? currentVerseSpec)
{
if (currentVerseSpec == null)
{
@@ -142,15 +144,15 @@
currentVerseSpec.ChapterNumber,
currentVerseSpec.VerseNumber),
NoteTitle = titleAndContent.Title.Trim(),
NoteContent = titleAndContent.Content.Trim(),
NoteTitle = titleAndContent.Title?.Trim(),
NoteContent = titleAndContent.Content?.Trim(),
ColourIndex = currentVerseSpec.ColourIndex,
StartTokenInVerse = currentVerseSpec.StartWordIndex,
EndTokenInVerse = currentVerseSpec.EndWordIndex,
});
}
private NoteTitleAndContent ParseTitleAndContent(IReadOnlyList<string> lines)
private static NoteTitleAndContent? ParseTitleAndContent(IReadOnlyList<string> lines)
{
if (lines.Count == 0)
{
@@ -174,11 +176,11 @@
return result;
}
private BibleNotesVerseSpecification GetVerseSpecification(string line)
private static BibleNotesVerseSpecification? GetVerseSpecification(string line)
{
var trimmed = line.Trim();
if (!trimmed.StartsWith("[") || !trimmed.EndsWith("]"))
if (!trimmed.StartsWith("[", StringComparison.Ordinal) || !trimmed.EndsWith("]", StringComparison.Ordinal))
{
return null;
}
@@ -251,7 +253,7 @@
}
}
private string FindValue(string[] lines, string token)
private static string? FindValue(string[] lines, string token)
{
var line = lines.FirstOrDefault(x =>
x.Trim().StartsWith(token, StringComparison.OrdinalIgnoreCase));

View File

@@ -2,13 +2,13 @@
{
using System.Collections.Generic;
using System.Linq;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using Models.DatabaseModels;
using Serilog;
/// <summary>
/// Cleans jwlibrary files by removing redundant or anomalous database rows.
/// </summary>
internal class Cleaner
internal sealed class Cleaner
{
private readonly Database _database;
@@ -114,11 +114,10 @@
{
int removed = 0;
var userMarkIdsFound = new HashSet<int>();
var ranges = _database.BlockRanges;
if (ranges.Any())
{
var userMarkIdsFound = new HashSet<int>();
var userMarkIds = GetUserMarkIdsInUse();
foreach (var range in Enumerable.Reverse(ranges))

View File

@@ -4,14 +4,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using Models.DatabaseModels;
using Serilog;
/// <summary>
/// Isolates all data access to the SQLite database embedded in
/// jwlibrary files.
/// </summary>
internal class DataAccessLayer
internal sealed class DataAccessLayer
{
private readonly string _databaseFilePath;
@@ -27,13 +27,12 @@
public void CreateEmptyClone(string cloneFilePath)
{
Log.Logger.Debug($"Creating empty clone: {cloneFilePath}");
using (var source = CreateConnection(_databaseFilePath))
using (var destination = CreateConnection(cloneFilePath))
{
source.BackupDatabase(destination, "main", "main");
ClearData(destination);
}
using var source = CreateConnection(_databaseFilePath);
using var destination = CreateConnection(cloneFilePath);
source.BackupDatabase(destination, "main", "main");
ClearData(destination);
}
/// <summary>
@@ -62,23 +61,22 @@
{
var result = new Database();
using (var connection = CreateConnection())
{
result.InitBlank();
using var connection = CreateConnection();
result.LastModified.TimeLastModified = ReadAllRows(connection, ReadLastModified)?.FirstOrDefault()?.TimeLastModified;
result.Locations.AddRange(ReadAllRows(connection, ReadLocation));
result.Notes.AddRange(ReadAllRows(connection, ReadNote));
result.Tags.AddRange(ReadAllRows(connection, ReadTag));
result.TagMaps.AddRange(ReadAllRows(connection, ReadTagMap));
result.BlockRanges.AddRange(ReadAllRows(connection, ReadBlockRange));
result.Bookmarks.AddRange(ReadAllRows(connection, ReadBookmark));
result.UserMarks.AddRange(ReadAllRows(connection, ReadUserMark));
result.InputFields.AddRange(ReadAllRows(connection, ReadInputField));
result.InitBlank();
// ensure bookmarks appear in similar order to original.
result.Bookmarks.Sort((bookmark1, bookmark2) => bookmark1.Slot.CompareTo(bookmark2.Slot));
}
result.LastModified.TimeLastModified = ReadAllRows(connection, ReadLastModified).FirstOrDefault()?.TimeLastModified;
result.Locations.AddRange(ReadAllRows(connection, ReadLocation));
result.Notes.AddRange(ReadAllRows(connection, ReadNote));
result.Tags.AddRange(ReadAllRows(connection, ReadTag));
result.TagMaps.AddRange(ReadAllRows(connection, ReadTagMap));
result.BlockRanges.AddRange(ReadAllRows(connection, ReadBlockRange));
result.Bookmarks.AddRange(ReadAllRows(connection, ReadBookmark));
result.UserMarks.AddRange(ReadAllRows(connection, ReadUserMark));
result.InputFields.AddRange(ReadAllRows(connection, ReadInputField));
// ensure bookmarks appear in similar order to original.
result.Bookmarks.Sort((bookmark1, bookmark2) => bookmark1.Slot.CompareTo(bookmark2.Slot));
return result;
}
@@ -87,26 +85,25 @@
SqliteConnection connection,
Func<SqliteDataReader, TRowType> readRowFunction)
{
using (var cmd = connection.CreateCommand())
using var cmd = connection.CreateCommand();
var result = new List<TRowType>();
var tableName = typeof(TRowType).Name;
cmd.CommandText = $"select * from {tableName}";
Log.Logger.Debug($"SQL: {cmd.CommandText}");
using (var reader = cmd.ExecuteReader())
{
var result = new List<TRowType>();
var tableName = typeof(TRowType).Name;
cmd.CommandText = $"select * from {tableName}";
Log.Logger.Debug($"SQL: {cmd.CommandText}");
using (var reader = cmd.ExecuteReader())
while (reader.Read())
{
while (reader.Read())
{
result.Add(readRowFunction(reader));
}
result.Add(readRowFunction(reader));
}
Log.Logger.Debug($"SQL result set count: {result.Count}");
return result;
}
Log.Logger.Debug($"SQL result set count: {result.Count}");
return result;
}
private static string ReadString(SqliteDataReader reader, string columnName)
@@ -114,7 +111,7 @@
return reader[columnName].ToString();
}
private static string ReadNullableString(SqliteDataReader reader, string columnName)
private static string? ReadNullableString(SqliteDataReader reader, string columnName)
{
var value = reader[columnName];
return value == DBNull.Value ? null : value.ToString();
@@ -159,35 +156,32 @@
private static void VacuumDatabase(SqliteConnection connection)
{
using (var command = connection.CreateCommand())
{
command.CommandText = "vacuum;";
Log.Logger.Debug($"SQL: {command.CommandText}");
using var command = connection.CreateCommand();
command.ExecuteNonQuery();
}
command.CommandText = "vacuum;";
Log.Logger.Debug($"SQL: {command.CommandText}");
command.ExecuteNonQuery();
}
private static void UpdateLastModified(SqliteConnection connection)
{
using (var command = connection.CreateCommand())
{
command.CommandText = "delete from LastModified; insert into LastModified default values";
Log.Logger.Debug($"SQL: {command.CommandText}");
using var command = connection.CreateCommand();
command.ExecuteNonQuery();
}
command.CommandText = "delete from LastModified; insert into LastModified default values";
Log.Logger.Debug($"SQL: {command.CommandText}");
command.ExecuteNonQuery();
}
private static void ClearTable(SqliteConnection connection, string tableName)
{
using (var command = connection.CreateCommand())
{
command.CommandText = $"delete from {tableName}";
Log.Logger.Debug($"SQL: {command.CommandText}");
using var command = connection.CreateCommand();
command.ExecuteNonQuery();
}
command.CommandText = $"delete from {tableName}";
Log.Logger.Debug($"SQL: {command.CommandText}");
command.ExecuteNonQuery();
}
private static void PopulateTable<TRowType>(SqliteConnection connection, List<TRowType> rows)
@@ -202,6 +196,11 @@
foreach (var row in rows)
{
if (row == null)
{
continue;
}
using var cmd = connection.CreateCommand();
cmd.CommandText = $"insert into {tableName} ({columnNamesCsv}) values ({paramNamesCsv})";
AddPopulateTableParams(cmd, columnNames, paramNames, row);
@@ -220,7 +219,7 @@
{
for (int n = 0; n < columnNames.Count; ++n)
{
var value = row.GetType().GetProperty(columnNames[n])?.GetValue(row) ?? DBNull.Value;
var value = row!.GetType().GetProperty(columnNames[n])?.GetValue(row) ?? DBNull.Value;
cmd.Parameters.AddWithValue(paramNames[n], value);
}
}

View File

@@ -5,7 +5,7 @@
/// <summary>
/// Used by the <see cref="Merger"/> to map old and new id values./>
/// </summary>
internal class IdTranslator
internal sealed class IdTranslator
{
private readonly Dictionary<int, int> _ids;

View File

@@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JWLMerge.BackupFileServices.Events;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using Events;
using Models.DatabaseModels;
using Serilog;
/// <summary>
@@ -25,7 +25,7 @@
private int _maxBlockRangeId;
private int _maxBookmarkId;
public event EventHandler<ProgressEventArgs> ProgressEvent;
public event EventHandler<ProgressEventArgs>? ProgressEvent;
/// <summary>
/// Merges the specified databases.
@@ -158,7 +158,7 @@
}
}
private bool OverlappingBlockRanges(BlockRange blockRange1, BlockRange blockRange2)
private static bool OverlappingBlockRanges(BlockRange blockRange1, BlockRange blockRange2)
{
if (blockRange1.StartToken == blockRange2.StartToken &&
blockRange1.EndToken == blockRange2.EndToken)
@@ -192,7 +192,7 @@
var tagId = _translatedTagIds.GetTranslatedId(sourceTagMap.TagId);
var id = 0;
TagMap existingTagMap = null;
TagMap? existingTagMap = null;
if (sourceTagMap.LocationId != null)
{
@@ -202,8 +202,11 @@
{
// must add location...
var location = source.FindLocation(sourceTagMap.LocationId.Value);
InsertLocation(location, destination);
id = _translatedLocationIds.GetTranslatedId(sourceTagMap.LocationId.Value);
if (location != null)
{
InsertLocation(location, destination);
id = _translatedLocationIds.GetTranslatedId(sourceTagMap.LocationId.Value);
}
}
existingTagMap = destination.FindTagMapForLocation(tagId, id);
@@ -224,7 +227,7 @@
NormaliseTagMapPositions(destination.TagMaps);
}
private void NormaliseTagMapPositions(List<TagMap> entries)
private static void NormaliseTagMapPositions(List<TagMap> entries)
{
// there is unique constraint on TagId, Position
var tmpStorage = entries.GroupBy(x => x.TagId).ToDictionary(x => x.Key);
@@ -273,8 +276,11 @@
{
var referencedLocation = sourceUserMark.LocationId;
var location = source.FindLocation(referencedLocation);
if (location != null)
{
InsertLocation(location, destination);
}
InsertLocation(location, destination);
InsertUserMark(sourceUserMark, destination);
}
}
@@ -300,7 +306,7 @@
}
}
private void InsertInputField(InputField inputField, int locationId, Database destination)
private static void InsertInputField(InputField inputField, int locationId, Database destination)
{
var inputFldClone = inputField.Clone();
inputFldClone.LocationId = locationId;
@@ -438,8 +444,10 @@
{
var referencedLocation = note.LocationId.Value;
var location = source.FindLocation(referencedLocation);
InsertLocation(location, destination);
if (location != null)
{
InsertLocation(location, destination);
}
}
InsertNote(note, destination);
@@ -447,7 +455,7 @@
}
}
private void UpdateNote(Note source, Note destination)
private static void UpdateNote(Note source, Note destination)
{
destination.Title = source.Title;
destination.Content = source.Content;
@@ -456,7 +464,7 @@
private void OnProgressEvent(string message)
{
ProgressEvent?.Invoke(this, new ProgressEventArgs { Message = message });
ProgressEvent?.Invoke(this, new ProgressEventArgs(message));
}
private void ProgressMessage(string logMessage)

View File

@@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JWLMerge.BackupFileServices.Models;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using Models;
using Models.DatabaseModels;
internal sealed class NotesImporter
{
@@ -74,7 +74,7 @@
}
else
{
if (!existingNote.Content.Equals(note.NoteContent))
if (NoteIsDifferent(existingNote, note.NoteContent))
{
// need to update the note.
result.BibleNotesUpdated++;
@@ -92,6 +92,16 @@
return result;
}
private static bool NoteIsDifferent(Note existingNote, string? newNote)
{
if (string.IsNullOrEmpty(existingNote.Content))
{
return !string.IsNullOrEmpty(newNote);
}
return !existingNote.Content.Equals(newNote);
}
private void InsertNote(BibleNote note)
{
var book = note.BookChapterAndVerse.BookNumber;
@@ -100,7 +110,7 @@
var location = _targetDatabase.FindLocationByBibleChapter(_bibleKeySymbol, book, chapter) ??
InsertLocation(book, chapter);
UserMark userMark = null;
UserMark? userMark = null;
if (note.StartTokenInVerse != null && note.EndTokenInVerse != null)
{
// the note should be associated with some
@@ -185,7 +195,7 @@
return userMark;
}
private UserMark FindExistingUserMark(int locationId, int startToken, int endToken)
private UserMark? FindExistingUserMark(int locationId, int startToken, int endToken)
{
var userMarksForLocation = _targetDatabase.FindUserMarks(locationId);
if (userMarksForLocation == null)
@@ -228,7 +238,7 @@
return location;
}
private Note FindExistingNote(Database database, BibleNote note)
private static Note? FindExistingNote(Database database, BibleNote note)
{
var existingVerseNotes = database.FindNotes(note.BookChapterAndVerse);
return existingVerseNotes?.FirstOrDefault(verseNote => verseNote.Title == note.NoteTitle);

View File

@@ -4,7 +4,7 @@
using System.Text;
// ReSharper disable once ClassNeverInstantiated.Global
internal class RedactService
internal sealed class RedactService
{
private readonly Lazy<string[]> _loremIpsumLines;
private readonly Random _random = new Random();
@@ -32,7 +32,7 @@
{
if (sb.Length > 0)
{
sb.Append(" ");
sb.Append(' ');
}
sb.Append(GetRandomSentence());

View File

@@ -2,10 +2,10 @@
{
using System;
using System.Collections.Generic;
using JWLMerge.BackupFileServices.Events;
using JWLMerge.BackupFileServices.Models;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using JWLMerge.ExcelServices;
using Events;
using Models;
using Models.DatabaseModels;
using ExcelServices;
/// <summary>
/// The BackupFileService interface.
@@ -71,7 +71,7 @@
/// <returns>Number of notes removed.</returns>
int RemoveNotesByTag(
BackupFile backup,
int[] tagIds,
int[]? tagIds,
bool removeUntaggedNotes,
bool removeAssociatedUnderlining,
bool removeAssociatedTags);
@@ -83,7 +83,7 @@
/// <param name="colorIndexes">The color indexes to target.</param>
/// <param name="removeAssociatedNotes">Whether associated notes should also be removed.</param>
/// <returns>Number of underlined items removed.</returns>
int RemoveUnderliningByColour(BackupFile backup, int[] colorIndexes, bool removeAssociatedNotes);
int RemoveUnderliningByColour(BackupFile backup, int[]? colorIndexes, bool removeAssociatedNotes);
/// <summary>
/// Removes underlining by publication and colour.
@@ -99,7 +99,7 @@
BackupFile backup,
int colorIndex,
bool anyColor,
string publicationSymbol,
string? publicationSymbol,
bool anyPublication,
bool removeAssociatedNotes);

View File

@@ -14,6 +14,10 @@
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.6" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />

View File

@@ -2,7 +2,7 @@
{
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JWLMerge.BackupFileServices.Models.ManifestFile;
using ManifestFile;
/// <summary>
/// The Backup file.
@@ -10,15 +10,15 @@
/// <remarks>We implement INotifyPropertyChanged to prevent the common "WPF binding leak".</remarks>
public sealed class BackupFile : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;
public Manifest Manifest { get; set; }
public Manifest Manifest { get; set; } = null!;
public DatabaseModels.Database Database { get; set; }
public DatabaseModels.Database Database { get; set; } = null!;
#pragma warning disable IDE0051 // Remove unused private members
#pragma warning disable S1144 // Remove unused private members
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
#pragma warning restore S1144 // Remove unused private members
#pragma warning restore IDE0051 // Remove unused private members
{

View File

@@ -2,7 +2,7 @@
{
using System;
public struct BibleBookChapterAndVerse : IEquatable<BibleBookChapterAndVerse>
public readonly struct BibleBookChapterAndVerse : IEquatable<BibleBookChapterAndVerse>
{
public BibleBookChapterAndVerse(int bookNum, int chapterNum, int verseNum)
{

View File

@@ -1,6 +1,6 @@
namespace JWLMerge.BackupFileServices.Models
{
internal class BibleNotesVerseSpecification
internal sealed class BibleNotesVerseSpecification
{
public int BookNumber { get; set; }

View File

@@ -10,8 +10,8 @@
public int ColourIndex { get; set; }
public string NoteTitle { get; set; }
public string? NoteTitle { get; set; }
public string NoteContent { get; set; }
public string? NoteContent { get; set; }
}
}

View File

@@ -28,12 +28,12 @@
/// <summary>
/// The title text.
/// </summary>
public string Title { get; set; }
public string Title { get; set; } = null!;
/// <summary>
/// A snippet of the bookmarked text (can be null)
/// </summary>
public string Snippet { get; set; }
public string? Snippet { get; set; }
/// <summary>
/// The block type. Compare Locations.cs > BlockType

View File

@@ -3,29 +3,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JWLMerge.BackupFileServices.Exceptions;
using Exceptions;
using Serilog;
public class Database
{
private readonly Dictionary<int, int> _bookmarkSlots = new Dictionary<int, int>();
private Lazy<Dictionary<string, Note>> _notesGuidIndex;
private Lazy<Dictionary<int, Note>> _notesIdIndex;
private Lazy<Dictionary<int, List<InputField>>> _inputFieldsIndex;
private Lazy<Dictionary<BibleBookChapterAndVerse, List<Note>>> _notesVerseIndex;
private Lazy<Dictionary<string, UserMark>> _userMarksGuidIndex;
private Lazy<Dictionary<int, UserMark>> _userMarksIdIndex;
private Lazy<Dictionary<int, List<UserMark>>> _userMarksLocationIdIndex;
private Lazy<Dictionary<int, Location>> _locationsIdIndex;
private Lazy<Dictionary<string, Location>> _locationsValueIndex;
private Lazy<Dictionary<string, Location>> _locationsBibleChapterIndex;
private Lazy<Dictionary<TagTypeAndName, Tag>> _tagsNameIndex;
private Lazy<Dictionary<int, Tag>> _tagsIdIndex;
private Lazy<Dictionary<string, TagMap>> _tagMapNoteIndex;
private Lazy<Dictionary<string, TagMap>> _tagMapLocationIndex;
private Lazy<Dictionary<int, List<BlockRange>>> _blockRangesUserMarkIdIndex;
private Lazy<Dictionary<string, Bookmark>> _bookmarksIndex;
private Lazy<Dictionary<string, Note>> _notesGuidIndex = null!;
private Lazy<Dictionary<int, Note>> _notesIdIndex = null!;
private Lazy<Dictionary<int, List<InputField>>> _inputFieldsIndex = null!;
private Lazy<Dictionary<BibleBookChapterAndVerse, List<Note>>> _notesVerseIndex = null!;
private Lazy<Dictionary<string, UserMark>> _userMarksGuidIndex = null!;
private Lazy<Dictionary<int, UserMark>> _userMarksIdIndex = null!;
private Lazy<Dictionary<int, List<UserMark>>> _userMarksLocationIdIndex = null!;
private Lazy<Dictionary<int, Location>> _locationsIdIndex = null!;
private Lazy<Dictionary<string, Location>> _locationsValueIndex = null!;
private Lazy<Dictionary<string, Location>> _locationsBibleChapterIndex = null!;
private Lazy<Dictionary<TagTypeAndName, Tag>> _tagsNameIndex = null!;
private Lazy<Dictionary<int, Tag>> _tagsIdIndex = null!;
private Lazy<Dictionary<string, TagMap>> _tagMapNoteIndex = null!;
private Lazy<Dictionary<string, TagMap>> _tagMapLocationIndex = null!;
private Lazy<Dictionary<int, List<BlockRange>>> _blockRangesUserMarkIdIndex = null!;
private Lazy<Dictionary<string, Bookmark>> _bookmarksIndex = null!;
public Database()
{
@@ -99,7 +99,7 @@
public void AddBibleNoteAndUpdateIndex(
BibleBookChapterAndVerse verseRef,
Note value,
TagMap tagMap)
TagMap? tagMap)
{
if (value == null)
{
@@ -226,63 +226,63 @@
}
}
public Note FindNote(string noteGuid)
public Note? FindNote(string noteGuid)
{
return _notesGuidIndex.Value.TryGetValue(noteGuid, out var note) ? note : null;
}
public Note FindNote(int noteId)
public Note? FindNote(int noteId)
{
return _notesIdIndex.Value.TryGetValue(noteId, out var note) ? note : null;
}
public IEnumerable<Note> FindNotes(BibleBookChapterAndVerse verseRef)
public IEnumerable<Note>? FindNotes(BibleBookChapterAndVerse verseRef)
{
return _notesVerseIndex.Value.TryGetValue(verseRef, out var notes) ? notes : null;
}
public UserMark FindUserMark(string userMarkGuid)
public UserMark? FindUserMark(string userMarkGuid)
{
return _userMarksGuidIndex.Value.TryGetValue(userMarkGuid, out var userMark) ? userMark : null;
}
public UserMark FindUserMark(int userMarkId)
public UserMark? FindUserMark(int userMarkId)
{
return _userMarksIdIndex.Value.TryGetValue(userMarkId, out var userMark) ? userMark : null;
}
public IEnumerable<UserMark> FindUserMarks(int locationId)
public IEnumerable<UserMark>? FindUserMarks(int locationId)
{
return _userMarksLocationIdIndex.Value.TryGetValue(locationId, out var userMarks) ? userMarks : null;
}
public Tag FindTag(int tagType, string tagName)
public Tag? FindTag(int tagType, string tagName)
{
var key = new TagTypeAndName(tagType, tagName);
return _tagsNameIndex.Value.TryGetValue(key, out var tag) ? tag : null;
}
public Tag FindTag(int tagId)
public Tag? FindTag(int tagId)
{
return _tagsIdIndex.Value.TryGetValue(tagId, out var tag) ? tag : null;
}
public TagMap FindTagMapForNote(int tagId, int noteId)
public TagMap? FindTagMapForNote(int tagId, int noteId)
{
return _tagMapNoteIndex.Value.TryGetValue(GetTagMapNoteKey(tagId, noteId), out var tag) ? tag : null;
}
public TagMap FindTagMapForLocation(int tagId, int locationId)
public TagMap? FindTagMapForLocation(int tagId, int locationId)
{
return _tagMapLocationIndex.Value.TryGetValue(GetTagMapLocationKey(tagId, locationId), out var tag) ? tag : null;
}
public Location FindLocation(int locationId)
public Location? FindLocation(int locationId)
{
return _locationsIdIndex.Value.TryGetValue(locationId, out var location) ? location : null;
}
public InputField FindInputField(int locationId, string textTag)
public InputField? FindInputField(int locationId, string textTag)
{
if (!_inputFieldsIndex.Value.TryGetValue(locationId, out var list))
{
@@ -292,7 +292,7 @@
return list.SingleOrDefault(x => x.TextTag.Equals(textTag, StringComparison.OrdinalIgnoreCase));
}
public Location FindLocationByValues(Location locationValues)
public Location? FindLocationByValues(Location locationValues)
{
if (locationValues == null)
{
@@ -303,18 +303,18 @@
return _locationsValueIndex.Value.TryGetValue(key, out var location) ? location : null;
}
public Location FindLocationByBibleChapter(string bibleKeySymbol, int bibleBookNumber, int bibleChapter)
public Location? FindLocationByBibleChapter(string bibleKeySymbol, int bibleBookNumber, int bibleChapter)
{
var key = GetLocationByBibleChapterKey(bibleBookNumber, bibleChapter, bibleKeySymbol);
return _locationsBibleChapterIndex.Value.TryGetValue(key, out var location) ? location : null;
}
public IReadOnlyCollection<BlockRange> FindBlockRanges(int userMarkId)
public IReadOnlyCollection<BlockRange>? FindBlockRanges(int userMarkId)
{
return _blockRangesUserMarkIdIndex.Value.TryGetValue(userMarkId, out var ranges) ? ranges : null;
}
public Bookmark FindBookmark(int locationId, int publicationLocationId)
public Bookmark? FindBookmark(int locationId, int publicationLocationId)
{
string key = GetBookmarkKey(locationId, publicationLocationId);
return _bookmarksIndex.Value.TryGetValue(key, out var bookmark) ? bookmark : null;
@@ -483,19 +483,19 @@
return result;
}
private string GetBookmarkKey(int locationId, int publicationLocationId)
private static string GetBookmarkKey(int locationId, int publicationLocationId)
{
return $"{locationId}-{publicationLocationId}";
}
private string GetLocationByValueKey(Location location)
private static 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 string GetLocationByBibleChapterKey(int bibleBookNumber, int chapterNumber, string bibleKeySymbol)
private static string GetLocationByBibleChapterKey(int bibleBookNumber, int chapterNumber, string? bibleKeySymbol)
{
return $"{bibleBookNumber}-{chapterNumber}-{bibleKeySymbol}";
return $"{bibleBookNumber}-{chapterNumber}-{bibleKeySymbol ?? string.Empty}";
}
private Dictionary<string, Bookmark> BookmarkIndexValueFactory()
@@ -524,12 +524,12 @@
return Tags.ToDictionary(tag => tag.TagId);
}
private string GetTagMapNoteKey(int tagId, int noteId)
private static string GetTagMapNoteKey(int tagId, int noteId)
{
return $"{tagId}-{noteId}";
}
private string GetTagMapLocationKey(int tagId, int locationId)
private static string GetTagMapLocationKey(int tagId, int locationId)
{
return $"{tagId}-{locationId}";
}

View File

@@ -4,9 +4,9 @@
{
public int LocationId { get; set; }
public string TextTag { get; set; }
public string TextTag { get; set; } = null!;
public string Value { get; set; }
public string Value { get; set; } = null!;
public InputField Clone()
{

View File

@@ -8,7 +8,7 @@
/// Time stamp when the database was last modified.
/// </summary>
[JsonProperty(PropertyName = "LastModified")]
public string TimeLastModified { get; set; }
public string? TimeLastModified { get; set; }
public void Reset()
{

View File

@@ -38,7 +38,7 @@
/// <summary>
/// The JWL publication key symbol (nullable).
/// </summary>
public string KeySymbol { get; set; }
public string? KeySymbol { get; set; }
/// <summary>
/// The MEPS identifier for the publication language.
@@ -57,7 +57,7 @@
/// <summary>
/// A location title (nullable).
/// </summary>
public string Title { get; set; }
public string? Title { get; set; }
public Location Clone()
{

View File

@@ -12,7 +12,7 @@
/// <summary>
/// A Guid (that should assist in merging notes).
/// </summary>
public string Guid { get; set; }
public string Guid { get; set; } = null!;
/// <summary>
/// The user mark identifier (if the note is associated with user-highlighting).
@@ -28,17 +28,17 @@
/// <summary>
/// The user-defined note title.
/// </summary>
public string Title { get; set; }
public string? Title { get; set; }
/// <summary>
/// The user-defined note content.
/// </summary>
public string Content { get; set; }
public string? Content { get; set; }
/// <summary>
/// Time stamp when the note was last edited. ISO 8601 format.
/// </summary>
public string LastModified { get; set; }
public string? LastModified { get; set; }
/// <summary>
/// The type of block associated with the note.

View File

@@ -16,13 +16,13 @@
/// <summary>
/// The name of the tag.
/// </summary>
public string Name { get; set; }
public string Name { get; set; } = null!;
/// <summary>
/// The optional image file name.
/// </summary>
/// <remarks>Added in db ver 7 April 2020.</remarks>
public string ImageFileName { get; set; }
public string? ImageFileName { get; set; }
public Tag Clone()
{

View File

@@ -18,19 +18,19 @@
/// <summary>
/// Playlist Item Id.
/// </summary>
/// <remarks>added in in db v7, Apr 2020</remarks>
/// <remarks>added in db v7, Apr 2020</remarks>
public int? PlaylistItemId { get; set; }
/// <summary>
/// Location Id.
/// </summary>
/// <remarks>added in in db v7, Apr 2020</remarks>
/// <remarks>added in db v7, Apr 2020</remarks>
public int? LocationId { get; set; }
/// <summary>
/// Note Id.
/// </summary>
/// <remarks>added in in db v7, Apr 2020.</remarks>
/// <remarks>added in db v7, Apr 2020.</remarks>
public int? NoteId { get; set; }
/// <summary>

View File

@@ -27,7 +27,7 @@
/// <summary>
/// The guid. Useful in merging!
/// </summary>
public string UserMarkGuid { get; set; }
public string UserMarkGuid { get; set; } = null!;
/// <summary>
/// The highlight version. Semantics unknown!

View File

@@ -9,17 +9,17 @@
/// <remarks>We implement INotifyPropertyChanged to prevent the common "WPF binding leak".</remarks>
public sealed class Manifest : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// The name of the backup file (without the "jwlibrary" extension).
/// </summary>
public string Name { get; set; }
public string Name { get; set; } = null!;
/// <summary>
/// The local creation date in the form "YYYY-MM-DD"
/// </summary>
public string CreationDate { get; set; }
public string CreationDate { get; set; } = null!;
/// <summary>
/// The manifest schema version.
@@ -34,7 +34,7 @@
/// <summary>
/// Details of the backup database.
/// </summary>
public UserDataBackup UserDataBackup { get; set; }
public UserDataBackup UserDataBackup { get; set; } = null!;
public Manifest Clone()
{
@@ -43,7 +43,7 @@
#pragma warning disable IDE0051 // Remove unused private members
#pragma warning disable S1144 // Remove unused private members
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
#pragma warning restore S1144 // Remove unused private members
#pragma warning restore IDE0051 // Remove unused private members
{

View File

@@ -10,28 +10,28 @@
/// <remarks>We implement INotifyPropertyChanged to prevent the common "WPF binding leak".</remarks>
public sealed class UserDataBackup : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// The last modified date of the database in ISO 8601, e.g. "2018-01-17T14:37:27+00:00"
/// Corresponds to the value in the LastModifiedDate table.
/// </summary>
public string LastModifiedDate { get; set; }
public string LastModifiedDate { get; set; } = null!;
/// <summary>
/// The name of the source device (e.g. the name of the PC).
/// </summary>
public string DeviceName { get; set; }
public string DeviceName { get; set; } = null!;
/// <summary>
/// The database name (always "userData.db"?)
/// </summary>
public string DatabaseName { get; set; }
public string DatabaseName { get; set; } = null!;
/// <summary>
/// A sha256 hash of the associated database file.
/// </summary>
public string Hash { get; set; }
public string Hash { get; set; } = null!;
/// <summary>
/// The database schema version.
@@ -41,7 +41,7 @@
#pragma warning disable IDE0051 // Remove unused private members
#pragma warning disable S1144 // Remove unused private members
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
#pragma warning restore S1144 // Remove unused private members
#pragma warning restore IDE0051 // Remove unused private members
{

View File

@@ -1,9 +1,9 @@
namespace JWLMerge.BackupFileServices.Models
{
internal class NoteTitleAndContent
internal sealed class NoteTitleAndContent
{
public string Title { get; set; }
public string? Title { get; set; }
public string Content { get; set; }
public string? Content { get; set; }
}
}

View File

@@ -1,6 +1,6 @@
namespace JWLMerge.BackupFileServices.Models
{
internal class TagTypeAndName
internal sealed class TagTypeAndName
{
public TagTypeAndName(int type, string name)
{

View File

@@ -13,7 +13,11 @@ namespace JWLMerge.ExcelServices
private const string WorkbookName = "Notes";
// returns the last row written
public int AppendToBibleNotesFile(string excelFilePath, IReadOnlyCollection<BibleNote> notes, int startRow, string backupFilePath)
public int AppendToBibleNotesFile(
string excelFilePath,
IReadOnlyCollection<BibleNote>? notes,
int startRow,
string backupFilePath)
{
if (string.IsNullOrEmpty(excelFilePath))
{
@@ -30,73 +34,71 @@ namespace JWLMerge.ExcelServices
{
return startRow;
}
using (var workbook = new XLWorkbook(excelFilePath))
using var workbook = new XLWorkbook(excelFilePath);
if (!workbook.Worksheets.TryGetWorksheet(WorkbookName, out var worksheet))
{
if (!workbook.Worksheets.TryGetWorksheet(WorkbookName, out var worksheet))
{
throw new ExcelServicesException("Could not find worksheet!");
}
var row = startRow;
foreach (var note in notes)
{
SetCellStringValue(worksheet, row, 1, note.PubSymbol);
SetCellStringValue(worksheet, row, 2, note.BookName);
SetCellIntegerValue(worksheet, row, 3, note.BookNumber);
SetCellIntegerValue(worksheet, row, 4, note.ChapterNumber);
SetCellIntegerValue(worksheet, row, 5, note.VerseNumber);
SetCellStringValue(worksheet, row, 6, note.ChapterAndVerseString);
SetCellStringValue(worksheet, row, 7, note.BookNameChapterAndVerseString);
SetCellIntegerValue(worksheet, row, 8, note.ColorCode);
SetCellStringValue(worksheet, row, 9, note.TagsCsv);
SetCellStringValue(worksheet, row, 10, note.NoteTitle);
SetCellStringValue(worksheet, row, 11, note.NoteContent);
++row;
}
workbook.Save();
return row;
throw new ExcelServicesException("Could not find worksheet!");
}
var row = startRow;
foreach (var note in notes)
{
SetCellStringValue(worksheet, row, 1, note.PubSymbol);
SetCellStringValue(worksheet, row, 2, note.BookName);
SetCellIntegerValue(worksheet, row, 3, note.BookNumber);
SetCellIntegerValue(worksheet, row, 4, note.ChapterNumber);
SetCellIntegerValue(worksheet, row, 5, note.VerseNumber);
SetCellStringValue(worksheet, row, 6, note.ChapterAndVerseString);
SetCellStringValue(worksheet, row, 7, note.BookNameChapterAndVerseString);
SetCellIntegerValue(worksheet, row, 8, note.ColorCode);
SetCellStringValue(worksheet, row, 9, note.TagsCsv);
SetCellStringValue(worksheet, row, 10, note.NoteTitle);
SetCellStringValue(worksheet, row, 11, note.NoteContent);
++row;
}
workbook.Save();
return row;
}
private void SetCellStringValue(IXLWorksheet worksheet, int row, int col, string value)
private static void SetCellStringValue(IXLWorksheet worksheet, int row, int col, string? value)
{
worksheet.Cell(row, col).DataType = XLDataType.Text;
worksheet.Cell(row, col).SetValue(value);
worksheet.Cell(row, col).SetValue(value ?? string.Empty);
}
private void SetCellIntegerValue(IXLWorksheet worksheet, int row, int col, int? value)
private static void SetCellIntegerValue(IXLWorksheet worksheet, int row, int col, int? value)
{
worksheet.Cell(row, col).DataType = XLDataType.Number;
worksheet.Cell(row, col).SetValue(value);
}
private int GenerateHeader(string excelFilePath, string backupFilePath)
private static int GenerateHeader(string excelFilePath, string backupFilePath)
{
using (var workbook = new XLWorkbook())
{
var worksheet = workbook.Worksheets.Add(WorkbookName);
worksheet.Cell("A1").Value = "Bible Notes Extracted from JWL Backup File";
worksheet.Cell("A2").Value = $"Backup file: {backupFilePath}";
worksheet.Cell("A3").Value = $"Exported: {DateTime.Now.ToShortDateString()}";
using var workbook = new XLWorkbook();
worksheet.Cell("A5").Value = "Symbol";
worksheet.Cell("B5").Value = "Book";
worksheet.Cell("C5").Value = "BookNumber";
worksheet.Cell("D5").Value = "Chapter";
worksheet.Cell("E5").Value = "Verse";
worksheet.Cell("F5").Value = "ChapterAndVerse";
worksheet.Cell("G5").Value = "FullRef";
worksheet.Cell("H5").Value = "Color";
worksheet.Cell("I5").Value = "Tags";
worksheet.Cell("J5").Value = "Title";
worksheet.Cell("K5").Value = "Content";
var worksheet = workbook.Worksheets.Add(WorkbookName);
worksheet.Cell("A1").Value = "Bible Notes Extracted from JWL Backup File";
worksheet.Cell("A2").Value = $"Backup file: {backupFilePath}";
worksheet.Cell("A3").Value = $"Exported: {DateTime.Now.ToShortDateString()}";
workbook.SaveAs(excelFilePath);
}
worksheet.Cell("A5").Value = "Symbol";
worksheet.Cell("B5").Value = "Book";
worksheet.Cell("C5").Value = "BookNumber";
worksheet.Cell("D5").Value = "Chapter";
worksheet.Cell("E5").Value = "Verse";
worksheet.Cell("F5").Value = "ChapterAndVerse";
worksheet.Cell("G5").Value = "FullRef";
worksheet.Cell("H5").Value = "Color";
worksheet.Cell("I5").Value = "Tags";
worksheet.Cell("J5").Value = "Title";
worksheet.Cell("K5").Value = "Content";
workbook.SaveAs(excelFilePath);
// return last row used.
return 4;

View File

@@ -5,6 +5,10 @@ namespace JWLMerge.ExcelServices
{
public interface IExcelService
{
int AppendToBibleNotesFile(string excelFilePath, IReadOnlyCollection<BibleNote> notes, int startRow, string backupFilePath);
int AppendToBibleNotesFile(
string excelFilePath,
IReadOnlyCollection<BibleNote>? notes,
int startRow,
string backupFilePath);
}
}

View File

@@ -18,4 +18,8 @@
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
</Project>

View File

@@ -2,23 +2,29 @@
{
public class BibleNote
{
public int BookNumber { get; set; }
public BibleNote(int bookNumber, string bookName)
{
BookNumber = bookNumber;
BookName = bookName;
}
public string BookName { get; set; }
public int BookNumber { get; }
public string BookName { get; }
public int? ChapterNumber { get; set; }
public int? VerseNumber { get; set; }
public string NoteTitle { get; set; }
public string? NoteTitle { get; set; }
public string NoteContent { get; set; }
public string? NoteContent { get; set; }
public string TagsCsv { get; set; }
public string? TagsCsv { get; set; }
public int? ColorCode { get; set; }
public string PubSymbol { get; set; }
public string? PubSymbol { get; set; }
public string ChapterAndVerseString
{

View File

@@ -7,6 +7,10 @@
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />

View File

@@ -2,24 +2,24 @@
{
using System;
using System.Collections.Generic;
using JWLMerge.BackupFileServices.Models;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using JWLMerge.BackupFileServices.Models.ManifestFile;
using BackupFileServices.Models;
using BackupFileServices.Models.DatabaseModels;
using BackupFileServices.Models.ManifestFile;
public class TestBase
{
private readonly Random _random = new Random();
private readonly Random _random = new();
protected BackupFile CreateMockBackup(int numRecords = 100)
{
return new BackupFile
return new()
{
Manifest = CreateMockManifest(),
Database = CreateMockDatabase(numRecords),
};
}
protected Database CreateMockDatabase(int numRecords)
private Database CreateMockDatabase(int numRecords)
{
var result = new Database();
@@ -34,7 +34,7 @@
return result;
}
protected Manifest CreateMockManifest()
private static Manifest CreateMockManifest()
{
DateTime now = DateTime.Now;
string simpleDateString = $"{now.Year}-{now.Month:D2}-{now.Day:D2}";
@@ -55,7 +55,7 @@
};
}
private List<BlockRange> CreateMockBlockRanges(int numRecords)
private static List<BlockRange> CreateMockBlockRanges(int numRecords)
{
var result = new List<BlockRange>();
@@ -97,7 +97,7 @@
return result;
}
private List<UserMark> CreateMockUserMarks(int numRecords)
private static List<UserMark> CreateMockUserMarks(int numRecords)
{
var result = new List<UserMark>();

View File

@@ -1,8 +1,7 @@
namespace JWLMerge.Tests
{
using JWLMerge.BackupFileServices.Helpers;
using JWLMerge.BackupFileServices.Models;
using JWLMerge.BackupFileServices.Models.ManifestFile;
using BackupFileServices.Helpers;
using BackupFileServices.Models.ManifestFile;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
@@ -11,11 +10,11 @@
[TestMethod]
public void TestAllClean()
{
BackupFile file = CreateMockBackup();
var file = CreateMockBackup();
file.Manifest = new Manifest();
Cleaner cleaner = new Cleaner(file.Database);
int removedRows = cleaner.Clean();
var cleaner = new Cleaner(file.Database);
var removedRows = cleaner.Clean();
Assert.AreEqual(0, removedRows);
}

View File

@@ -2,9 +2,9 @@
{
using System.Collections.Generic;
using System.Linq;
using JWLMerge.BackupFileServices.Helpers;
using JWLMerge.BackupFileServices.Models;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using BackupFileServices.Helpers;
using BackupFileServices.Models;
using BackupFileServices.Models.DatabaseModels;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
@@ -13,13 +13,14 @@
[TestMethod]
public void TestImport1()
{
int numRecords = 100;
const int numRecords = 100;
var file1 = CreateMockBackup(numRecords);
var notes = CreateMockBibleNotes().ToArray();
var mockImportOptions = new ImportBibleNotesParams();
int mepsLanguageId = 0;
const int mepsLanguageId = 0;
var importer = new NotesImporter(
file1.Database,
"nwtsty",
@@ -41,17 +42,17 @@
Assert.IsTrue(result.BibleNotesUpdated == 0);
}
private IEnumerable<BibleNote> CreateMockBibleNotes()
private static IEnumerable<BibleNote> CreateMockBibleNotes()
{
return new List<BibleNote>
{
new BibleNote
new()
{
BookChapterAndVerse = new BibleBookChapterAndVerse(1, 1, 1),
NoteTitle = "A note 1",
NoteContent = "My notes go here",
},
new BibleNote
new()
{
BookChapterAndVerse = new BibleBookChapterAndVerse(2, 2, 2),
NoteTitle = "A note 2",

View File

@@ -1,7 +1,6 @@
namespace JWLMerge.Tests
{
using System;
using JWLMerge.BackupFileServices.Helpers;
using BackupFileServices.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
@@ -10,13 +9,13 @@
[TestMethod]
public void TestMerge1()
{
int numRecords = 100;
const int numRecords = 100;
var file1 = CreateMockBackup(numRecords);
var file2 = CreateMockBackup(numRecords);
var file3 = CreateMockBackup(numRecords);
Merger merger = new Merger();
var merger = new Merger();
var mergedDatabase = merger.Merge(new[] { file1.Database, file2.Database, file3.Database });
mergedDatabase.CheckValidity();

View File

@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JWLMerge.BackupFileServices", "JWLMerge.BackupFileServices\JWLMerge.BackupFileServices.csproj", "{83446629-CDBB-43FF-B628-1B8A3A9603C3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JWLMerge.BackupFileServices", "JWLMerge.BackupFileServices\JWLMerge.BackupFileServices.csproj", "{83446629-CDBB-43FF-B628-1B8A3A9603C3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JWL Database Schemas", "JWL Database Schemas", "{DA7BDF58-CFEA-489C-B18C-944D9986758D}"
ProjectSection(SolutionItems) = preProject
@@ -13,21 +13,20 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JWL Database Schemas", "JWL
Version008.txt = Version008.txt
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JWLMergeCLI", "JWLMergeCLI\JWLMergeCLI.csproj", "{2CBBA1C2-72C9-4287-A262-EC1D2A2F6E56}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JWLMergeCLI", "JWLMergeCLI\JWLMergeCLI.csproj", "{2CBBA1C2-72C9-4287-A262-EC1D2A2F6E56}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DA506BB2-675E-47BE-BC54-ED6EAE369243}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
JWLMerge\JWLMerge.ruleset = JWLMerge\JWLMerge.ruleset
jwlmergecmdline.png = jwlmergecmdline.png
SolutionInfo.cs = SolutionInfo.cs
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JWLMerge.Tests", "JWLMerge.Tests\JWLMerge.Tests.csproj", "{07B843A0-2DD7-40C1-AFE9-5F1BCA22393E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JWLMerge.Tests", "JWLMerge.Tests\JWLMerge.Tests.csproj", "{07B843A0-2DD7-40C1-AFE9-5F1BCA22393E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JWLMerge", "JWLMerge\JWLMerge.csproj", "{0D88A466-7C90-4C0B-B8BA-E1F5EC91D4A4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JWLMerge", "JWLMerge\JWLMerge.csproj", "{0D88A466-7C90-4C0B-B8BA-E1F5EC91D4A4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JWLMerge.ExcelServices", "JWLMerge.ExcelServices\JWLMerge.ExcelServices.csproj", "{F8D64B6C-C475-4B5E-8B32-D717380F0F36}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JWLMerge.ExcelServices", "JWLMerge.ExcelServices\JWLMerge.ExcelServices.csproj", "{F8D64B6C-C475-4B5E-8B32-D717380F0F36}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@@ -1,7 +1,9 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=JWLMerge_002EBackupFileServices_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Colossians/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=colour/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=colours/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Favourite/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=favourites/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fixup/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=initialised/@EntryIndexedValue">True</s:Boolean>
@@ -10,4 +12,6 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=lorem/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Meps/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Normalise/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=nwtsty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=playlists/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Snackbar/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -10,10 +10,10 @@ namespace JWLMerge
using System.Windows.Interop;
using System.Windows.Media;
using Serilog;
using JWLMerge.BackupFileServices;
using JWLMerge.ExcelServices;
using JWLMerge.Services;
using JWLMerge.ViewModel;
using BackupFileServices;
using ExcelServices;
using Services;
using ViewModel;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Toolkit.Mvvm.DependencyInjection;
@@ -23,15 +23,14 @@ namespace JWLMerge
public partial class App : Application
{
private readonly string _appString = "JWLMergeAC";
private Mutex _appMutex;
private Mutex? _appMutex;
protected override void OnExit(ExitEventArgs e)
{
_appMutex?.Dispose();
Log.Logger.Information("==== Exit ====");
}
protected override void OnStartup(StartupEventArgs e)
{
ConfigureServices();
@@ -60,7 +59,7 @@ namespace JWLMerge
Current.Shutdown();
}
private void ConfigureServices()
private static void ConfigureServices()
{
var serviceCollection = new ServiceCollection();
@@ -88,7 +87,7 @@ namespace JWLMerge
Ioc.Default.ConfigureServices(serviceProvider);
}
private bool ForceSoftwareRendering()
private static bool ForceSoftwareRendering()
{
// https://blogs.msdn.microsoft.com/jgoldb/2010/06/22/software-rendering-usage-in-wpf/
// renderingTier values:

View File

@@ -1,10 +1,9 @@
namespace JWLMerge.Helpers
{
using System;
using System.Collections.Generic;
using System.Linq;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using JWLMerge.Models;
using Models;
internal static class ColourHelper
{

View File

@@ -1,9 +1,9 @@
namespace JWLMerge.Helpers
{
using System;
using JWLMerge.BackupFileServices;
using BackupFileServices;
using JWLMerge.BackupFileServices.Models.ManifestFile;
using JWLMerge.Models;
using Models;
internal static class DesignTimeFileCreation
{
@@ -20,11 +20,9 @@
DeviceName = "MYPC",
};
return new JwLibraryFile
{
FilePath = "c:\\temp\\myfile.jwlibrary",
BackupFile = file,
};
#pragma warning disable S1075 // URIs should not be hardcoded
return new JwLibraryFile("c:\\temp\\myfile.jwlibrary", file);
#pragma warning restore S1075 // URIs should not be hardcoded
}
private static string GenerateDateString(DateTime dateTime)

View File

@@ -1,8 +1,8 @@
namespace JWLMerge.Helpers
{
using JWLMerge.BackupFileServices;
using BackupFileServices;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using JWLMerge.Models;
using Models;
internal static class MergePreparation
{

View File

@@ -3,29 +3,24 @@
using System.Collections.Generic;
using System.Linq;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using JWLMerge.Models;
using Models;
internal static class PublicationHelper
{
public static PublicationDef[] GetPublications(List<Location> locations, List<UserMark> userMarks, bool includeAllPublicationsItem)
public static PublicationDef[] GetPublications(
List<Location> locations, List<UserMark> userMarks, bool includeAllPublicationsItem)
{
var locationsThatAreMarked = userMarks.Select(x => x.LocationId).ToHashSet();
var result = locations
.Where(x => locationsThatAreMarked.Contains(x.LocationId))
.Select(x => x.KeySymbol).Distinct().Select(
x => new PublicationDef
{
KeySymbol = x,
}).OrderBy(x => x.KeySymbol).ToList();
.Where(x => locationsThatAreMarked.Contains(x.LocationId) && !string.IsNullOrEmpty(x.KeySymbol))
.Select(x => x.KeySymbol).Distinct()
.Select(x => new PublicationDef(x!, false))
.OrderBy(x => x.KeySymbol).ToList();
if (includeAllPublicationsItem)
{
result.Insert(0, new PublicationDef
{
IsAllPublicationsSymbol = true,
KeySymbol = "All Publications",
});
result.Insert(0, new PublicationDef("All Publications", true));
}
return result.ToArray();

View File

@@ -8,25 +8,26 @@
internal static class VersionDetection
{
public static Version GetLatestReleaseVersion(string latestReleaseUrl)
public static Version? GetLatestReleaseVersion(string latestReleaseUrl)
{
string version = null;
string? version = null;
try
{
using (var client = new HttpClient())
#pragma warning disable U2U1025 // Avoid instantiating HttpClient
using var client = new HttpClient();
#pragma warning restore U2U1025 // Avoid instantiating HttpClient
var response = client.GetAsync(new Uri(latestReleaseUrl)).Result;
if (response.IsSuccessStatusCode)
{
var response = client.GetAsync(new Uri(latestReleaseUrl)).Result;
if (response.IsSuccessStatusCode)
var latestVersionUri = response.RequestMessage?.RequestUri;
if (latestVersionUri != null)
{
var latestVersionUri = response.RequestMessage.RequestUri;
if (latestVersionUri != null)
var segments = latestVersionUri.Segments;
if (segments.Any())
{
var segments = latestVersionUri.Segments;
if (segments.Any())
{
version = segments[segments.Length - 1];
}
version = segments[segments.Length - 1];
}
}
}
@@ -42,7 +43,9 @@
public static Version GetCurrentVersion()
{
var ver = Assembly.GetExecutingAssembly().GetName().Version;
return new Version($"{ver.Major}.{ver.Minor}.{ver.Build}.{ver.Revision}");
return ver != null
? new Version($"{ver.Major}.{ver.Minor}.{ver.Build}.{ver.Revision}")
: new Version();
}
}
}

View File

@@ -13,6 +13,10 @@
<Compile Include="..\SolutionInfo.cs" Link="Properties\SolutionInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MaterialDesignColors" Version="2.0.1" />
<PackageReference Include="MaterialDesignThemes" Version="4.1.0" />

View File

@@ -2,7 +2,7 @@
{
using System.ComponentModel;
using System.Windows;
using JWLMerge.Messages;
using Messages;
using Microsoft.Toolkit.Mvvm.Messaging;
/// <summary>
@@ -17,17 +17,17 @@
private void PanelOnDragOver(object sender, DragEventArgs e)
{
WeakReferenceMessenger.Default.Send(new DragOverMessage { DragEventArgs = e });
WeakReferenceMessenger.Default.Send(new DragOverMessage(e));
}
private void PanelOnDrop(object sender, DragEventArgs e)
{
WeakReferenceMessenger.Default.Send(new DragDropMessage { DragEventArgs = e });
WeakReferenceMessenger.Default.Send(new DragDropMessage(e));
}
private void MainWindowOnClosing(object sender, CancelEventArgs e)
{
WeakReferenceMessenger.Default.Send(new MainWindowClosingMessage { CancelEventArgs = e });
WeakReferenceMessenger.Default.Send(new MainWindowClosingMessage(e));
}
}
}

View File

@@ -2,8 +2,13 @@
{
using System.Windows;
internal class DragDropMessage
internal sealed class DragDropMessage
{
public DragEventArgs DragEventArgs { get; set; }
public DragDropMessage(DragEventArgs args)
{
DragEventArgs = args;
}
public DragEventArgs DragEventArgs { get; }
}
}

View File

@@ -2,8 +2,13 @@
{
using System.Windows;
internal class DragOverMessage
internal sealed class DragOverMessage
{
public DragEventArgs DragEventArgs { get; set; }
public DragOverMessage(DragEventArgs args)
{
DragEventArgs = args;
}
public DragEventArgs DragEventArgs { get; }
}
}

View File

@@ -2,8 +2,13 @@
{
using System.ComponentModel;
internal class MainWindowClosingMessage
internal sealed class MainWindowClosingMessage
{
public CancelEventArgs CancelEventArgs { get; set; }
public MainWindowClosingMessage(CancelEventArgs args)
{
CancelEventArgs = args;
}
public CancelEventArgs CancelEventArgs { get; }
}
}

View File

@@ -1,8 +1,8 @@
namespace JWLMerge.Models
{
internal class ColorResult
internal sealed class ColorResult
{
public int[] ColourIndexes { get; set; }
public int[]? ColourIndexes { get; set; }
public bool RemoveNotes { get; set; }
}

View File

@@ -2,7 +2,7 @@
{
using System.Windows.Media;
internal class ColourDef
internal sealed class ColourDef
{
public ColourDef(int colourIndex, string name, string rgb)
{
@@ -11,11 +11,11 @@
RgbString = rgb;
}
public int ColourIndex { get; set; }
public int ColourIndex { get; }
public string Name { get; set; }
public string Name { get; }
public string RgbString { get; set; }
public string RgbString { get; }
public Color Color => (Color)ColorConverter.ConvertFromString(RgbString);
}

View File

@@ -3,15 +3,22 @@
using Microsoft.Toolkit.Mvvm.ComponentModel;
using System.Windows.Media;
internal class ColourListItem : ObservableObject
internal sealed class ColourListItem : ObservableObject
{
private bool _isChecked;
public string Name { get; set; }
public ColourListItem(string name, int id, Color color)
{
Name = name;
Id = id;
Color = color;
}
public int Id { get; set; }
public string Name { get; }
public Color Color { get; set; }
public int Id { get; }
public Color Color { get; }
public bool IsChecked
{

View File

@@ -1,10 +1,15 @@
namespace JWLMerge.Models
{
internal class DataTypeListItem
internal sealed class DataTypeListItem
{
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public string Caption { get; set; }
public DataTypeListItem(string caption, JwLibraryFileDataTypes dataType)
{
Caption = caption;
DataType = dataType;
}
public JwLibraryFileDataTypes DataType { get; set; }
public string Caption { get; }
public JwLibraryFileDataTypes DataType { get; }
}
}

View File

@@ -1,9 +1,15 @@
namespace JWLMerge.Models
{
internal class FileFormatErrorListItem
internal sealed class FileFormatErrorListItem
{
public string Filename { get; set; }
public FileFormatErrorListItem(string filename, string errorMsg)
{
Filename = filename;
ErrorMsg = errorMsg;
}
public string ErrorMsg { get; set; }
public string Filename { get; }
public string ErrorMsg { get; }
}
}

View File

@@ -1,19 +1,25 @@
namespace JWLMerge.Models
using System.Diagnostics.CodeAnalysis;
namespace JWLMerge.Models
{
using System.Text;
using JWLMerge.BackupFileServices.Models;
using Microsoft.Toolkit.Mvvm.ComponentModel;
internal class JwLibraryFile : ObservableObject
internal sealed class JwLibraryFile : ObservableObject
{
public JwLibraryFile()
public JwLibraryFile(string filePath, BackupFile backupFile)
{
FilePath = filePath;
BackupFile = backupFile;
MergeParameters = new MergeParameters();
MergeParameters.PropertyChanged += MergeParametersPropertyChanged;
}
public string FilePath { get; set; }
public string FilePath { get; }
[DisallowNull]
public BackupFile BackupFile { get; set; }
public MergeParameters MergeParameters { get; }
@@ -40,7 +46,7 @@
OnPropertyChanged(nameof(TooltipSummaryText));
}
private void MergeParametersPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
private void MergeParametersPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged(nameof(MergeParameters));
}

View File

@@ -1,8 +1,8 @@
namespace JWLMerge.Models
{
internal class PubColourResult
internal sealed class PubColourResult
{
public string PublicationSymbol { get; set; }
public string? PublicationSymbol { get; set; }
public int ColorIndex { get; set; }

View File

@@ -1,9 +1,15 @@
namespace JWLMerge.Models
{
internal class PublicationDef
internal sealed class PublicationDef
{
public string KeySymbol { get; set; }
public PublicationDef(string keySymbol, bool isAllPubSymbol)
{
KeySymbol = keySymbol;
IsAllPublicationsSymbol = isAllPubSymbol;
}
public bool IsAllPublicationsSymbol { get; set; }
public string KeySymbol { get; }
public bool IsAllPublicationsSymbol { get; }
}
}

View File

@@ -2,13 +2,19 @@
{
using Microsoft.Toolkit.Mvvm.ComponentModel;
internal class TagListItem : ObservableObject
internal sealed class TagListItem : ObservableObject
{
private bool _isChecked;
public string Name { get; set; }
public TagListItem(string name, int id)
{
Name = name;
Id = id;
}
public int Id { get; set; }
public string Name { get; }
public int Id { get; }
public bool IsChecked
{

View File

@@ -4,16 +4,16 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using JWLMerge.BackupFileServices.Exceptions;
using BackupFileServices.Exceptions;
using JWLMerge.BackupFileServices.Models;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using JWLMerge.Dialogs;
using JWLMerge.Models;
using JWLMerge.ViewModel;
using Dialogs;
using Models;
using ViewModel;
using MaterialDesignThemes.Wpf;
// ReSharper disable once ClassNeverInstantiated.Global
internal class DialogService : IDialogService
internal sealed class DialogService : IDialogService
{
public const int UntaggedItemId = -1;
private const string NotTaggedString = "** Not Tagged **";
@@ -36,16 +36,13 @@
switch (e)
{
case WrongDatabaseVersionException dbVerEx:
dc.Errors.Add(new FileFormatErrorListItem
{ Filename = dbVerEx.Filename, ErrorMsg = dbVerEx.Message });
dc.Errors.Add(new FileFormatErrorListItem(dbVerEx.Filename ?? "Error", dbVerEx.Message));
break;
case WrongManifestVersionException mftVerEx:
dc.Errors.Add(new FileFormatErrorListItem
{ Filename = mftVerEx.Filename, ErrorMsg = mftVerEx.Message });
dc.Errors.Add(new FileFormatErrorListItem(mftVerEx.Filename ?? "Error", mftVerEx.Message));
break;
default:
dc.Errors.Add(new FileFormatErrorListItem
{ Filename = "Error", ErrorMsg = bex.Message });
dc.Errors.Add(new FileFormatErrorListItem("Error", bex.Message));
break;
}
}
@@ -53,10 +50,7 @@
await DialogHost.Show(
dialog,
(object sender, DialogClosingEventArgs args) =>
{
_isDialogVisible = false;
}).ConfigureAwait(false);
(object sender, DialogClosingEventArgs args) => _isDialogVisible = false).ConfigureAwait(false);
}
public async Task<bool> ShouldRemoveFavouritesAsync()
@@ -102,20 +96,12 @@
if (includeUntaggedNotes)
{
dc.TagItems.Add(new TagListItem
{
Id = UntaggedItemId,
Name = NotTaggedString,
});
dc.TagItems.Add(new TagListItem(NotTaggedString, UntaggedItemId));
}
foreach (var tag in tags)
{
dc.TagItems.Add(new TagListItem
{
Id = tag.TagId,
Name = tag.Name,
});
dc.TagItems.Add(new TagListItem(tag.Name, tag.TagId));
}
await DialogHost.Show(
@@ -147,12 +133,7 @@
foreach (var c in colours)
{
dc.ColourItems.Add(new ColourListItem
{
Id = c.ColourIndex,
Name = c.Name,
Color = c.Color,
});
dc.ColourItems.Add(new ColourListItem(c.Name, c.ColourIndex, c.Color));
}
await DialogHost.Show(
@@ -167,7 +148,7 @@
};
}
public async Task<PubColourResult> GetPubAndColourSelectionForUnderlineRemovalAsync(PublicationDef[] pubs, ColourDef[] colors)
public async Task<PubColourResult?> GetPubAndColourSelectionForUnderlineRemovalAsync(PublicationDef[] pubs, ColourDef[] colors)
{
_isDialogVisible = true;
@@ -180,12 +161,7 @@
foreach (var c in colors)
{
dc.ColourItems.Add(new ColourListItem
{
Id = c.ColourIndex,
Name = c.Name,
Color = c.Color,
});
dc.ColourItems.Add(new ColourListItem(c.Name, c.ColourIndex, c.Color));
}
foreach (var p in pubs)
@@ -201,7 +177,7 @@
return dc.Result;
}
public async Task<ImportBibleNotesParams> GetImportBibleNotesParamsAsync(IReadOnlyCollection<Tag> databaseTags)
public async Task<ImportBibleNotesParams?> GetImportBibleNotesParamsAsync(IReadOnlyCollection<Tag> databaseTags)
{
_isDialogVisible = true;

View File

@@ -6,7 +6,7 @@
using System.Windows;
// ReSharper disable once ClassNeverInstantiated.Global
internal class DragDropService : IDragDropService
internal sealed class DragDropService : IDragDropService
{
/// <summary>
/// Determines whether we can accept the drag and drop operation
@@ -42,7 +42,7 @@
{
foreach (var filePath in dataObject.GetFileDropList())
{
if (IsJwLibraryFile(filePath))
if (IsJwLibraryFile(filePath) && !string.IsNullOrEmpty(filePath))
{
result.Add(filePath);
}
@@ -52,10 +52,15 @@
return result;
}
private bool IsJwLibraryFile(string filePath)
private static bool IsJwLibraryFile(string? filePath)
{
if (string.IsNullOrEmpty(filePath))
{
return false;
}
var ext = Path.GetExtension(filePath);
return ext != null && ext.Equals(".jwlibrary", StringComparison.OrdinalIgnoreCase);
return ext.Equals(".jwlibrary", StringComparison.OrdinalIgnoreCase);
}
}
}

View File

@@ -5,15 +5,15 @@
using Microsoft.Win32;
// ReSharper disable once ClassNeverInstantiated.Global
internal class FileOpenSaveService : IFileOpenSaveService
internal sealed class FileOpenSaveService : IFileOpenSaveService
{
private static string SaveDirectory { get; set; }
private static string? SaveDirectory { get; set; }
private static string ImportDirectory { get; set; }
private static string? ImportDirectory { get; set; }
private static string ExportDirectory { get; set; }
private static string? ExportDirectory { get; set; }
public string GetBibleNotesExportFilePath(string title)
public string? GetBibleNotesExportFilePath(string title)
{
var saveFileDialog = new SaveFileDialog
{
@@ -32,7 +32,7 @@
return null;
}
public string GetBibleNotesImportFilePath(string title)
public string? GetBibleNotesImportFilePath(string title)
{
var openFileDialog = new OpenFileDialog
{
@@ -53,7 +53,7 @@
return null;
}
public string GetSaveFilePath(string title)
public string? GetSaveFilePath(string title)
{
var saveFileDialog = new SaveFileDialog
{
@@ -72,7 +72,7 @@
return null;
}
private string GetJWLMergeDocsFolder()
private static string GetJWLMergeDocsFolder()
{
var folder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "JWLMerge");
if (!Directory.Exists(folder))
@@ -83,17 +83,17 @@
return folder;
}
private string GetDefaultSaveFolder()
private static string GetDefaultSaveFolder()
{
return GetJWLMergeDocsFolder();
}
private string GetDefaultExportFolder()
private static string GetDefaultExportFolder()
{
return GetJWLMergeDocsFolder();
}
private string GetDefaultImportFolder()
private static string GetDefaultImportFolder()
{
var folder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
if (!Directory.Exists(folder))

View File

@@ -5,7 +5,7 @@
using System.Threading.Tasks;
using JWLMerge.BackupFileServices.Models;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using JWLMerge.Models;
using Models;
internal interface IDialogService
{
@@ -15,13 +15,13 @@
Task<bool> ShouldRemoveFavouritesAsync();
Task<ImportBibleNotesParams> GetImportBibleNotesParamsAsync(IReadOnlyCollection<Tag> databaseTags);
Task<ImportBibleNotesParams?> GetImportBibleNotesParamsAsync(IReadOnlyCollection<Tag> databaseTags);
Task<NotesByTagResult> GetTagSelectionForNotesRemovalAsync(Tag[] tags, bool includeUntaggedNotes);
Task<ColorResult> GetColourSelectionForUnderlineRemovalAsync(ColourDef[] colours);
Task<PubColourResult> GetPubAndColourSelectionForUnderlineRemovalAsync(PublicationDef[] pubs, ColourDef[] colors);
Task<PubColourResult?> GetPubAndColourSelectionForUnderlineRemovalAsync(PublicationDef[] pubs, ColourDef[] colors);
bool IsDialogVisible();
}

View File

@@ -2,10 +2,10 @@
{
internal interface IFileOpenSaveService
{
string GetSaveFilePath(string title);
string? GetSaveFilePath(string title);
string GetBibleNotesImportFilePath(string title);
string? GetBibleNotesImportFilePath(string title);
string GetBibleNotesExportFilePath(string title);
string? GetBibleNotesExportFilePath(string title);
}
}

View File

@@ -12,7 +12,7 @@
void Enqueue(
object content,
object actionContent,
Action<object> actionHandler,
Action<object?> actionHandler,
object actionArgument,
bool promote,
bool neverConsiderToBeDuplicate);

View File

@@ -1,8 +1,8 @@
namespace JWLMerge.Services
{
internal class NotesByTagResult
internal sealed class NotesByTagResult
{
public int[] TagIds { get; set; }
public int[]? TagIds { get; set; }
public bool RemoveUntaggedNotes { get; set; }

View File

@@ -16,7 +16,7 @@
public void Enqueue(
object content,
object actionContent,
Action<object> actionHandler,
Action<object?> actionHandler,
object actionArgument,
bool promote,
bool neverConsiderToBeDuplicate)
@@ -54,7 +54,7 @@
public void Dispose()
{
((SnackbarMessageQueue)TheSnackbarMessageQueue)?.Dispose();
((SnackbarMessageQueue)TheSnackbarMessageQueue).Dispose();
}
}
}

View File

@@ -4,11 +4,11 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using JWLMerge.BackupFileServices;
using JWLMerge.ViewModel;
using BackupFileServices;
using ViewModel;
// ReSharper disable once ClassNeverInstantiated.Global
internal class WindowService : IWindowService
internal sealed class WindowService : IWindowService
{
private readonly List<DetailWindow> _detailWindows;
@@ -67,9 +67,9 @@
return window;
}
private void DetailWindowClosed(object sender, EventArgs e)
private void DetailWindowClosed(object? sender, EventArgs e)
{
var window = (DetailWindow)sender;
var window = (DetailWindow?)sender;
if (window != null)
{
_detailWindows.Remove(window);
@@ -77,12 +77,17 @@
}
}
private DetailWindow GetDetailWindow(string filePath)
private DetailWindow? GetDetailWindow(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
return null;
}
foreach (var window in _detailWindows)
{
var path = ((DetailViewModel)window.DataContext).FilePath;
if (IsSameFile(path, filePath))
if (!string.IsNullOrEmpty(path) && IsSameFile(path, filePath))
{
return window;
}
@@ -91,7 +96,7 @@
return null;
}
private bool IsSameFile(string path1, string path2)
private static bool IsSameFile(string path1, string path2)
{
return Path.GetFullPath(path1).Equals(Path.GetFullPath(path2), StringComparison.OrdinalIgnoreCase);
}

View File

@@ -1,19 +1,19 @@
namespace JWLMerge.ViewModel
{
using System.Collections.Generic;
using JWLMerge.Models;
using Models;
using MaterialDesignThemes.Wpf;
using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
internal class BackupFileFormatErrorViewModel : ObservableObject
internal sealed class BackupFileFormatErrorViewModel : ObservableObject
{
public BackupFileFormatErrorViewModel()
{
OkCommand = new RelayCommand(Ok);
}
public List<FileFormatErrorListItem> Errors { get; } = new List<FileFormatErrorListItem>();
public List<FileFormatErrorListItem> Errors { get; } = new();
public RelayCommand OkCommand { get; }

View File

@@ -5,25 +5,25 @@
using JWLMerge.BackupFileServices.Models;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using JWLMerge.BackupFileServices.Models.ManifestFile;
using JWLMerge.Models;
using Models;
using Microsoft.Toolkit.Mvvm.ComponentModel;
// ReSharper disable once ClassNeverInstantiated.Global
internal class DetailViewModel : ObservableObject
internal sealed class DetailViewModel : ObservableObject
{
private DataTypeListItem _selectedDataType;
private DataTypeListItem? _selectedDataType;
private bool _notesRedacted;
private string _windowTitle;
private BackupFile _backupFile;
private string? _windowTitle;
private BackupFile? _backupFile;
public DetailViewModel()
{
ListItems = CreateListItems();
}
public string FilePath { get; set; }
public string? FilePath { get; set; }
public BackupFile BackupFile
public BackupFile? BackupFile
{
get => _backupFile;
set
@@ -36,13 +36,13 @@
}
}
}
public List<DataTypeListItem> ListItems { get; }
public string WindowTitle
{
get => _windowTitle ?? string.Empty;
set => SetProperty(ref _windowTitle, value);
private set => SetProperty(ref _windowTitle, value);
}
public bool NotesRedacted
@@ -61,7 +61,7 @@
public bool NotesNotRedacted => !NotesRedacted;
public DataTypeListItem SelectedDataType
public DataTypeListItem? SelectedDataType
{
get => _selectedDataType;
set
@@ -76,7 +76,9 @@
}
}
public IEnumerable DataItemsSource
#pragma warning disable U2U1200 // Prefer generic collections over non-generic ones
public IEnumerable? DataItemsSource
#pragma warning restore U2U1200 // Prefer generic collections over non-generic ones
{
get
{
@@ -118,13 +120,17 @@
}
}
#pragma warning disable S1168 // Empty arrays and collections should be returned instead of null
return null;
#pragma warning restore S1168 // Empty arrays and collections should be returned instead of null
}
}
public bool IsNotesItemSelected => SelectedDataType.DataType == JwLibraryFileDataTypes.Note;
private IEnumerable ManifestAsItemsSource(Manifest manifest)
public bool IsNotesItemSelected => SelectedDataType?.DataType == JwLibraryFileDataTypes.Note;
#pragma warning disable U2U1011 // Return types should be specific
private static IEnumerable ManifestAsItemsSource(Manifest? manifest)
#pragma warning restore U2U1011 // Return types should be specific
{
var result = new List<KeyValuePair<string, string>>();
@@ -144,20 +150,20 @@
return result;
}
private List<DataTypeListItem> CreateListItems()
private static List<DataTypeListItem> CreateListItems()
{
return new List<DataTypeListItem>
return new()
{
new DataTypeListItem { Caption = "Manifest", DataType = JwLibraryFileDataTypes.Manifest },
new DataTypeListItem { Caption = "Block Range", DataType = JwLibraryFileDataTypes.BlockRange },
new DataTypeListItem { Caption = "Bookmark", DataType = JwLibraryFileDataTypes.Bookmark },
new DataTypeListItem { Caption = "InputField", DataType = JwLibraryFileDataTypes.InputField },
new DataTypeListItem { Caption = "Last Modified", DataType = JwLibraryFileDataTypes.LastModified },
new DataTypeListItem { Caption = "Location", DataType = JwLibraryFileDataTypes.Location },
new DataTypeListItem { Caption = "Note", DataType = JwLibraryFileDataTypes.Note },
new DataTypeListItem { Caption = "Tag", DataType = JwLibraryFileDataTypes.Tag },
new DataTypeListItem { Caption = "Tag Map", DataType = JwLibraryFileDataTypes.TagMap },
new DataTypeListItem { Caption = "User Mark", DataType = JwLibraryFileDataTypes.UserMark },
new("Manifest", JwLibraryFileDataTypes.Manifest),
new("Block Range", JwLibraryFileDataTypes.BlockRange),
new("Bookmark", JwLibraryFileDataTypes.Bookmark),
new("InputField", JwLibraryFileDataTypes.InputField),
new("Last Modified", JwLibraryFileDataTypes.LastModified),
new("Location", JwLibraryFileDataTypes.Location),
new("Note", JwLibraryFileDataTypes.Note),
new("Tag", JwLibraryFileDataTypes.Tag),
new("Tag Map", JwLibraryFileDataTypes.TagMap),
new("User Mark", JwLibraryFileDataTypes.UserMark),
};
}
}

View File

@@ -8,9 +8,9 @@
using Microsoft.Toolkit.Mvvm.Input;
// ReSharper disable once ClassNeverInstantiated.Global
internal class ImportBibleNotesViewModel : ObservableObject
internal sealed class ImportBibleNotesViewModel : ObservableObject
{
private IReadOnlyCollection<Tag> _tags;
private IReadOnlyCollection<Tag>? _tags;
public ImportBibleNotesViewModel()
{
@@ -18,13 +18,13 @@
CancelCommand = new RelayCommand(Cancel);
}
public ImportBibleNotesParams Result { get; private set; }
public ImportBibleNotesParams? Result { get; private set; }
public RelayCommand OkCommand { get; set; }
public RelayCommand CancelCommand { get; set; }
public IReadOnlyCollection<Tag> Tags
public IReadOnlyCollection<Tag>? Tags
{
get => _tags;
set

View File

@@ -9,14 +9,14 @@ namespace JWLMerge.ViewModel
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using JWLMerge.BackupFileServices;
using BackupFileServices;
using JWLMerge.BackupFileServices.Helpers;
using JWLMerge.BackupFileServices.Models.DatabaseModels;
using JWLMerge.ExcelServices;
using JWLMerge.Helpers;
using JWLMerge.Messages;
using JWLMerge.Models;
using JWLMerge.Services;
using ExcelServices;
using Helpers;
using Messages;
using Models;
using Services;
using MaterialDesignThemes.Wpf;
using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
@@ -24,7 +24,7 @@ namespace JWLMerge.ViewModel
using Serilog;
// ReSharper disable once ClassNeverInstantiated.Global
internal class MainViewModel : ObservableObject
internal sealed class MainViewModel : ObservableObject
{
private readonly string _latestReleaseUrl = Properties.Resources.LATEST_RELEASE_URL;
private readonly IDragDropService _dragDropService;
@@ -69,9 +69,9 @@ namespace JWLMerge.ViewModel
GetVersionData();
}
public ObservableCollection<JwLibraryFile> Files { get; } = new ObservableCollection<JwLibraryFile>();
public ObservableCollection<JwLibraryFile> Files { get; } = new();
public string Title { get; set; }
public string Title { get; private set; } = null!;
public bool FileListEmpty => Files.Count == 0;
@@ -86,8 +86,8 @@ namespace JWLMerge.ViewModel
OnPropertyChanged();
OnPropertyChanged(nameof(IsNotBusy));
MergeCommand?.NotifyCanExecuteChanged();
CloseCardCommand?.NotifyCanExecuteChanged();
MergeCommand.NotifyCanExecuteChanged();
CloseCardCommand.NotifyCanExecuteChanged();
}
}
}
@@ -113,31 +113,31 @@ namespace JWLMerge.ViewModel
public ISnackbarMessageQueue TheSnackbarMessageQueue => _snackbarService.TheSnackbarMessageQueue;
// commands...
public RelayCommand<string> CloseCardCommand { get; set; }
public RelayCommand<string> CloseCardCommand { get; private set; } = null!;
public RelayCommand<string> ShowDetailsCommand { get; set; }
public RelayCommand<string> ShowDetailsCommand { get; private set; } = null!;
public RelayCommand MergeCommand { get; set; }
public RelayCommand MergeCommand { get; private set; } = null!;
public RelayCommand HomepageCommand { get; set; }
public RelayCommand HomepageCommand { get; private set; } = null!;
public RelayCommand UpdateCommand { get; set; }
public RelayCommand UpdateCommand { get; private set; } = null!;
public RelayCommand<string> RemoveFavouritesCommand { get; set; }
public RelayCommand<string> RemoveFavouritesCommand { get; private set; } = null!;
public RelayCommand<string> RedactNotesCommand { get; set; }
public RelayCommand<string> RedactNotesCommand { get; private set; } = null!;
public RelayCommand<string> ImportBibleNotesCommand { get; set; }
public RelayCommand<string> ImportBibleNotesCommand { get; private set; } = null!;
public RelayCommand<string> ExportBibleNotesCommand { get; set; }
public RelayCommand<string> ExportBibleNotesCommand { get; private set; } = null!;
public RelayCommand<string> RemoveNotesByTagCommand { get; set; }
public RelayCommand<string> RemoveNotesByTagCommand { get; private set; } = null!;
public RelayCommand<string> RemoveUnderliningByColourCommand { get; set; }
public RelayCommand<string> RemoveUnderliningByColourCommand { get; private set; } = null!;
public RelayCommand<string> RemoveUnderliningByPubAndColourCommand { get; set; }
public RelayCommand<string> RemoveUnderliningByPubAndColourCommand { get; private set; } = null!;
private JwLibraryFile GetFile(string filePath)
private JwLibraryFile? GetFile(string? filePath)
{
var file = Files.SingleOrDefault(x => x.FilePath.Equals(filePath));
if (file == null)
@@ -158,34 +158,38 @@ namespace JWLMerge.ViewModel
}
private void FilesCollectionChanged(
object sender,
object? sender,
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(nameof(FileListEmpty));
OnPropertyChanged(nameof(MergeCommandCaption));
MergeCommand?.NotifyCanExecuteChanged();
MergeCommand.NotifyCanExecuteChanged();
}
private void InitCommands()
{
CloseCardCommand = new RelayCommand<string>(RemoveCard, filePath => !IsBusy && !_dialogService.IsDialogVisible());
ShowDetailsCommand = new RelayCommand<string>(ShowDetails, filePath => !IsBusy);
CloseCardCommand = new RelayCommand<string>(RemoveCard, _ => !IsBusy && !_dialogService.IsDialogVisible());
ShowDetailsCommand = new RelayCommand<string>(ShowDetails, _ => !IsBusy);
MergeCommand = new RelayCommand(MergeFiles, () => GetMergeableFileCount() > 0 && !IsBusy && !_dialogService.IsDialogVisible());
HomepageCommand = new RelayCommand(LaunchHomepage);
UpdateCommand = new RelayCommand(LaunchLatestReleasePage);
RemoveFavouritesCommand = new RelayCommand<string>(async (filePath) => await RemoveFavouritesAsync(filePath), filePath => !IsBusy);
RedactNotesCommand = new RelayCommand<string>(async (filePath) => await RedactNotesAsync(filePath), filePath => !IsBusy);
ImportBibleNotesCommand = new RelayCommand<string>(async (filePath) => await ImportBibleNotesAsync(filePath), filePath => !IsBusy);
ExportBibleNotesCommand = new RelayCommand<string>(async (filePath) => await ExportBibleNotesAsync(filePath), filePath => !IsBusy);
RemoveNotesByTagCommand = new RelayCommand<string>(async (filePath) => await RemoveNotesByTagAsync(filePath), filePath => !IsBusy);
RemoveUnderliningByColourCommand = new RelayCommand<string>(async (filePath) => await RemoveUnderliningByColourAsync(filePath), filePath => !IsBusy);
RemoveUnderliningByPubAndColourCommand = new RelayCommand<string>(async (filePath) => await RemoveUnderliningByPubAndColourAsync(filePath), filePath => !IsBusy);
RemoveFavouritesCommand = new RelayCommand<string>(async filePath => await RemoveFavouritesAsync(filePath), _ => !IsBusy);
RedactNotesCommand = new RelayCommand<string>(async filePath => await RedactNotesAsync(filePath), _ => !IsBusy);
ImportBibleNotesCommand = new RelayCommand<string>(async filePath => await ImportBibleNotesAsync(filePath), _ => !IsBusy);
ExportBibleNotesCommand = new RelayCommand<string>(async filePath => await ExportBibleNotesAsync(filePath), _ => !IsBusy);
RemoveNotesByTagCommand = new RelayCommand<string>(async filePath => await RemoveNotesByTagAsync(filePath), _ => !IsBusy);
RemoveUnderliningByColourCommand = new RelayCommand<string>(async filePath => await RemoveUnderliningByColourAsync(filePath), _ => !IsBusy);
RemoveUnderliningByPubAndColourCommand = new RelayCommand<string>(async filePath => await RemoveUnderliningByPubAndColourAsync(filePath), _ => !IsBusy);
}
private async Task ExportBibleNotesAsync(string filePath)
private async Task ExportBibleNotesAsync(string? filePath)
{
var file = GetFile(filePath);
if (file == null)
{
return;
}
var bibleNotesExportFilePath = _fileOpenSaveService.GetBibleNotesExportFilePath("Bible Notes File");
if (string.IsNullOrWhiteSpace(bibleNotesExportFilePath))
@@ -217,9 +221,13 @@ namespace JWLMerge.ViewModel
}
}
private async Task ImportBibleNotesAsync(string filePath)
private async Task ImportBibleNotesAsync(string? filePath)
{
var file = GetFile(filePath);
if (file == null)
{
return;
}
var bibleNotesImportFilePath = _fileOpenSaveService.GetBibleNotesImportFilePath("Bible Notes File");
if (string.IsNullOrWhiteSpace(bibleNotesImportFilePath))
@@ -227,7 +235,7 @@ namespace JWLMerge.ViewModel
return;
}
var userDefinedTags = file.BackupFile.Database.Tags.Where(x => x.Type != 0)
var userDefinedTags = file!.BackupFile.Database.Tags.Where(x => x.Type != 0)
.OrderBy(x => x.Name)
.ToList();
@@ -256,7 +264,7 @@ namespace JWLMerge.ViewModel
_backupFileService.WriteNewDatabaseWithClean(file.BackupFile, file.FilePath, file.FilePath);
});
_windowService.Close(filePath);
_windowService.Close(filePath!);
_snackbarService.Enqueue("Bible notes imported successfully");
file.RefreshTooltipSummary();
@@ -272,9 +280,13 @@ namespace JWLMerge.ViewModel
}
}
private async Task RemoveUnderliningByPubAndColourAsync(string filePath)
private async Task RemoveUnderliningByPubAndColourAsync(string? filePath)
{
var file = GetFile(filePath);
if (file == null)
{
return;
}
var colors = ColourHelper.GetHighlighterColoursInUse(file.BackupFile.Database.UserMarks, true);
var pubs = PublicationHelper.GetPublications(file.BackupFile.Database.Locations, file.BackupFile.Database.UserMarks, true);
@@ -296,11 +308,11 @@ namespace JWLMerge.ViewModel
if (underliningRemoved > 0)
{
_backupFileService.WriteNewDatabaseWithClean(file.BackupFile, filePath, filePath);
_backupFileService.WriteNewDatabaseWithClean(file.BackupFile, filePath!, filePath!);
}
});
_windowService.Close(filePath);
_windowService.Close(filePath!);
_snackbarService.Enqueue(underliningRemoved == 0
? "There was no underlining to remove!"
@@ -319,9 +331,13 @@ namespace JWLMerge.ViewModel
}
}
private async Task RemoveUnderliningByColourAsync(string filePath)
private async Task RemoveUnderliningByColourAsync(string? filePath)
{
var file = GetFile(filePath);
if (file == null)
{
return;
}
if (!file.BackupFile.Database.UserMarks.Any())
{
@@ -349,11 +365,11 @@ namespace JWLMerge.ViewModel
if (underliningRemoved > 0)
{
_backupFileService.WriteNewDatabaseWithClean(file.BackupFile, filePath, filePath);
_backupFileService.WriteNewDatabaseWithClean(file.BackupFile, filePath!, filePath!);
}
});
_windowService.Close(filePath);
_windowService.Close(filePath!);
_snackbarService.Enqueue(underliningRemoved == 0
? "There was no underlining to remove!"
@@ -372,9 +388,13 @@ namespace JWLMerge.ViewModel
}
}
private async Task RemoveNotesByTagAsync(string filePath)
private async Task RemoveNotesByTagAsync(string? filePath)
{
var file = GetFile(filePath);
if (file == null)
{
return;
}
var tags = TagHelper.GetTagsInUseByNotes(file.BackupFile.Database);
@@ -403,11 +423,11 @@ namespace JWLMerge.ViewModel
if (notesRemovedCount > 0)
{
_backupFileService.WriteNewDatabaseWithClean(file.BackupFile, filePath, filePath);
_backupFileService.WriteNewDatabaseWithClean(file.BackupFile, filePath!, filePath!);
}
});
_windowService.Close(filePath);
_windowService.Close(filePath!);
_snackbarService.Enqueue(notesRemovedCount == 0
? "There were no notes to remove!"
@@ -426,18 +446,18 @@ namespace JWLMerge.ViewModel
}
}
private async Task RedactNotesAsync(string filePath)
private async Task RedactNotesAsync(string? filePath)
{
var file = GetFile(filePath);
var notes = file.BackupFile?.Database.Notes;
var notes = file?.BackupFile.Database.Notes;
if (notes == null || !notes.Any())
{
_snackbarService.Enqueue("No notes found");
return;
}
if (file.NotesRedacted)
if (file!.NotesRedacted)
{
_snackbarService.Enqueue("Notes already obfuscated");
return;
@@ -452,10 +472,10 @@ namespace JWLMerge.ViewModel
await Task.Run(() =>
{
count = _backupFileService.RedactNotes(file.BackupFile);
_backupFileService.WriteNewDatabase(file.BackupFile, filePath, filePath);
_backupFileService.WriteNewDatabase(file.BackupFile, filePath!, filePath!);
});
_windowService.Close(filePath);
_windowService.Close(filePath!);
file.NotesRedacted = true;
_snackbarService.Enqueue($"{count} Notes obfuscated successfully");
}
@@ -471,11 +491,11 @@ namespace JWLMerge.ViewModel
}
}
private async Task RemoveFavouritesAsync(string filePath)
private async Task RemoveFavouritesAsync(string? filePath)
{
var file = GetFile(filePath);
var favourites = file.BackupFile?.Database.TagMaps.Where(x => x.TagId == 1);
var favourites = file?.BackupFile.Database.TagMaps.Where(x => x.TagId == 1);
if (favourites == null || !favourites.Any())
{
_snackbarService.Enqueue("No favourites found");
@@ -489,14 +509,14 @@ namespace JWLMerge.ViewModel
{
await Task.Run(() =>
{
_backupFileService.RemoveFavourites(file.BackupFile);
_backupFileService.WriteNewDatabase(file.BackupFile, filePath, filePath);
_backupFileService.RemoveFavourites(file!.BackupFile);
_backupFileService.WriteNewDatabase(file.BackupFile, filePath!, filePath!);
});
_snackbarService.Enqueue("Favourites removed successfully");
_windowService.Close(filePath);
_windowService.Close(filePath!);
file.RefreshTooltipSummary();
file!.RefreshTooltipSummary();
}
catch (Exception ex)
{
@@ -568,13 +588,7 @@ namespace JWLMerge.ViewModel
// applying any merge parameters.
ReloadFiles();
}
}).ContinueWith(previousTask =>
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
IsBusy = false;
}));
});
}).ContinueWith(_ => Application.Current.Dispatcher.BeginInvoke(new Action(() => IsBusy = false)));
}
private void ApplyMergeParameters()
@@ -606,7 +620,7 @@ namespace JWLMerge.ViewModel
});
}
private string GetSuitableFilePathForSchema()
private string? GetSuitableFilePathForSchema()
{
foreach (var file in Files)
{
@@ -619,8 +633,13 @@ namespace JWLMerge.ViewModel
return null;
}
private void RemoveCard(string filePath)
private void RemoveCard(string? filePath)
{
if (string.IsNullOrEmpty(filePath))
{
return;
}
foreach (var file in Files)
{
if (IsSameFile(file.FilePath, filePath))
@@ -632,8 +651,13 @@ namespace JWLMerge.ViewModel
}
}
private void ShowDetails(string filePath)
private void ShowDetails(string? filePath)
{
if (string.IsNullOrEmpty(filePath))
{
return;
}
var file = GetFile(filePath);
if (file != null)
{
@@ -704,11 +728,7 @@ namespace JWLMerge.ViewModel
{
var backupFile = _backupFileService.Load(file);
tmpFilesCollection.Add(new JwLibraryFile
{
BackupFile = backupFile,
FilePath = file,
});
tmpFilesCollection.Add(new JwLibraryFile(file, backupFile));
});
foreach (var file in tmpFilesCollection)
@@ -721,14 +741,14 @@ namespace JWLMerge.ViewModel
}
}
private void FilePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
private void FilePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
// when the merge params are modified it can leave the number of mergeable items at less than 2.
MergeCommand?.NotifyCanExecuteChanged();
MergeCommand.NotifyCanExecuteChanged();
OnPropertyChanged(nameof(MergeCommandCaption));
}
private bool IsSameFile(string path1, string path2)
private static bool IsSameFile(string path1, string path2)
{
return Path.GetFullPath(path1).Equals(Path.GetFullPath(path2), StringComparison.OrdinalIgnoreCase);
}
@@ -767,7 +787,7 @@ namespace JWLMerge.ViewModel
}
}
private bool IsInDesignMode()
private static bool IsInDesignMode()
{
#if DEBUG
DependencyObject dep = new();

View File

@@ -5,7 +5,7 @@
using Microsoft.Toolkit.Mvvm.Input;
// ReSharper disable once ClassNeverInstantiated.Global
internal class RedactNotesPromptViewModel : ObservableObject
internal sealed class RedactNotesPromptViewModel : ObservableObject
{
public RedactNotesPromptViewModel()
{
@@ -13,9 +13,9 @@
NoCommand = new RelayCommand(No);
}
public RelayCommand YesCommand { get; set; }
public RelayCommand YesCommand { get; }
public RelayCommand NoCommand { get; set; }
public RelayCommand NoCommand { get; }
public bool Result { get; private set; }

View File

@@ -5,7 +5,7 @@
using Microsoft.Toolkit.Mvvm.Input;
// ReSharper disable once ClassNeverInstantiated.Global
internal class RemoveFavouritesPromptViewModel : ObservableObject
internal sealed class RemoveFavouritesPromptViewModel : ObservableObject
{
public RemoveFavouritesPromptViewModel()
{
@@ -13,9 +13,9 @@
NoCommand = new RelayCommand(No);
}
public RelayCommand YesCommand { get; set; }
public RelayCommand YesCommand { get; }
public RelayCommand NoCommand { get; set; }
public RelayCommand NoCommand { get; }
public bool Result { get; private set; }

View File

@@ -3,14 +3,14 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using JWLMerge.Models;
using JWLMerge.Services;
using Models;
using Services;
using MaterialDesignThemes.Wpf;
using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
// ReSharper disable once ClassNeverInstantiated.Global
internal class RemoveNotesByTagViewModel : ObservableObject
internal sealed class RemoveNotesByTagViewModel : ObservableObject
{
private bool _removeAssociatedUnderlining;
private bool _removeAssociatedTags;
@@ -23,13 +23,13 @@
TagItems.CollectionChanged += TagItemsCollectionChanged;
}
public RelayCommand OkCommand { get; set; }
public RelayCommand OkCommand { get; }
public RelayCommand CancelCommand { get; set; }
public RelayCommand CancelCommand { get; }
public int[] Result { get; private set; }
public int[]? Result { get; private set; }
public ObservableCollection<TagListItem> TagItems { get; } = new ObservableCollection<TagListItem>();
public ObservableCollection<TagListItem> TagItems { get; } = new();
public bool SelectionMade => TagItems.Any(x => x.IsChecked);
@@ -49,9 +49,9 @@
? "Remove associated Tags"
: "Remove associated Tag";
private void TagItemsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
private void TagItemsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null)
{
foreach (TagListItem item in e.NewItems)
{
@@ -65,7 +65,7 @@
return TagItems.Count(x => x.IsChecked && x.Id != DialogService.UntaggedItemId);
}
private void ItemPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
private void ItemPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged(nameof(SelectionMade));
OnPropertyChanged(nameof(RemoveTagsCaption));

View File

@@ -3,13 +3,13 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using JWLMerge.Models;
using Models;
using MaterialDesignThemes.Wpf;
using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
// ReSharper disable once ClassNeverInstantiated.Global
internal class RemoveUnderliningByColourViewModel : ObservableObject
internal sealed class RemoveUnderliningByColourViewModel : ObservableObject
{
private bool _removeAssociatedNotes;
@@ -21,15 +21,15 @@
ColourItems.CollectionChanged += ItemsCollectionChanged;
}
public RelayCommand OkCommand { get; set; }
public RelayCommand OkCommand { get; }
public RelayCommand CancelCommand { get; set; }
public RelayCommand CancelCommand { get; }
public ObservableCollection<ColourListItem> ColourItems { get; } = new ObservableCollection<ColourListItem>();
public ObservableCollection<ColourListItem> ColourItems { get; } = new();
public bool SelectionMade => ColourItems.Any(x => x.IsChecked);
public int[] Result { get; private set; }
public int[]? Result { get; private set; }
public bool RemoveAssociatedNotes
{
@@ -37,9 +37,9 @@
set => SetProperty(ref _removeAssociatedNotes, value);
}
private void ItemsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
private void ItemsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null)
{
foreach (ColourListItem item in e.NewItems)
{
@@ -48,7 +48,7 @@
}
}
private void ItemPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
private void ItemPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged(nameof(SelectionMade));
}

View File

@@ -1,16 +1,16 @@
namespace JWLMerge.ViewModel
{
using System.Collections.ObjectModel;
using JWLMerge.Models;
using Models;
using MaterialDesignThemes.Wpf;
using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
internal class RemoveUnderliningByPubAndColourViewModel : ObservableObject
internal sealed class RemoveUnderliningByPubAndColourViewModel : ObservableObject
{
private bool _removeAssociatedNotes;
private PublicationDef _selectedPublication;
private ColourListItem _selectedColour;
private PublicationDef? _selectedPublication;
private ColourListItem? _selectedColour;
public RemoveUnderliningByPubAndColourViewModel()
{
@@ -18,15 +18,15 @@
CancelCommand = new RelayCommand(Cancel);
}
public RelayCommand OkCommand { get; set; }
public RelayCommand OkCommand { get; }
public RelayCommand CancelCommand { get; set; }
public RelayCommand CancelCommand { get; }
public ObservableCollection<PublicationDef> PublicationList { get; } = new ObservableCollection<PublicationDef>();
public ObservableCollection<PublicationDef> PublicationList { get; } = new();
public ObservableCollection<ColourListItem> ColourItems { get; } = new ObservableCollection<ColourListItem>();
public ObservableCollection<ColourListItem> ColourItems { get; } = new();
public PublicationDef SelectedPublication
public PublicationDef? SelectedPublication
{
get => _selectedPublication;
set
@@ -40,7 +40,7 @@
}
}
public ColourListItem SelectedColour
public ColourListItem? SelectedColour
{
get => _selectedColour;
set
@@ -56,7 +56,7 @@
public bool SelectionMade => SelectedPublication != null && SelectedColour != null;
public PubColourResult Result { get; private set; }
public PubColourResult? Result { get; private set; }
public bool RemoveAssociatedNotes
{

View File

@@ -6,7 +6,7 @@ namespace JWLMerge.ViewModel
/// This class contains static references to all the view models in the
/// application and provides an entry point for the bindings.
/// </summary>
internal class ViewModelLocator
internal sealed class ViewModelLocator
{
public static MainViewModel Main => Ioc.Default.GetService<MainViewModel>()!;

View File

@@ -0,0 +1,7 @@
[*.cs]
# S112: General exceptions should never be thrown
dotnet_diagnostic.S112.severity = silent
# U2U1202: Use LINQ Count methods efficiently
dotnet_diagnostic.U2U1202.severity = silent

View File

@@ -12,7 +12,7 @@
AwaitingOutput,
}
public static CommandLineArgs Parse(string[] args)
public static CommandLineArgs? Parse(string[]? args)
{
if (args == null || args.Length < 2)
{

View File

@@ -1,9 +1,9 @@
namespace JWLMergeCLI.Args
{
internal class CommandLineArgs
internal sealed class CommandLineArgs
{
public string[] BackupFiles { get; set; }
public string[] BackupFiles { get; set; } = null!;
public string OutputFilePath { get; set; }
public string? OutputFilePath { get; set; }
}
}

View File

@@ -8,6 +8,10 @@
<ApplicationIcon>JWLMerge.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\SolutionInfo.cs" Link="Properties\SolutionInfo.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
using System.Linq;
using JWLMerge.BackupFileServices;
using JWLMerge.BackupFileServices.Events;
using JWLMergeCLI.Args;
using Args;
using Serilog;
/// <summary>
@@ -12,7 +12,7 @@
/// </summary>
internal sealed class MainApp
{
public event EventHandler<ProgressEventArgs> ProgressEvent;
public event EventHandler<ProgressEventArgs>? ProgressEvent;
/// <summary>
/// Runs the app.
@@ -20,7 +20,7 @@
/// <param name="args">Program arguments</param>
public void Run(CommandLineArgs args)
{
IBackupFileService backupFileService = new BackupFileService();
var backupFileService = new BackupFileService();
backupFileService.ProgressEvent += BackupFileServiceProgress;
var backup = backupFileService.Merge(args.BackupFiles);
@@ -32,7 +32,7 @@
OnProgressEvent(logMessage);
}
private void BackupFileServiceProgress(object sender, ProgressEventArgs e)
private void BackupFileServiceProgress(object? sender, ProgressEventArgs e)
{
OnProgressEvent(e);
}
@@ -44,7 +44,7 @@
private void OnProgressEvent(string message)
{
OnProgressEvent(new ProgressEventArgs { Message = message });
OnProgressEvent(new ProgressEventArgs(message));
}
}
}

View File

@@ -3,7 +3,7 @@
using System;
using System.Diagnostics;
using System.IO;
using JWLMergeCLI.Args;
using Args;
using Serilog;
public static class Program
@@ -71,7 +71,7 @@
{
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
return fvi.FileVersion;
return fvi.FileVersion ?? "Unknown";
}
private static void ShowUsage()
@@ -79,36 +79,36 @@
Console.ForegroundColor = ConsoleColor.DarkBlue;
Console.BackgroundColor = ConsoleColor.White;
Console.WriteLine();
Console.WriteLine($" JWLMergeCLI version {GetVersion()} ");
Console.WriteLine($@" JWLMergeCLI version {GetVersion()} ");
Console.WriteLine();
Console.ResetColor();
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine(" Description:");
Console.WriteLine(@" Description:");
Console.ResetColor();
Console.WriteLine(" JWLMergeCLI is used to merge the contents of 2 or more jwlibrary backup");
Console.WriteLine(" files. These files are produced by the JW Library backup command and");
Console.WriteLine(" contain your personal study notes and highlighting.");
Console.WriteLine(@" JWLMergeCLI is used to merge the contents of 2 or more jwlibrary backup");
Console.WriteLine(@" files. These files are produced by the JW Library backup command and");
Console.WriteLine(@" contain your personal study notes and highlighting.");
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine(" Usage:");
Console.WriteLine(@" Usage:");
Console.ResetColor();
Console.WriteLine(" JWLMergeCLI <jwlibrary file 1> <jwlibrary file 2>... [-o output file]");
Console.WriteLine(@" JWLMergeCLI <jwlibrary file 1> <jwlibrary file 2>... [-o output file]");
Console.WriteLine();
Console.WriteLine(" Note that you can optionally specify the full path and name of the merged");
Console.WriteLine(" file using the -o (or --output directive). If you omit it, the merged");
Console.WriteLine(" file is stored in the current folder.");
Console.WriteLine(@" Note that you can optionally specify the full path and name of the merged");
Console.WriteLine(@" file using the -o (or --output directive). If you omit it, the merged");
Console.WriteLine(@" file is stored in the current folder.");
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine(" An example:");
Console.WriteLine(@" An example:");
Console.ResetColor();
Console.WriteLine(" JWLMergeCLI \"C:\\Backup_PC16.jwlibrary\" \"C:\\Backup_iPad.jwlibrary\"");
Console.WriteLine(@" JWLMergeCLI ""C:\Backup_PC16.jwlibrary"" ""C:\Backup_iPad.jwlibrary""");
Console.WriteLine();
}
private static void AppProgress(object sender, JWLMerge.BackupFileServices.Events.ProgressEventArgs e)
private static void AppProgress(object? sender, JWLMerge.BackupFileServices.Events.ProgressEventArgs e)
{
Console.WriteLine(e.Message);
}