diff --git a/src/LogExpert.Core/Classes/Persister/ProjectData.cs b/src/LogExpert.Core/Classes/Persister/ProjectData.cs deleted file mode 100644 index 5a5abd3d..00000000 --- a/src/LogExpert.Core/Classes/Persister/ProjectData.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace LogExpert.Core.Classes.Persister; - -[Serializable] -public class ProjectData -{ - #region Fields - - /// - /// Gets or sets the list of members. - /// - public List FileNames { get; set; } = []; - - /// - /// Gets or sets the XML representation of the tab layout configuration. - /// - public string TabLayoutXml { get; set; } - - /// - /// Gets or sets the full file path to the project file. - /// - public string ProjectFilePath { get; set; } - - #endregion -} \ No newline at end of file diff --git a/src/LogExpert.Core/Classes/Persister/ProjectPersister.cs b/src/LogExpert.Core/Classes/Persister/ProjectPersister.cs deleted file mode 100644 index 877c6280..00000000 --- a/src/LogExpert.Core/Classes/Persister/ProjectPersister.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System.Text; - -using LogExpert.Core.Interfaces; - -using Newtonsoft.Json; - -using NLog; - -namespace LogExpert.Core.Classes.Persister; - -public static class ProjectPersister -{ - private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); - - #region Public methods - - /// - /// Loads the project session data from a specified file, including validation of referenced files. - /// Resolves .lxp persistence files to actual .log files before validation. - /// - /// The path to the project file (.lxj) - /// The plugin registry for file system validation - /// A containing the project data and validation results - public static ProjectLoadResult LoadProjectData (string projectFileName, IPluginRegistry pluginRegistry) - { - try - { - var settings = new JsonSerializerSettings - { - Formatting = Formatting.Indented, - }; - - var json = File.ReadAllText(projectFileName, Encoding.UTF8); - var projectData = JsonConvert.DeserializeObject(json, settings); - - // Set project file path for alternative file search - projectData.ProjectFilePath = projectFileName; - - // Resolve .lxp files to actual .log files - var resolvedFiles = ProjectFileResolver.ResolveProjectFiles(projectData, pluginRegistry); - - // Create mapping: logFile → originalFile - var logToOriginalMapping = new Dictionary(); - foreach (var (logFile, originalFile) in resolvedFiles) - { - logToOriginalMapping[logFile] = originalFile; - } - - // Create new ProjectData with resolved log file paths - var resolvedProjectData = new ProjectData - { - FileNames = [.. resolvedFiles.Select(r => r.LogFile)], - TabLayoutXml = projectData.TabLayoutXml, - ProjectFilePath = projectData.ProjectFilePath - }; - - // Validate the actual log files (not .lxp files) - var validationResult = ProjectFileValidator.ValidateProject(resolvedProjectData, pluginRegistry); - - return new ProjectLoadResult - { - ProjectData = resolvedProjectData, - ValidationResult = validationResult, - LogToOriginalFileMapping = logToOriginalMapping - }; - } - catch (Exception ex) when (ex is UnauthorizedAccessException or - IOException or - JsonSerializationException) - { - _logger.Warn($"Error loading persistence data from {projectFileName}, trying old xml version"); - - var projectData = ProjectPersisterXML.LoadProjectData(projectFileName); - - // Set project file path for alternative file search - projectData.ProjectFilePath = projectFileName; - - // Resolve .lxp files for XML fallback as well - var resolvedFiles = ProjectFileResolver.ResolveProjectFiles(projectData, pluginRegistry); - - var logToOriginalMapping = new Dictionary(); - foreach (var (logFile, originalFile) in resolvedFiles) - { - logToOriginalMapping[logFile] = originalFile; - } - - var resolvedProjectData = new ProjectData - { - FileNames = [.. resolvedFiles.Select(r => r.LogFile)], - TabLayoutXml = projectData.TabLayoutXml, - ProjectFilePath = projectData.ProjectFilePath - }; - - var validationResult = ProjectFileValidator.ValidateProject(resolvedProjectData, pluginRegistry); - - return new ProjectLoadResult - { - ProjectData = resolvedProjectData, - ValidationResult = validationResult, - LogToOriginalFileMapping = logToOriginalMapping - }; - } - } - - /// - /// Saves the specified project data to a file in JSON format. - /// - /// The method serializes the into a JSON string with indented - /// formatting and writes it to the specified using UTF-8 encoding. - /// The path to the file where the project data will be saved. Cannot be null or empty. - /// The project data to be serialized and saved. Cannot be null. - public static void SaveProjectData (string projectFileName, ProjectData projectData) - { - var settings = new JsonSerializerSettings - { - Formatting = Formatting.Indented, - }; - - try - { - var json = JsonConvert.SerializeObject(projectData, settings); - File.WriteAllText(projectFileName, json, Encoding.UTF8); - } - catch (Exception ex) when (ex is JsonSerializationException or - UnauthorizedAccessException or - IOException) - { - _logger.Error(ex, $"Error saving persistence data to {projectFileName}"); - } - } - - #endregion -} \ No newline at end of file diff --git a/src/LogExpert.Core/Classes/Persister/SessionData.cs b/src/LogExpert.Core/Classes/Persister/SessionData.cs new file mode 100644 index 00000000..001d6780 --- /dev/null +++ b/src/LogExpert.Core/Classes/Persister/SessionData.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; + +namespace LogExpert.Core.Classes.Persister; + +/// +/// Persisted workspace contents of a Session (.lxj): a list of log file paths +/// plus a tab/window layout XML blob. See CONTEXT.md "Sessions". +/// +[Serializable] +public class SessionData +{ + #region Fields + + /// + /// Gets or sets the list of log file paths included in this Session. + /// May contain references to Session File (.lxp) entries that resolve to logs. + /// + public List FileNames { get; set; } = []; + + /// + /// Gets or sets the XML representation of the tab layout configuration. + /// + public string TabLayoutXml { get; set; } + + /// + /// Gets or sets the full file path to the Session (.lxj) on disk. + /// + /// + /// JSON property name pinned to "SessionFilePath" so existing .lxj files (which + /// were written under the previous "Project" terminology) continue to deserialize + /// without migration. The value is also re-set at load time from the file path, + /// so even old files without this key load correctly. + /// + [JsonProperty("SessionFilePath")] + public string SessionFilePath { get; set; } + + #endregion +} diff --git a/src/LogExpert.Core/Classes/Persister/ProjectFileResolver.cs b/src/LogExpert.Core/Classes/Persister/SessionFileResolver.cs similarity index 58% rename from src/LogExpert.Core/Classes/Persister/ProjectFileResolver.cs rename to src/LogExpert.Core/Classes/Persister/SessionFileResolver.cs index 8ae60bba..6499ec99 100644 --- a/src/LogExpert.Core/Classes/Persister/ProjectFileResolver.cs +++ b/src/LogExpert.Core/Classes/Persister/SessionFileResolver.cs @@ -5,25 +5,25 @@ namespace LogExpert.Core.Classes.Persister; /// -/// Helper class to resolve project file references to actual log files. -/// Handles .lxp (persistence) files by extracting the actual log file path. +/// Helper class to resolve Session file references to actual log files. +/// Handles Session File (.lxp) entries by extracting the actual log file path. /// -public static class ProjectFileResolver +public static class SessionFileResolver { /// - /// Resolves project file names to actual log files. - /// If a file is a .lxp persistence file, extracts the log file path from it. + /// Resolves Session file names to actual log files. + /// If a file is a Session File (.lxp) entry, extracts the log file path from it. /// - /// The project data containing file references + /// The Session data containing file references /// Plugin registry for file system resolution (optional) /// List of tuples containing (logFilePath, originalFilePath) - public static ReadOnlyCollection<(string LogFile, string OriginalFile)> ResolveProjectFiles (ProjectData projectData, IPluginRegistry pluginRegistry = null) + public static ReadOnlyCollection<(string LogFile, string OriginalFile)> ResolveSessionFiles (SessionData sessionData, IPluginRegistry pluginRegistry = null) { - ArgumentNullException.ThrowIfNull(projectData); + ArgumentNullException.ThrowIfNull(sessionData); var resolved = new List<(string LogFile, string OriginalFile)>(); - foreach (var fileName in projectData.FileNames) + foreach (var fileName in sessionData.FileNames) { var logFile = PersisterHelpers.FindFilenameForSettings(fileName, pluginRegistry); resolved.Add((logFile, fileName)); @@ -31,4 +31,4 @@ public static class ProjectFileResolver return resolved.AsReadOnly(); } -} \ No newline at end of file +} diff --git a/src/LogExpert.Core/Classes/Persister/ProjectFileValidator.cs b/src/LogExpert.Core/Classes/Persister/SessionFileValidator.cs similarity index 90% rename from src/LogExpert.Core/Classes/Persister/ProjectFileValidator.cs rename to src/LogExpert.Core/Classes/Persister/SessionFileValidator.cs index dd8ebd0f..5344a94d 100644 --- a/src/LogExpert.Core/Classes/Persister/ProjectFileValidator.cs +++ b/src/LogExpert.Core/Classes/Persister/SessionFileValidator.cs @@ -12,7 +12,7 @@ namespace LogExpert.Core.Classes.Persister; /// path. It supports validation of both local file paths and URI-based files through plugin resolution. All methods are /// thread-safe and do not modify input data. Use the provided methods to check file existence, resolve canonical file /// paths, and locate possible alternatives for missing files. -public static class ProjectFileValidator +public static class SessionFileValidator { /// /// Validates the files referenced by the specified project and identifies missing or accessible files using @@ -21,21 +21,21 @@ public static class ProjectFileValidator /// Files are considered valid if they exist on disk or if a suitable file system plugin is /// available for URI-based files. For missing files, possible alternative paths are suggested based on the project /// file location. - /// The project data containing the list of file names to validate and the project file path. Cannot be null. + /// The project data containing the list of file names to validate and the project file path. Cannot be null. /// The plugin registry used to resolve file system plugins for URI-based files. Cannot be null. - /// A ProjectValidationResult containing lists of valid files, missing files, and possible alternative file paths + /// A SessionValidationResult containing lists of valid files, missing files, and possible alternative file paths /// for missing files. - public static ProjectValidationResult ValidateProject (ProjectData projectData, IPluginRegistry pluginRegistry) + public static SessionValidationResult ValidateSession (SessionData sessionData, IPluginRegistry pluginRegistry) { - ArgumentNullException.ThrowIfNull(projectData); + ArgumentNullException.ThrowIfNull(sessionData); ArgumentNullException.ThrowIfNull(pluginRegistry); - var result = new ProjectValidationResult(); + var result = new SessionValidationResult(); // Cache drive letters once to avoid repeated expensive DriveInfo.GetDrives() calls var cachedDriveLetters = GetFixedDriveLetters(); - foreach (var fileName in projectData.FileNames) + foreach (var fileName in sessionData.FileNames) { var normalizedPath = NormalizeFilePath(fileName); @@ -60,7 +60,7 @@ public static ProjectValidationResult ValidateProject (ProjectData projectData, { result.MissingFiles.Add(fileName); - var alternativePaths = FindAlternativePaths(fileName, projectData.ProjectFilePath, cachedDriveLetters); + var alternativePaths = FindAlternativePaths(fileName, sessionData.SessionFilePath, cachedDriveLetters); result.PossibleAlternatives[fileName] = alternativePaths; } } @@ -138,12 +138,12 @@ or DriveNotFoundException /// result. /// The name or path of the file to search for. Can be an absolute or relative path. Cannot be null, empty, or /// whitespace. - /// The full path to the project file used as a reference for searching related directories. Can be null or empty if + /// The full path to the project file used as a reference for searching related directories. Can be null or empty if /// project context is not available. /// Pre-computed list of fixed drive letters to avoid repeated DriveInfo.GetDrives() calls. /// A list of strings containing the full paths of files found that match the specified file name in alternative /// locations. The list will be empty if no matching files are found. - private static List FindAlternativePaths (string fileName, string projectFilePath, List cachedDriveLetters) + private static List FindAlternativePaths (string fileName, string sessionFilePath, List cachedDriveLetters) { var alternatives = new List(); @@ -160,21 +160,21 @@ private static List FindAlternativePaths (string fileName, string projec } // Search in directory of .lxj project file - if (!string.IsNullOrWhiteSpace(projectFilePath)) + if (!string.IsNullOrWhiteSpace(sessionFilePath)) { try { - var projectDir = Path.GetDirectoryName(projectFilePath); - if (!string.IsNullOrEmpty(projectDir) && Directory.Exists(projectDir)) + var sessionDir = Path.GetDirectoryName(sessionFilePath); + if (!string.IsNullOrEmpty(sessionDir) && Directory.Exists(sessionDir)) { - var candidatePath = Path.Join(projectDir, baseName); + var candidatePath = Path.Join(sessionDir, baseName); if (File.Exists(candidatePath)) { alternatives.Add(candidatePath); } // Also check subdirectories (one level deep) - var subdirs = Directory.GetDirectories(projectDir); + var subdirs = Directory.GetDirectories(sessionDir); alternatives.AddRange( subdirs .Select(subdir => Path.Join(subdir, baseName)) @@ -243,14 +243,14 @@ UnauthorizedAccessException or } // Try relative path resolution from project directory - if (!Path.IsPathRooted(fileName) && !string.IsNullOrWhiteSpace(projectFilePath)) + if (!Path.IsPathRooted(fileName) && !string.IsNullOrWhiteSpace(sessionFilePath)) { try { - var projectDir = Path.GetDirectoryName(projectFilePath); - if (!string.IsNullOrEmpty(projectDir)) + var sessionDir = Path.GetDirectoryName(sessionFilePath); + if (!string.IsNullOrEmpty(sessionDir)) { - var relativePath = Path.Join(projectDir, fileName); + var relativePath = Path.Join(sessionDir, fileName); var normalizedPath = Path.GetFullPath(relativePath); if (File.Exists(normalizedPath) && !alternatives.Contains(normalizedPath)) diff --git a/src/LogExpert.Core/Classes/Persister/ProjectLoadResult.cs b/src/LogExpert.Core/Classes/Persister/SessionLoadResult.cs similarity index 88% rename from src/LogExpert.Core/Classes/Persister/ProjectLoadResult.cs rename to src/LogExpert.Core/Classes/Persister/SessionLoadResult.cs index 83b15ceb..72a919e9 100644 --- a/src/LogExpert.Core/Classes/Persister/ProjectLoadResult.cs +++ b/src/LogExpert.Core/Classes/Persister/SessionLoadResult.cs @@ -3,17 +3,17 @@ namespace LogExpert.Core.Classes.Persister; /// /// Represents the result of loading a project file, including validation information. /// -public class ProjectLoadResult +public class SessionLoadResult { /// /// The loaded project data (contains resolved log file paths). /// - public ProjectData ProjectData { get; set; } + public SessionData SessionData { get; set; } /// /// Validation result containing valid, missing, and alternative file paths. /// - public ProjectValidationResult ValidationResult { get; set; } + public SessionValidationResult ValidationResult { get; set; } /// /// Mapping of original file references to resolved log files. diff --git a/src/LogExpert.Core/Classes/Persister/SessionPersister.cs b/src/LogExpert.Core/Classes/Persister/SessionPersister.cs new file mode 100644 index 00000000..7a26a38e --- /dev/null +++ b/src/LogExpert.Core/Classes/Persister/SessionPersister.cs @@ -0,0 +1,133 @@ +using System.Text; + +using LogExpert.Core.Interfaces; + +using Newtonsoft.Json; + +using NLog; + +namespace LogExpert.Core.Classes.Persister; + +public static class SessionPersister +{ + private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + + #region Public methods + + /// + /// Loads a Session (.lxj) from disk, including validation of referenced files. + /// Resolves Session File (.lxp) entries to actual .log files before validation. + /// + /// The path to the Session file (.lxj) + /// The plugin registry for file system validation + /// A containing the session data and validation results + public static SessionLoadResult LoadSessionData (string sessionFileName, IPluginRegistry pluginRegistry) + { + try + { + var settings = new JsonSerializerSettings + { + Formatting = Formatting.Indented, + }; + + var json = File.ReadAllText(sessionFileName, Encoding.UTF8); + var sessionData = JsonConvert.DeserializeObject(json, settings); + + // Set Session file path for alternative file search + sessionData.SessionFilePath = sessionFileName; + + // Resolve Session File (.lxp) entries to actual .log files + var resolvedFiles = SessionFileResolver.ResolveSessionFiles(sessionData, pluginRegistry); + + // Create mapping: logFile → originalFile + var logToOriginalMapping = new Dictionary(); + foreach (var (logFile, originalFile) in resolvedFiles) + { + logToOriginalMapping[logFile] = originalFile; + } + + // Create new SessionData with resolved log file paths + var resolvedSessionData = new SessionData + { + FileNames = [.. resolvedFiles.Select(r => r.LogFile)], + TabLayoutXml = sessionData.TabLayoutXml, + SessionFilePath = sessionData.SessionFilePath + }; + + // Validate the actual log files (not Session File .lxp entries) + var validationResult = SessionFileValidator.ValidateSession(resolvedSessionData, pluginRegistry); + + return new SessionLoadResult + { + SessionData = resolvedSessionData, + ValidationResult = validationResult, + LogToOriginalFileMapping = logToOriginalMapping + }; + } + catch (Exception ex) when (ex is UnauthorizedAccessException or + IOException or + JsonSerializationException) + { + _logger.Warn($"Error loading session data from {sessionFileName}, trying old xml version"); + + var sessionData = SessionPersisterXML.LoadSessionData(sessionFileName); + + // Set Session file path for alternative file search + sessionData.SessionFilePath = sessionFileName; + + // Resolve Session File (.lxp) entries for XML fallback as well + var resolvedFiles = SessionFileResolver.ResolveSessionFiles(sessionData, pluginRegistry); + + var logToOriginalMapping = new Dictionary(); + foreach (var (logFile, originalFile) in resolvedFiles) + { + logToOriginalMapping[logFile] = originalFile; + } + + var resolvedSessionData = new SessionData + { + FileNames = [.. resolvedFiles.Select(r => r.LogFile)], + TabLayoutXml = sessionData.TabLayoutXml, + SessionFilePath = sessionData.SessionFilePath + }; + + var validationResult = SessionFileValidator.ValidateSession(resolvedSessionData, pluginRegistry); + + return new SessionLoadResult + { + SessionData = resolvedSessionData, + ValidationResult = validationResult, + LogToOriginalFileMapping = logToOriginalMapping + }; + } + } + + /// + /// Saves the specified Session data to a file in JSON format. + /// + /// The method serializes the into a JSON string with indented + /// formatting and writes it to the specified using UTF-8 encoding. + /// The path to the file where the Session data will be saved. Cannot be null or empty. + /// The Session data to be serialized and saved. Cannot be null. + public static void SaveSessionData (string sessionFileName, SessionData sessionData) + { + var settings = new JsonSerializerSettings + { + Formatting = Formatting.Indented, + }; + + try + { + var json = JsonConvert.SerializeObject(sessionData, settings); + File.WriteAllText(sessionFileName, json, Encoding.UTF8); + } + catch (Exception ex) when (ex is JsonSerializationException or + UnauthorizedAccessException or + IOException) + { + _logger.Error(ex, $"Error saving session data to {sessionFileName}"); + } + } + + #endregion +} diff --git a/src/LogExpert.Core/Classes/Persister/ProjectPersisterXML.cs b/src/LogExpert.Core/Classes/Persister/SessionPersisterXML.cs similarity index 52% rename from src/LogExpert.Core/Classes/Persister/ProjectPersisterXML.cs rename to src/LogExpert.Core/Classes/Persister/SessionPersisterXML.cs index 618b925e..5e51cc4e 100644 --- a/src/LogExpert.Core/Classes/Persister/ProjectPersisterXML.cs +++ b/src/LogExpert.Core/Classes/Persister/SessionPersisterXML.cs @@ -4,50 +4,54 @@ namespace LogExpert.Core.Classes.Persister; -public static class ProjectPersisterXML +/// +/// Legacy XML loader for Session (.lxj) files written under the older XML format. +/// Used as a fallback when JSON deserialization fails. See . +/// +public static class SessionPersisterXML { private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); /// - /// Loads project data from the specified XML file. + /// Loads Session data from a legacy XML-format .lxj file. /// /// The method reads the XML file to extract file names and layout information. If the XML file /// contains a layout element, its inner XML is stored in the TabLayoutXml property of the returned - /// object. If any exception occurs during the loading process, an error is logged, and an - /// empty object is returned. - /// The path to the XML file containing the project data. - /// A object populated with file names and layout information from the XML file. If an - /// error occurs during loading, an empty object is returned. - public static ProjectData LoadProjectData (string projectFileName) + /// object. If any exception occurs during loading, an error is logged and an + /// empty object is returned. + /// The path to the XML file containing the Session data. + /// A object populated with file names and layout information from the XML file. If an + /// error occurs during loading, an empty object is returned. + public static SessionData LoadSessionData (string sessionFileName) { - var projectData = new ProjectData(); + var sessionData = new SessionData(); var xmlDoc = new XmlDocument(); try { - xmlDoc.Load(projectFileName); + xmlDoc.Load(sessionFileName); var fileList = xmlDoc.GetElementsByTagName("member"); foreach (XmlNode fileNode in fileList) { var fileElement = fileNode as XmlElement; var fileName = fileElement.GetAttribute("fileName"); - projectData.FileNames.Add(fileName); + sessionData.FileNames.Add(fileName); } var layoutElements = xmlDoc.GetElementsByTagName("layout"); if (layoutElements.Count > 0) { - projectData.TabLayoutXml = layoutElements[0].InnerXml; + sessionData.TabLayoutXml = layoutElements[0].InnerXml; } - return projectData; + return sessionData; } catch (Exception xmlParsingException) when (xmlParsingException is XmlException or UnauthorizedAccessException or IOException) { - _logger.Error(xmlParsingException, $"Error loading persistence data from {projectFileName}, unknown format, parsing xml or json was not possible"); - return new ProjectData(); + _logger.Error(xmlParsingException, $"Error loading Session data from {sessionFileName}, unknown format, parsing xml or json was not possible"); + return new SessionData(); } } -} \ No newline at end of file +} diff --git a/src/LogExpert.Core/Classes/Persister/ProjectValidationResult.cs b/src/LogExpert.Core/Classes/Persister/SessionValidationResult.cs similarity index 88% rename from src/LogExpert.Core/Classes/Persister/ProjectValidationResult.cs rename to src/LogExpert.Core/Classes/Persister/SessionValidationResult.cs index fed2d742..3d570db2 100644 --- a/src/LogExpert.Core/Classes/Persister/ProjectValidationResult.cs +++ b/src/LogExpert.Core/Classes/Persister/SessionValidationResult.cs @@ -1,6 +1,6 @@ namespace LogExpert.Core.Classes.Persister; -public class ProjectValidationResult +public class SessionValidationResult { public List ValidFiles { get; } = []; diff --git a/src/LogExpert.Core/Enums/ProjectLoadDlgResult.cs b/src/LogExpert.Core/Enums/SessionLoadDlgResult.cs similarity index 74% rename from src/LogExpert.Core/Enums/ProjectLoadDlgResult.cs rename to src/LogExpert.Core/Enums/SessionLoadDlgResult.cs index c32b6c2c..7386d8c7 100644 --- a/src/LogExpert.Core/Enums/ProjectLoadDlgResult.cs +++ b/src/LogExpert.Core/Enums/SessionLoadDlgResult.cs @@ -1,6 +1,6 @@ namespace LogExpert.Core.Enums; -public enum ProjectLoadDlgResult +public enum SessionLoadDlgResult { Cancel, CloseTabs, diff --git a/src/LogExpert.Persister.Tests/ProjectFileValidatorTests.cs b/src/LogExpert.Persister.Tests/SessionFileValidatorTests.cs similarity index 86% rename from src/LogExpert.Persister.Tests/ProjectFileValidatorTests.cs rename to src/LogExpert.Persister.Tests/SessionFileValidatorTests.cs index e030fe0b..fae0e744 100644 --- a/src/LogExpert.Persister.Tests/ProjectFileValidatorTests.cs +++ b/src/LogExpert.Persister.Tests/SessionFileValidatorTests.cs @@ -7,10 +7,10 @@ namespace LogExpert.Persister.Tests; /// /// Unit tests for the Project File Validator implementation (Issue #514). /// Tests validation logic for missing files in project/session loading. -/// Includes tests for ProjectFileResolver, PersisterHelpers, and ProjectPersister updates. +/// Includes tests for SessionFileResolver, PersisterHelpers, and SessionPersister updates. /// [TestFixture] -public class ProjectFileValidatorTests +public class SessionFileValidatorTests { private string _testDirectory; private string _projectFile; @@ -20,7 +20,7 @@ public class ProjectFileValidatorTests public void Setup () { // Create temporary test directory - _testDirectory = Path.Join(Path.GetTempPath(), "LogExpertTests", "ProjectValidator", Guid.NewGuid().ToString()); + _testDirectory = Path.Join(Path.GetTempPath(), "LogExpertTests", "SessionValidator", Guid.NewGuid().ToString()); _ = Directory.CreateDirectory(_testDirectory); // Initialize test log files list @@ -71,13 +71,13 @@ private void CreateTestLogFiles (params string[] fileNames) /// private void CreateTestProjectFile (params string[] logFileNames) { - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = [.. logFileNames.Select(name => Path.Join(_testDirectory, name))], TabLayoutXml = "test" }; - ProjectPersister.SaveProjectData(_projectFile, projectData); + SessionPersister.SaveSessionData(_projectFile, sessionData); } /// @@ -221,14 +221,14 @@ public void PersisterHelpers_FindFilenameForSettings_CorruptedLxp_ReturnsLxpPath #endregion - #region ProjectFileResolver Tests + #region SessionFileResolver Tests [Test] public void ProjectFileResolver_ResolveProjectFiles_AllLogFiles_ReturnsUnchanged () { // Arrange CreateTestLogFiles("file1.log", "file2.log"); - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = [ @@ -238,7 +238,7 @@ public void ProjectFileResolver_ResolveProjectFiles_AllLogFiles_ReturnsUnchanged }; // Act - var result = ProjectFileResolver.ResolveProjectFiles(projectData, PluginRegistry.PluginRegistry.Instance); + var result = SessionFileResolver.ResolveSessionFiles(sessionData, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Has.Count.EqualTo(2)); @@ -256,7 +256,7 @@ public void ProjectFileResolver_ResolveProjectFiles_WithLxpFiles_ResolvesToLogs CreatePersistenceFile("settings1.lxp", "actual1.log"); CreatePersistenceFile("settings2.lxp", "actual2.log"); - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = [ @@ -266,7 +266,7 @@ public void ProjectFileResolver_ResolveProjectFiles_WithLxpFiles_ResolvesToLogs }; // Act - var result = ProjectFileResolver.ResolveProjectFiles(projectData, PluginRegistry.PluginRegistry.Instance); + var result = SessionFileResolver.ResolveSessionFiles(sessionData, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Has.Count.EqualTo(2)); @@ -283,7 +283,7 @@ public void ProjectFileResolver_ResolveProjectFiles_MixedFiles_ResolvesProperly CreateTestLogFiles("direct.log", "referenced.log"); CreatePersistenceFile("indirect.lxp", "referenced.log"); - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = [ @@ -293,7 +293,7 @@ public void ProjectFileResolver_ResolveProjectFiles_MixedFiles_ResolvesProperly }; // Act - var result = ProjectFileResolver.ResolveProjectFiles(projectData, PluginRegistry.PluginRegistry.Instance); + var result = SessionFileResolver.ResolveSessionFiles(sessionData, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Has.Count.EqualTo(2)); @@ -308,20 +308,20 @@ public void ProjectFileResolver_ResolveProjectFiles_NullProjectData_ThrowsArgume { // Act & Assert _ = Assert.Throws(() => - ProjectFileResolver.ResolveProjectFiles(null, PluginRegistry.PluginRegistry.Instance)); + SessionFileResolver.ResolveSessionFiles(null, PluginRegistry.PluginRegistry.Instance)); } [Test] public void ProjectFileResolver_ResolveProjectFiles_EmptyProject_ReturnsEmptyList () { // Arrange - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = [] }; // Act - var result = ProjectFileResolver.ResolveProjectFiles(projectData, PluginRegistry.PluginRegistry.Instance); + var result = SessionFileResolver.ResolveSessionFiles(sessionData, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Is.Empty, "Empty project should return empty list"); @@ -332,13 +332,13 @@ public void ProjectFileResolver_ResolveProjectFiles_ReturnsReadOnlyCollection () { // Arrange CreateTestLogFiles("test.log"); - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = [Path.Join(_testDirectory, "test.log")] }; // Act - var result = ProjectFileResolver.ResolveProjectFiles(projectData, PluginRegistry.PluginRegistry.Instance); + var result = SessionFileResolver.ResolveSessionFiles(sessionData, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Is.InstanceOf>()); @@ -346,20 +346,20 @@ public void ProjectFileResolver_ResolveProjectFiles_ReturnsReadOnlyCollection () #endregion - #region ProjectLoadResult Tests + #region SessionLoadResult Tests [Test] public void ProjectLoadResult_HasValidFiles_AllFilesValid_ReturnsTrue () { // Arrange - var projectData = new ProjectData(); - var validationResult = new ProjectValidationResult(); + var sessionData = new SessionData(); + var validationResult = new SessionValidationResult(); validationResult.ValidFiles.Add("file1.log"); validationResult.ValidFiles.Add("file2.log"); - var result = new ProjectLoadResult + var result = new SessionLoadResult { - ProjectData = projectData, + SessionData = sessionData, ValidationResult = validationResult }; @@ -374,14 +374,14 @@ public void ProjectLoadResult_HasValidFiles_AllFilesValid_ReturnsTrue () public void ProjectLoadResult_HasValidFiles_NoValidFiles_ReturnsFalse () { // Arrange - var projectData = new ProjectData(); - var validationResult = new ProjectValidationResult(); + var sessionData = new SessionData(); + var validationResult = new SessionValidationResult(); validationResult.MissingFiles.Add("file1.log"); validationResult.MissingFiles.Add("file2.log"); - var result = new ProjectLoadResult + var result = new SessionLoadResult { - ProjectData = projectData, + SessionData = sessionData, ValidationResult = validationResult }; @@ -396,15 +396,15 @@ public void ProjectLoadResult_HasValidFiles_NoValidFiles_ReturnsFalse () public void ProjectLoadResult_HasValidFiles_SomeValidFiles_ReturnsTrue () { // Arrange - var projectData = new ProjectData(); - var validationResult = new ProjectValidationResult(); + var sessionData = new SessionData(); + var validationResult = new SessionValidationResult(); validationResult.ValidFiles.Add("file1.log"); validationResult.MissingFiles.Add("file2.log"); validationResult.MissingFiles.Add("file3.log"); - var result = new ProjectLoadResult + var result = new SessionLoadResult { - ProjectData = projectData, + SessionData = sessionData, ValidationResult = validationResult }; @@ -419,14 +419,14 @@ public void ProjectLoadResult_HasValidFiles_SomeValidFiles_ReturnsTrue () public void ProjectLoadResult_RequiresUserIntervention_AllFilesValid_ReturnsFalse () { // Arrange - var projectData = new ProjectData(); - var validationResult = new ProjectValidationResult(); + var sessionData = new SessionData(); + var validationResult = new SessionValidationResult(); validationResult.ValidFiles.Add("file1.log"); validationResult.ValidFiles.Add("file2.log"); - var result = new ProjectLoadResult + var result = new SessionLoadResult { - ProjectData = projectData, + SessionData = sessionData, ValidationResult = validationResult }; @@ -441,14 +441,14 @@ public void ProjectLoadResult_RequiresUserIntervention_AllFilesValid_ReturnsFals public void ProjectLoadResult_RequiresUserIntervention_SomeMissingFiles_ReturnsTrue () { // Arrange - var projectData = new ProjectData(); - var validationResult = new ProjectValidationResult(); + var sessionData = new SessionData(); + var validationResult = new SessionValidationResult(); validationResult.ValidFiles.Add("file1.log"); validationResult.MissingFiles.Add("file2.log"); - var result = new ProjectLoadResult + var result = new SessionLoadResult { - ProjectData = projectData, + SessionData = sessionData, ValidationResult = validationResult }; @@ -469,7 +469,7 @@ public void ProjectLoadResult_LogToOriginalFileMapping_StoresMapping () ["C:\\logs\\direct.log"] = "C:\\logs\\direct.log" }; - var result = new ProjectLoadResult + var result = new SessionLoadResult { LogToOriginalFileMapping = mapping }; @@ -482,13 +482,13 @@ public void ProjectLoadResult_LogToOriginalFileMapping_StoresMapping () #endregion - #region ProjectValidationResult Tests + #region SessionValidationResult Tests [Test] public void ProjectValidationResult_HasMissingFiles_WithMissingFiles_ReturnsTrue () { // Arrange - var result = new ProjectValidationResult(); + var result = new SessionValidationResult(); result.ValidFiles.Add("file1.log"); result.MissingFiles.Add("file2.log"); @@ -503,7 +503,7 @@ public void ProjectValidationResult_HasMissingFiles_WithMissingFiles_ReturnsTrue public void ProjectValidationResult_HasMissingFiles_WithoutMissingFiles_ReturnsFalse () { // Arrange - var result = new ProjectValidationResult(); + var result = new SessionValidationResult(); result.ValidFiles.Add("file1.log"); result.ValidFiles.Add("file2.log"); @@ -516,7 +516,7 @@ public void ProjectValidationResult_HasMissingFiles_WithoutMissingFiles_ReturnsF #endregion - #region ProjectPersister.LoadProjectData - All Files Valid + #region SessionPersister.LoadSessionData - All Files Valid [Test] public void LoadProjectData_AllFilesExist_ReturnsSuccessResult () @@ -526,11 +526,11 @@ public void LoadProjectData_AllFilesExist_ReturnsSuccessResult () CreateTestProjectFile("log1.log", "log2.log", "log3.log"); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Is.Not.Null, "Result should not be null"); - Assert.That(result.ProjectData, Is.Not.Null, "ProjectData should not be null"); + Assert.That(result.SessionData, Is.Not.Null, "SessionData should not be null"); Assert.That(result.ValidationResult, Is.Not.Null, "ValidationResult should not be null"); Assert.That(result.HasValidFiles, Is.True, "Should have valid files"); Assert.That(result.RequiresUserIntervention, Is.False, "Should not require intervention"); @@ -546,10 +546,10 @@ public void LoadProjectData_AllFilesExist_ProjectDataContainsCorrectFiles () CreateTestProjectFile("alpha.log", "beta.log", "gamma.log"); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert - var fileNames = result.ProjectData.FileNames.Select(Path.GetFileName).ToList(); + var fileNames = result.SessionData.FileNames.Select(Path.GetFileName).ToList(); Assert.That(fileNames, Does.Contain("alpha.log"), "Should contain alpha.log"); Assert.That(fileNames, Does.Contain("beta.log"), "Should contain beta.log"); Assert.That(fileNames, Does.Contain("gamma.log"), "Should contain gamma.log"); @@ -563,11 +563,11 @@ public void LoadProjectData_AllFilesExist_PreservesTabLayoutXml () CreateTestProjectFile("test.log"); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert - Assert.That(result.ProjectData.TabLayoutXml, Is.Not.Null.And.Not.Empty, "TabLayoutXml should be preserved"); - Assert.That(result.ProjectData.TabLayoutXml, Does.Contain(""), "Should contain layout XML"); + Assert.That(result.SessionData.TabLayoutXml, Is.Not.Null.And.Not.Empty, "TabLayoutXml should be preserved"); + Assert.That(result.SessionData.TabLayoutXml, Does.Contain(""), "Should contain layout XML"); } [Test] @@ -579,7 +579,7 @@ public void LoadProjectData_WithLxpFiles_ResolvesToActualLogs () CreatePersistenceFile("settings2.lxp", "actual2.log"); // Create project referencing .lxp files - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = [ @@ -587,15 +587,15 @@ public void LoadProjectData_WithLxpFiles_ResolvesToActualLogs () Path.Join(_testDirectory, "settings2.lxp") ] }; - ProjectPersister.SaveProjectData(_projectFile, projectData); + SessionPersister.SaveSessionData(_projectFile, sessionData); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Is.Not.Null); Assert.That(result.ValidationResult.ValidFiles.Count, Is.EqualTo(2), "Should validate actual log files"); - var fileNames = result.ProjectData.FileNames.Select(Path.GetFileName).ToList(); + var fileNames = result.SessionData.FileNames.Select(Path.GetFileName).ToList(); Assert.That(fileNames, Does.Contain("actual1.log"), "Should contain resolved log file"); Assert.That(fileNames, Does.Contain("actual2.log"), "Should contain resolved log file"); } @@ -607,14 +607,14 @@ public void LoadProjectData_WithLxpFiles_PreservesMapping () CreateTestLogFiles("actual.log"); CreatePersistenceFile("settings.lxp", "actual.log"); - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = [Path.Join(_testDirectory, "settings.lxp")] }; - ProjectPersister.SaveProjectData(_projectFile, projectData); + SessionPersister.SaveSessionData(_projectFile, sessionData); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result.LogToOriginalFileMapping, Is.Not.Null); @@ -626,7 +626,7 @@ public void LoadProjectData_WithLxpFiles_PreservesMapping () #endregion - #region ProjectPersister.LoadProjectData - Some Files Missing + #region SessionPersister.LoadSessionData - Some Files Missing [Test] public void LoadProjectData_SomeFilesMissing_ReturnsPartialSuccessResult () @@ -637,7 +637,7 @@ public void LoadProjectData_SomeFilesMissing_ReturnsPartialSuccessResult () CreateTestProjectFile("exists1.log", "exists2.log", "missing.log"); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Is.Not.Null, "Result should not be null"); @@ -656,7 +656,7 @@ public void LoadProjectData_SomeFilesMissing_ValidFilesListIsCorrect () CreateTestProjectFile("valid1.log", "valid2.log", "invalid.log"); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert var validFileNames = result.ValidationResult.ValidFiles.Select(Path.GetFileName).ToList(); @@ -674,7 +674,7 @@ public void LoadProjectData_SomeFilesMissing_MissingFilesListIsCorrect () CreateTestProjectFile("present.log", "absent1.log", "absent2.log"); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert var missingFileNames = result.ValidationResult.MissingFiles.Select(Path.GetFileName).ToList(); @@ -692,7 +692,7 @@ public void LoadProjectData_MajorityFilesMissing_StillReturnsValidFiles () CreateTestProjectFile("only_valid.log", "missing1.log", "missing2.log", "missing3.log", "missing4.log"); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result.HasValidFiles, Is.True, "Should have at least one valid file"); @@ -708,14 +708,14 @@ public void LoadProjectData_LxpReferencingMissingLog_ReportsLogAsMissing () CreatePersistenceFile("settings.lxp", "missing.log"); DeleteLogFiles("missing.log"); - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = [Path.Join(_testDirectory, "settings.lxp")] }; - ProjectPersister.SaveProjectData(_projectFile, projectData); + SessionPersister.SaveSessionData(_projectFile, sessionData); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result.ValidationResult.MissingFiles.Count, Is.EqualTo(1), "Should report missing log file"); @@ -724,7 +724,7 @@ public void LoadProjectData_LxpReferencingMissingLog_ReportsLogAsMissing () #endregion - #region ProjectPersister.LoadProjectData - All Files Missing + #region SessionPersister.LoadSessionData - All Files Missing [Test] public void LoadProjectData_AllFilesMissing_ReturnsFailureResult () @@ -735,7 +735,7 @@ public void LoadProjectData_AllFilesMissing_ReturnsFailureResult () CreateTestProjectFile("missing1.log", "missing2.log"); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Is.Not.Null, "Result should not be null"); @@ -753,7 +753,7 @@ public void LoadProjectData_AllFilesMissing_MissingFilesListComplete () CreateTestProjectFile("gone1.log", "gone2.log", "gone3.log"); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result.ValidationResult.MissingFiles.Count, Is.EqualTo(3), "Should have 3 missing files"); @@ -765,7 +765,7 @@ public void LoadProjectData_AllFilesMissing_MissingFilesListComplete () #endregion - #region ProjectPersister.LoadProjectData - Empty/Invalid Projects + #region SessionPersister.LoadSessionData - Empty/Invalid Projects [Test] public void LoadProjectData_EmptyProject_ReturnsEmptyResult () @@ -774,11 +774,11 @@ public void LoadProjectData_EmptyProject_ReturnsEmptyResult () CreateTestProjectFile(); // Empty project with no files // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Is.Not.Null, "Result should not be null"); - Assert.That(result.ProjectData.FileNames, Is.Empty, "FileNames should be empty"); + Assert.That(result.SessionData.FileNames, Is.Empty, "FileNames should be empty"); Assert.That(result.ValidationResult.ValidFiles, Is.Empty, "ValidFiles should be empty"); Assert.That(result.ValidationResult.MissingFiles, Is.Empty, "MissingFiles should be empty"); } @@ -790,12 +790,12 @@ public void LoadProjectData_NonExistentProjectFile_ReturnsNull () var nonExistentProject = Path.Join(_testDirectory, "does_not_exist.lxj"); // Act - var result = ProjectPersister.LoadProjectData(nonExistentProject, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(nonExistentProject, PluginRegistry.PluginRegistry.Instance); // Assert // FIXED: Now returns empty result instead of null when file doesn't exist Assert.That(result, Is.Not.Null, "Result should not be null even for non-existent file"); - Assert.That(result.ProjectData, Is.Not.Null, "ProjectData should be initialized"); + Assert.That(result.SessionData, Is.Not.Null, "SessionData should be initialized"); } [Test] @@ -807,7 +807,7 @@ public void LoadProjectData_CorruptedProjectFile_ThrowsJsonReaderException () // Act & Assert - JsonReaderException is not caught, so it propagates _ = Assert.Throws(() => - ProjectPersister.LoadProjectData(corruptedProject, PluginRegistry.PluginRegistry.Instance)); + SessionPersister.LoadSessionData(corruptedProject, PluginRegistry.PluginRegistry.Instance)); } #endregion @@ -819,7 +819,7 @@ public void LoadProjectData_DuplicateFileReferences_HandlesCorrectly () { // Arrange CreateTestLogFiles("duplicate.log"); - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = [ @@ -828,10 +828,10 @@ public void LoadProjectData_DuplicateFileReferences_HandlesCorrectly () Path.Join(_testDirectory, "duplicate.log") ] }; - ProjectPersister.SaveProjectData(_projectFile, projectData); + SessionPersister.SaveSessionData(_projectFile, sessionData); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Is.Not.Null, "Result should not be null"); @@ -847,7 +847,7 @@ public void LoadProjectData_FilesWithSpecialCharacters_ValidatesCorrectly () CreateTestProjectFile("file with spaces.log", "file-with-dashes.log", "file_with_underscores.log"); // Act - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); // Assert Assert.That(result, Is.Not.Null, "Result should not be null"); @@ -872,7 +872,7 @@ public void LoadProjectData_VeryLargeProject_ValidatesEfficiently () // Act var stopwatch = System.Diagnostics.Stopwatch.StartNew(); - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); stopwatch.Stop(); // Assert @@ -909,7 +909,7 @@ public void LoadProjectData_ManyMissingFiles_PerformsEfficiently () // Act var stopwatch = System.Diagnostics.Stopwatch.StartNew(); - var result = ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance); + var result = SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance); stopwatch.Stop(); // Assert @@ -928,7 +928,7 @@ public void LoadProjectData_NullProjectFile_ThrowsArgumentNullException () { // Act & Assert - File.ReadAllText throws ArgumentNullException for null path _ = Assert.Throws(() => - ProjectPersister.LoadProjectData(null, PluginRegistry.PluginRegistry.Instance)); + SessionPersister.LoadSessionData(null, PluginRegistry.PluginRegistry.Instance)); } [Test] @@ -936,7 +936,7 @@ public void LoadProjectData_EmptyProjectFile_ThrowsArgumentException () { // Act & Assert - File.ReadAllText throws ArgumentException for empty string _ = Assert.Throws(() => - ProjectPersister.LoadProjectData(string.Empty, PluginRegistry.PluginRegistry.Instance)); + SessionPersister.LoadSessionData(string.Empty, PluginRegistry.PluginRegistry.Instance)); } [Test] @@ -947,7 +947,7 @@ public void LoadProjectData_NullPluginRegistry_ThrowsArgumentNullException () // Act & Assert _ = Assert.Throws(() => - ProjectPersister.LoadProjectData(_projectFile, null)); + SessionPersister.LoadSessionData(_projectFile, null)); } #endregion @@ -973,7 +973,7 @@ public void LoadProjectData_LegacyProjectFormat_ThrowsJsonReaderException () // Act & Assert - JsonReaderException is not caught, so XML fallback doesn't trigger _ = Assert.Throws(() => - ProjectPersister.LoadProjectData(_projectFile, PluginRegistry.PluginRegistry.Instance)); + SessionPersister.LoadSessionData(_projectFile, PluginRegistry.PluginRegistry.Instance)); } #endregion diff --git a/src/LogExpert.Resources/Resources.Designer.cs b/src/LogExpert.Resources/Resources.Designer.cs index 4d43832d..15e879d7 100644 --- a/src/LogExpert.Resources/Resources.Designer.cs +++ b/src/LogExpert.Resources/Resources.Designer.cs @@ -1380,74 +1380,65 @@ public static string KeywordActionDlg_UI_Title { } /// - /// Looks up a localized string similar to Error loading project file. The file may be corrupted or inaccessible.. + /// Looks up a localized string similar to Error loading session. The file may be corrupted or inaccessible.. /// - public static string LoadProject_UI_Message_Error_FileMaybeCorruptedOrInaccessible { + public static string LoadSession_UI_Message_Error_FileMaybeCorruptedOrInaccessible { get { - return ResourceManager.GetString("LoadProject_UI_Message_Error_FileMaybeCorruptedOrInaccessible", resourceCulture); + return ResourceManager.GetString("LoadSession_UI_Message_Error_FileMaybeCorruptedOrInaccessible", resourceCulture); } } /// /// Looks up a localized string similar to Failed to update session file: {0}. /// - public static string LoadProject_UI_Message_Error_Message_FailedToUpdateSessionFile { + public static string LoadSession_UI_Message_Error_Message_FailedToUpdateSessionFile { get { - return ResourceManager.GetString("LoadProject_UI_Message_Error_Message_FailedToUpdateSessionFile", resourceCulture); + return ResourceManager.GetString("LoadSession_UI_Message_Error_Message_FailedToUpdateSessionFile", resourceCulture); } } /// /// Looks up a localized string similar to Session file has been updated with the new file paths.. /// - public static string LoadProject_UI_Message_Error_Message_UpdateSessionFile { + public static string LoadSession_UI_Message_Error_Message_UpdateSessionFile { get { - return ResourceManager.GetString("LoadProject_UI_Message_Error_Message_UpdateSessionFile", resourceCulture); + return ResourceManager.GetString("LoadSession_UI_Message_Error_Message_UpdateSessionFile", resourceCulture); } } /// /// Looks up a localized string similar to Session Update Failed. /// - public static string LoadProject_UI_Message_Error_Title_FailedToUpdateSessionFile { + public static string LoadSession_UI_Message_Error_Title_FailedToUpdateSessionFile { get { - return ResourceManager.GetString("LoadProject_UI_Message_Error_Title_FailedToUpdateSessionFile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Project Load Failed. - /// - public static string LoadProject_UI_Message_Error_Title_ProjectLoadFailed { - get { - return ResourceManager.GetString("LoadProject_UI_Message_Error_Title_ProjectLoadFailed", resourceCulture); + return ResourceManager.GetString("LoadSession_UI_Message_Error_Title_FailedToUpdateSessionFile", resourceCulture); } } /// /// Looks up a localized string similar to Session Load Failed. /// - public static string LoadProject_UI_Message_Error_Title_SessionLoadFailed { + public static string LoadSession_UI_Message_Error_Title_SessionLoadFailed { get { - return ResourceManager.GetString("LoadProject_UI_Message_Error_Title_SessionLoadFailed", resourceCulture); + return ResourceManager.GetString("LoadSession_UI_Message_Error_Title_SessionLoadFailed", resourceCulture); } } /// /// Looks up a localized string similar to Session Updated. /// - public static string LoadProject_UI_Message_Error_Title_UpdateSessionFile { + public static string LoadSession_UI_Message_Error_Title_UpdateSessionFile { get { - return ResourceManager.GetString("LoadProject_UI_Message_Error_Title_UpdateSessionFile", resourceCulture); + return ResourceManager.GetString("LoadSession_UI_Message_Error_Title_UpdateSessionFile", resourceCulture); } } /// /// Looks up a localized string similar to None of the files in this session could be found. The session cannot be loaded.. /// - public static string LoadProject_UI_Message_Message_FilesForSessionCouldNotBeFound { + public static string LoadSession_UI_Message_Message_FilesForSessionCouldNotBeFound { get { - return ResourceManager.GetString("LoadProject_UI_Message_Message_FilesForSessionCouldNotBeFound", resourceCulture); + return ResourceManager.GetString("LoadSession_UI_Message_Message_FilesForSessionCouldNotBeFound", resourceCulture); } } @@ -1945,9 +1936,9 @@ public static string LogTabWindow_UI_Message_PluginTrustConfigurationUpdate { /// /// Looks up a localized string similar to LogExpert session {0}. /// - public static string LogTabWindow_UI_Project_Session_Default_Filter { + public static string LogTabWindow_UI_Session_Default_Filter { get { - return ResourceManager.GetString("LogTabWindow_UI_Project_Session_Default_Filter", resourceCulture); + return ResourceManager.GetString("LogTabWindow_UI_Session_Default_Filter", resourceCulture); } } diff --git a/src/LogExpert.Resources/Resources.de.resx b/src/LogExpert.Resources/Resources.de.resx index e6ef440d..7c1e8ce8 100644 --- a/src/LogExpert.Resources/Resources.de.resx +++ b/src/LogExpert.Resources/Resources.de.resx @@ -659,7 +659,7 @@ Das ist eine Test Fehlermeldung, geworfen durch einen Hintergrund Thread - + LogExpert Session {0} @@ -2074,29 +2074,26 @@ Fortfahren? Gültig - + Sitzungsaktualisierung fehlgeschlagen - + Sitzungsdatei konnte nicht aktualisiert werden: {0} - + Sitzung aktualisiert - + Die Sitzungsdatei wurde mit den neuen Dateipfaden aktualisiert. - + Das Laden der Sitzung ist fehlgeschlagen - + Keine der Dateien in dieser Sitzung konnte gefunden werden. Die Sitzung kann nicht geladen werden. - - Das Laden des Projekts ist fehlgeschlagen - - - Fehler beim Laden der Projektdatei. Die Datei ist möglicherweise beschädigt oder nicht zugänglich. + + Fehler beim Laden der Sitzung. Die Datei ist möglicherweise beschädigt oder nicht zugänglich. Standard (einzelne Zeile) diff --git a/src/LogExpert.Resources/Resources.resx b/src/LogExpert.Resources/Resources.resx index 02c7ca72..711968a9 100644 --- a/src/LogExpert.Resources/Resources.resx +++ b/src/LogExpert.Resources/Resources.resx @@ -665,7 +665,7 @@ This is a test exception thrown by a background thread - + LogExpert session {0} @@ -2138,28 +2138,25 @@ Restart LogExpert to apply changes? Default (single line) - - Error loading project file. The file may be corrupted or inaccessible. + + Error loading session. The file may be corrupted or inaccessible. - - Project Load Failed - - + None of the files in this session could be found. The session cannot be loaded. - + Session Load Failed - + Session file has been updated with the new file paths. - + Session Updated - + Failed to update session file: {0} - + Session Update Failed diff --git a/src/LogExpert.Resources/Resources.zh-CN.resx b/src/LogExpert.Resources/Resources.zh-CN.resx index e2b407eb..df390ea7 100644 --- a/src/LogExpert.Resources/Resources.zh-CN.resx +++ b/src/LogExpert.Resources/Resources.zh-CN.resx @@ -568,7 +568,7 @@ 这是由后台线程抛出的测试异常 - + LogExpert 会话 {0} @@ -1927,28 +1927,25 @@ YY[YY] = 年 默认(单行) - - 加载项目文件出错。文件可能已损坏或无法访问。 + + 加载会话出错。文件可能已损坏或无法访问。 - - 项目加载失败 - - + 此会话中的文件均未找到。无法加载会话。 - + 会话加载失败 - + 会话文件已使用新文件路径更新。 - + 会话已更新 - + 更新会话文件失败:{0} - + 会话更新失败 diff --git a/src/LogExpert.Tests/Services/ProjectFileHandlerTests.cs b/src/LogExpert.Tests/Services/SessionHandlerTests.cs similarity index 85% rename from src/LogExpert.Tests/Services/ProjectFileHandlerTests.cs rename to src/LogExpert.Tests/Services/SessionHandlerTests.cs index 598eaf20..5153cb43 100644 --- a/src/LogExpert.Tests/Services/ProjectFileHandlerTests.cs +++ b/src/LogExpert.Tests/Services/SessionHandlerTests.cs @@ -6,7 +6,7 @@ using LogExpert.UI.Controls.LogWindow; using LogExpert.UI.Interface; using LogExpert.UI.Services.FileOperationService; -using LogExpert.UI.Services.ProjectFileHandlerService; +using LogExpert.UI.Services.SessionHandlerService; using Moq; @@ -17,14 +17,14 @@ namespace LogExpert.Tests.Services; [TestFixture] [Apartment(ApartmentState.STA)] [SupportedOSPlatform("windows")] -internal class ProjectFileHandlerTests : IDisposable +internal class SessionHandlerTests : IDisposable { private Mock _pluginRegistryMock = null!; private List _addFileTabCalls = null!; private Settings _settings; private Mock _configManagerMock; private LogWindow _stubLogWindow = null!; - private ProjectFileHandler _sut = null!; + private SessionHandler _sut = null!; private bool _disposed; @@ -54,7 +54,7 @@ public void SetUp () forcePersistenceLoading: false, configManager: _configManagerMock.Object); - _sut = new ProjectFileHandler( + _sut = new SessionHandler( _pluginRegistryMock.Object, request => { @@ -69,7 +69,7 @@ public void TearDown () _stubLogWindow?.Dispose(); } - #region LoadProject — Phase 1 tests + #region LoadSession — Phase 1 tests [Test] public void LoadProject_ValidProjectFile_ReturnsSuccess () @@ -81,12 +81,12 @@ public void LoadProject_ValidProjectFile_ReturnsSuccess () try { // Act - var outcome = _sut.LoadProject(tempFile); + var outcome = _sut.LoadSession(tempFile); // Assert - Assert.That(outcome.Status, Is.EqualTo(ProjectLoadOutcome.LoadStatus.Success)); - Assert.That(outcome.ProjectData, Is.Not.Null); - Assert.That(outcome.ProjectData!.FileNames, Has.Count.EqualTo(1)); + Assert.That(outcome.Status, Is.EqualTo(SessionLoadOutcome.LoadStatus.Success)); + Assert.That(outcome.SessionData, Is.Not.Null); + Assert.That(outcome.SessionData!.FileNames, Has.Count.EqualTo(1)); Assert.That(outcome.ErrorMessage, Is.Null); Assert.That(outcome.ValidationResult, Is.Null); } @@ -107,10 +107,10 @@ public void LoadProject_ValidProjectWithLayoutXml_HasLayoutDataIsTrue () try { // Act - var outcome = _sut.LoadProject(tempFile); + var outcome = _sut.LoadSession(tempFile); // Assert - Assert.That(outcome.Status, Is.EqualTo(ProjectLoadOutcome.LoadStatus.Success)); + Assert.That(outcome.Status, Is.EqualTo(SessionLoadOutcome.LoadStatus.Success)); Assert.That(outcome.HasLayoutData, Is.True); Assert.That(outcome.LayoutXml, Is.EqualTo("")); } @@ -130,7 +130,7 @@ public void LoadProject_ValidProjectWithoutLayoutXml_HasLayoutDataIsFalse () try { // Act - var outcome = _sut.LoadProject(tempFile); + var outcome = _sut.LoadSession(tempFile); // Assert Assert.That(outcome.HasLayoutData, Is.False); @@ -153,11 +153,11 @@ public void LoadProject_ProjectWithMissingFiles_ReturnsNeedsIntervention () try { // Act - var outcome = _sut.LoadProject(tempFile); + var outcome = _sut.LoadSession(tempFile); // Assert - Assert.That(outcome.Status, Is.EqualTo(ProjectLoadOutcome.LoadStatus.NeedsIntervention)); - Assert.That(outcome.ProjectData, Is.Not.Null); + Assert.That(outcome.Status, Is.EqualTo(SessionLoadOutcome.LoadStatus.NeedsIntervention)); + Assert.That(outcome.SessionData, Is.Not.Null); Assert.That(outcome.ValidationResult, Is.Not.Null); Assert.That(outcome.ValidationResult!.HasMissingFiles, Is.True); } @@ -177,12 +177,12 @@ public void LoadProject_CorruptFile_ReturnsError () try { // Act - var outcome = _sut.LoadProject(tempFile); + var outcome = _sut.LoadSession(tempFile); // Assert - Assert.That(outcome.Status, Is.EqualTo(ProjectLoadOutcome.LoadStatus.Error)); + Assert.That(outcome.Status, Is.EqualTo(SessionLoadOutcome.LoadStatus.Error)); Assert.That(outcome.ErrorMessage, Is.Not.Null.And.Not.Empty); - Assert.That(outcome.ProjectData, Is.Null); + Assert.That(outcome.SessionData, Is.Null); } finally { @@ -197,10 +197,10 @@ public void LoadProject_NonexistentFile_ReturnsError () var fakePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".lxj"); // Act - var outcome = _sut.LoadProject(fakePath); + var outcome = _sut.LoadSession(fakePath); // Assert - Assert.That(outcome.Status, Is.EqualTo(ProjectLoadOutcome.LoadStatus.Error)); + Assert.That(outcome.Status, Is.EqualTo(SessionLoadOutcome.LoadStatus.Error)); Assert.That(outcome.ErrorMessage, Is.Not.Null.And.Not.Empty); } @@ -213,10 +213,10 @@ public void LoadProject_EmptyFileList_ReturnsEmptyProject () try { // Act - var outcome = _sut.LoadProject(tempFile); + var outcome = _sut.LoadSession(tempFile); // Assert - Assert.That(outcome.Status, Is.EqualTo(ProjectLoadOutcome.LoadStatus.EmptyProject)); + Assert.That(outcome.Status, Is.EqualTo(SessionLoadOutcome.LoadStatus.EmptySession)); Assert.That(outcome.ErrorMessage, Is.Not.Null.And.Not.Empty); } finally @@ -234,7 +234,7 @@ public void LoadProject_DoesNotInvokeAddFileTabCallback () try { // Act - _ = _sut.LoadProject(tempFile); + _ = _sut.LoadSession(tempFile); // Assert — phase 1 must never open tabs Assert.That(_addFileTabCalls, Is.Empty); @@ -316,7 +316,7 @@ public void ContinueLoad_WithAlternatives_ReplacesFilePathsBeforeOpening () { // Arrange var outcome = CreateOutcome( - ProjectLoadOutcome.LoadStatus.NeedsIntervention, + SessionLoadOutcome.LoadStatus.NeedsIntervention, ["C:\\old\\missing.log", "C:\\logs\\existing.log"], layoutXml: null); @@ -342,7 +342,7 @@ public void ContinueLoad_DuplicateFileWithAlternative_ReplacesBothOccurrences () { // Arrange var outcome = CreateOutcome( - ProjectLoadOutcome.LoadStatus.NeedsIntervention, + SessionLoadOutcome.LoadStatus.NeedsIntervention, ["C:\\missing.log", "C:\\missing.log"], layoutXml: null); @@ -368,7 +368,7 @@ public void ContinueLoad_CloseAllTabs_ReturnsTrueInResult () { // Arrange var outcome = CreateOutcome( - ProjectLoadOutcome.LoadStatus.NeedsIntervention, + SessionLoadOutcome.LoadStatus.NeedsIntervention, ["C:\\logs\\app.log"], layoutXml: ""); @@ -388,7 +388,7 @@ public void ContinueLoad_OpenInNewWindow_DoesNotOpenTabs_ReturnsFileNames () { // Arrange var outcome = CreateOutcome( - ProjectLoadOutcome.LoadStatus.NeedsIntervention, + SessionLoadOutcome.LoadStatus.NeedsIntervention, ["C:\\logs\\app.log"], layoutXml: null); @@ -409,7 +409,7 @@ public void ContinueLoad_OpenInNewWindowWithAlternatives_AppliesAlternativesThen { // Arrange var outcome = CreateOutcome( - ProjectLoadOutcome.LoadStatus.NeedsIntervention, + SessionLoadOutcome.LoadStatus.NeedsIntervention, ["C:\\old\\missing.log", "C:\\logs\\existing.log"], layoutXml: null); @@ -441,14 +441,14 @@ public void ContinueLoad_UpdateSessionFileTrue_SavesAmendedProject () try { - var outcome = new ProjectLoadOutcome + var outcome = new SessionLoadOutcome { - Status = ProjectLoadOutcome.LoadStatus.NeedsIntervention, - ProjectData = new ProjectData + Status = SessionLoadOutcome.LoadStatus.NeedsIntervention, + SessionData = new SessionData { FileNames = ["C:\\old\\missing.log"], TabLayoutXml = "", - ProjectFilePath = tempFile + SessionFilePath = tempFile }, LayoutXml = "" }; @@ -466,10 +466,10 @@ public void ContinueLoad_UpdateSessionFileTrue_SavesAmendedProject () _ = _sut.ContinueLoad(outcome, resolution, restoreLayout: false); // Assert: reload the file and check the path was updated - var reloaded = ProjectPersister.LoadProjectData(tempFile, _pluginRegistryMock.Object); - Assert.That(reloaded?.ProjectData?.FileNames, Has.Some.Contains("found.log")); + var reloaded = SessionPersister.LoadSessionData(tempFile, _pluginRegistryMock.Object); + Assert.That(reloaded?.SessionData?.FileNames, Has.Some.Contains("found.log")); // Layout XML must be preserved - Assert.That(reloaded?.ProjectData?.TabLayoutXml, Is.EqualTo("")); + Assert.That(reloaded?.SessionData?.TabLayoutXml, Is.EqualTo("")); } finally { @@ -487,13 +487,13 @@ public void ContinueLoad_UpdateSessionFileFalse_DoesNotSave () { var originalContent = File.ReadAllText(tempFile); - var outcome = new ProjectLoadOutcome + var outcome = new SessionLoadOutcome { - Status = ProjectLoadOutcome.LoadStatus.NeedsIntervention, - ProjectData = new ProjectData + Status = SessionLoadOutcome.LoadStatus.NeedsIntervention, + SessionData = new SessionData { FileNames = ["C:\\old\\missing.log"], - ProjectFilePath = tempFile + SessionFilePath = tempFile } }; @@ -534,14 +534,14 @@ public void ContinueLoad_NullResolution_SuccessReturnsCloseAllTabsFalse () #endregion - #region SaveProject tests + #region SaveSession tests [Test] public void SaveProject_ValidData_ReturnsTrueAndNullError () { // Arrange var tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".lxj"); - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = ["C:\\logs\\app.log"], TabLayoutXml = "" @@ -550,7 +550,7 @@ public void SaveProject_ValidData_ReturnsTrueAndNullError () try { // Act - var success = _sut.SaveProject(tempFile, projectData, out var errorMessage); + var success = _sut.SaveSession(tempFile, sessionData, out var errorMessage); // Assert Assert.That(success, Is.True); @@ -569,16 +569,16 @@ public void SaveProject_ValidData_ReturnsTrueAndNullError () [Test] public void SaveProject_NullPath_ReturnsFalseWithErrorMessage () { - // Note: ProjectPersister.SaveProjectData swallows IOException/UnauthorizedAccessException/ + // Note: SessionPersister.SaveSessionData swallows IOException/UnauthorizedAccessException/ // JsonSerializationException. Only uncaught exceptions (e.g. ArgumentNullException from - // File.WriteAllText(null,...)) propagate to ProjectFileHandler.SaveProject's catch block. - var projectData = new ProjectData + // File.WriteAllText(null,...)) propagate to SessionHandler.SaveSession's catch block. + var sessionData = new SessionData { FileNames = ["C:\\logs\\app.log"] }; // Act - var success = _sut.SaveProject(null!, projectData, out var errorMessage); + var success = _sut.SaveSession(null!, sessionData, out var errorMessage); // Assert Assert.That(success, Is.False); @@ -589,17 +589,17 @@ public void SaveProject_NullPath_ReturnsFalseWithErrorMessage () #region Helpers - private static ProjectLoadOutcome CreateSuccessOutcome (List fileNames, string? layoutXml) + private static SessionLoadOutcome CreateSuccessOutcome (List fileNames, string? layoutXml) { - return CreateOutcome(ProjectLoadOutcome.LoadStatus.Success, fileNames, layoutXml); + return CreateOutcome(SessionLoadOutcome.LoadStatus.Success, fileNames, layoutXml); } - private static ProjectLoadOutcome CreateOutcome (ProjectLoadOutcome.LoadStatus status, List fileNames, string? layoutXml) + private static SessionLoadOutcome CreateOutcome (SessionLoadOutcome.LoadStatus status, List fileNames, string? layoutXml) { - return new ProjectLoadOutcome + return new SessionLoadOutcome { Status = status, - ProjectData = new ProjectData + SessionData = new SessionData { FileNames = fileNames, TabLayoutXml = layoutXml! @@ -624,14 +624,14 @@ private static string CreateTempLogFile () /// private static string CreateTempProjectFile (List fileNames, string? layoutXml) { - var projectData = new ProjectData + var sessionData = new SessionData { FileNames = fileNames, TabLayoutXml = layoutXml! }; var tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".lxj"); - ProjectPersister.SaveProjectData(tempFile, projectData); + SessionPersister.SaveSessionData(tempFile, sessionData); return tempFile; } diff --git a/src/LogExpert.UI/Dialogs/Helpers/MissingFilesMessageBox.cs b/src/LogExpert.UI/Dialogs/Helpers/MissingFilesMessageBox.cs index 1ef0b8ad..826ad172 100644 --- a/src/LogExpert.UI/Dialogs/Helpers/MissingFilesMessageBox.cs +++ b/src/LogExpert.UI/Dialogs/Helpers/MissingFilesMessageBox.cs @@ -17,7 +17,7 @@ internal static class MissingFilesMessageBox /// /// The validation result containing missing file information /// True if user wants to continue loading valid files, false to cancel - public static bool Show (ProjectValidationResult validationResult) + public static bool Show (SessionValidationResult validationResult) { ArgumentNullException.ThrowIfNull(validationResult); diff --git a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs index 9caadd91..80efe776 100644 --- a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs +++ b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs @@ -25,7 +25,7 @@ using LogExpert.UI.Services.LedService; using LogExpert.UI.Services.LogWindowCoordinatorService; using LogExpert.UI.Services.MenuToolbarService; -using LogExpert.UI.Services.ProjectFileHandlerService; +using LogExpert.UI.Services.SessionHandlerService; using LogExpert.UI.Services.TabControllerService; using LogExpert.UI.Services.ToolWindowCoordinatorService; @@ -53,7 +53,7 @@ internal partial class LogTabWindow : Form, ILogTabWindow private readonly LogWindowCoordinator _logWindowCoordinator; private readonly ToolWindowCoordinator _toolWindowCoordinator; private readonly FileOperationService _fileOperationService; - private readonly ProjectFileHandler _projectFileHandler; + private readonly SessionHandler _sessionHandler; private bool _disposed; @@ -108,12 +108,12 @@ public LogTabWindow (string[] fileNames, int instanceNumber, bool showInstanceNu _deadIcon = _ledService.GetDeadIcon(); - _fileOperationService = new FileOperationService(configManager, _tabController, _ledService, PluginRegistry.PluginRegistry.Instance, CreateLogWindowFromRequest, () => Clipboard.ContainsText() ? Clipboard.GetText() : null, LoadProject); + _fileOperationService = new FileOperationService(configManager, _tabController, _ledService, PluginRegistry.PluginRegistry.Instance, CreateLogWindowFromRequest, () => Clipboard.ContainsText() ? Clipboard.GetText() : null, LoadSession); _fileOperationService.FileHistoryChanged += (_, _) => FillHistoryMenu(); _fileOperationService.FileOpened += OnFileOperationServiceFileOpened; - _projectFileHandler = new ProjectFileHandler(PluginRegistry.PluginRegistry.Instance, request => _fileOperationService.AddFileTab(request)); + _sessionHandler = new SessionHandler(PluginRegistry.PluginRegistry.Instance, request => _fileOperationService.AddFileTab(request)); _logWindowCoordinator = new LogWindowCoordinator(configManager, PluginRegistry.PluginRegistry.Instance, this, _tabController, _ledService, _fileOperationService); @@ -421,8 +421,8 @@ private void ApplyContextMenuResources () multiFileToolStripMenuItem.Text = Resources.LogTabWindow_UI_ToolStripMenuItem_multiFileToolStripMenuItem; multiFileEnabledStripMenuItem.Text = Resources.LogTabWindow_UI_StripMenuItem_multiFileEnabledStripMenuItem; multifileMaskToolStripMenuItem.Text = Resources.LogTabWindow_UI_ToolStripMenuItem_multifileMaskToolStripMenuItem; - loadProjectToolStripMenuItem.Text = Resources.LogTabWindow_UI_ToolStripMenuItem_loadProjectToolStripMenuItem; - saveProjectToolStripMenuItem.Text = Resources.LogTabWindow_UI_ToolStripMenuItem_saveProjectToolStripMenuItem; + loadSessionToolStripMenuItem.Text = Resources.LogTabWindow_UI_ToolStripMenuItem_loadProjectToolStripMenuItem; + saveSessionToolStripMenuItem.Text = Resources.LogTabWindow_UI_ToolStripMenuItem_saveProjectToolStripMenuItem; exportBookmarksToolStripMenuItem.Text = Resources.LogTabWindow_UI_ToolStripMenuItem_exportBookmarksToolStripMenuItem; lastUsedToolStripMenuItem.Text = Resources.LogTabWindow_UI_ToolStripMenuItem_lastUsedToolStripMenuItem; exitToolStripMenuItem.Text = Resources.LogTabWindow_UI_ToolStripMenuItem_exitToolStripMenuItem; @@ -499,8 +499,8 @@ private void ApplyToolTips () openURIToolStripMenuItem.ToolTipText = Resources.LogTabWindow_UI_ToolStripMenuItem_ToolTip_openURIToolStripMenuItem; newFromClipboardToolStripMenuItem.ToolTipText = Resources.LogTabWindow_UI_ToolStripMenuItem_ToolTip_newFromClipboardToolStripMenuItem; multiFileToolStripMenuItem.ToolTipText = Resources.LogTabWindow_UI_ToolStripMenuItem_ToolTip_multiFileToolStripMenuItem; - loadProjectToolStripMenuItem.ToolTipText = Resources.LogTabWindow_UI_ToolStripMenuItem_ToolTip_loadProjectToolStripMenuItem; - saveProjectToolStripMenuItem.ToolTipText = Resources.LogTabWindow_UI_ToolStripMenuItem_ToolTip_saveProjectToolStripMenuItem; + loadSessionToolStripMenuItem.ToolTipText = Resources.LogTabWindow_UI_ToolStripMenuItem_ToolTip_loadProjectToolStripMenuItem; + saveSessionToolStripMenuItem.ToolTipText = Resources.LogTabWindow_UI_ToolStripMenuItem_ToolTip_saveProjectToolStripMenuItem; timeshiftToolStripMenuItem.ToolTipText = Resources.LogTabWindow_UI_ToolStripMenuItem_ToolTip_timeshiftToolStripMenuItem; copyMarkedLinesIntoNewTabToolStripMenuItem.ToolTipText = Resources.LogTabWindow_UI_ToolStripMenuItem_ToolTip_copyMarkedLinesIntoNewTabToolStripMenuItem; columnizerToolStripMenuItem.ToolTipText = Resources.LogTabWindow_UI_ToolStripMenuItem_ToolTip_columnizerToolStripMenuItem; @@ -1437,28 +1437,28 @@ private void CloseAllTabs () [SupportedOSPlatform("windows")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0010:Add missing cases", Justification = "no need for the other switch cases")] - private void LoadProject (string projectFileName, bool restoreLayout) + private void LoadSession (string sessionFileName, bool restoreLayout) { - var outcome = _projectFileHandler.LoadProject(projectFileName); + var outcome = _sessionHandler.LoadSession(sessionFileName); bool openedTabs = false; switch (outcome.Status) { - case ProjectLoadOutcome.LoadStatus.Error: + case SessionLoadOutcome.LoadStatus.Error: { ShowOkMessage(outcome.ErrorMessage ?? Resources.LogExpert_Common_UI_Title_Error, - Resources.LoadProject_UI_Message_Error_Title_ProjectLoadFailed, + Resources.LoadSession_UI_Message_Error_Title_SessionLoadFailed, MessageBoxIcon.Error); return; } - case ProjectLoadOutcome.LoadStatus.EmptyProject: + case SessionLoadOutcome.LoadStatus.EmptySession: { - ShowOkMessage(outcome.ErrorMessage ?? Resources.LoadProject_UI_Message_Error_Title_SessionLoadFailed, - Resources.LoadProject_UI_Message_Message_FilesForSessionCouldNotBeFound, + ShowOkMessage(outcome.ErrorMessage ?? Resources.LoadSession_UI_Message_Error_Title_SessionLoadFailed, + Resources.LoadSession_UI_Message_Message_FilesForSessionCouldNotBeFound, MessageBoxIcon.Error); return; } - case ProjectLoadOutcome.LoadStatus.NeedsIntervention: + case SessionLoadOutcome.LoadStatus.NeedsIntervention: { var (dialogResult, updateSessionFile, selectedAlternatives) = MissingFilesDialog.ShowDialog(outcome.ValidationResult!, outcome.HasLayoutData); @@ -1481,12 +1481,12 @@ private void LoadProject (string projectFileName, bool restoreLayout) SelectedAlternatives = selectedAlternatives }; - var interventionResult = _projectFileHandler.ContinueLoad(outcome, resolution, restoreLayout); + var interventionResult = _sessionHandler.ContinueLoad(outcome, resolution, restoreLayout); if (updateSessionFile) { - ShowOkMessage(Resources.LoadProject_UI_Message_Error_Message_UpdateSessionFile, - Resources.LoadProject_UI_Message_Error_Title_UpdateSessionFile, + ShowOkMessage(Resources.LoadSession_UI_Message_Error_Message_UpdateSessionFile, + Resources.LoadSession_UI_Message_Error_Title_UpdateSessionFile, MessageBoxIcon.Information); } @@ -1504,9 +1504,9 @@ private void LoadProject (string projectFileName, bool restoreLayout) openedTabs = interventionResult.OpenedTabs; break; } - case ProjectLoadOutcome.LoadStatus.Success: + case SessionLoadOutcome.LoadStatus.Success: { - openedTabs = _projectFileHandler.ContinueLoad(outcome, null, restoreLayout).OpenedTabs; + openedTabs = _sessionHandler.ContinueLoad(outcome, null, restoreLayout).OpenedTabs; break; } } @@ -2364,12 +2364,12 @@ private void OnLogTabWindowSizeChanged (object sender, EventArgs e) } [SupportedOSPlatform("windows")] - private void OnSaveProjectToolStripMenuItemClick (object sender, EventArgs e) + private void OnSaveSessionToolStripMenuItemClick (object sender, EventArgs e) { SaveFileDialog dlg = new() { DefaultExt = "lxj", - Filter = string.Format(CultureInfo.InvariantCulture, Resources.LogTabWindow_UI_Project_Session_Default_Filter, "(*.lxj)|*.lxj") + Filter = string.Format(CultureInfo.InvariantCulture, Resources.LogTabWindow_UI_Session_Default_Filter, "(*.lxj)|*.lxj") }; if (dlg.ShowDialog() == DialogResult.OK) @@ -2386,13 +2386,13 @@ private void OnSaveProjectToolStripMenuItemClick (object sender, EventArgs e) } } - ProjectData projectData = new() + SessionData sessionData = new() { FileNames = fileNames, TabLayoutXml = SaveLayout() }; - if (!_projectFileHandler.SaveProject(fileName, projectData, out var errorMessage)) + if (!_sessionHandler.SaveSession(fileName, sessionData, out var errorMessage)) { ShowOkMessage(errorMessage ?? Resources.LogExpert_Common_UI_Title_Error, Resources.LogExpert_Common_UI_Title_Error, @@ -2402,18 +2402,18 @@ private void OnSaveProjectToolStripMenuItemClick (object sender, EventArgs e) } [SupportedOSPlatform("windows")] - private void OnLoadProjectToolStripMenuItemClick (object sender, EventArgs e) + private void OnLoadSessionToolStripMenuItemClick (object sender, EventArgs e) { OpenFileDialog dlg = new() { DefaultExt = "lxj", - Filter = string.Format(CultureInfo.InvariantCulture, Resources.LogTabWindow_UI_Project_Session_Default_Filter, "(*.lxj)|*.lxj") + Filter = string.Format(CultureInfo.InvariantCulture, Resources.LogTabWindow_UI_Session_Default_Filter, "(*.lxj)|*.lxj") }; if (dlg.ShowDialog() == DialogResult.OK) { - var projectFileName = dlg.FileName; - LoadProject(projectFileName, true); + var sessionFileName = dlg.FileName; + LoadSession(sessionFileName, true); } } diff --git a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.designer.cs b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.designer.cs index e5f9c152..8f7bab2a 100644 --- a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.designer.cs +++ b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.designer.cs @@ -40,8 +40,8 @@ private void InitializeComponent () multiFileEnabledStripMenuItem = new ToolStripMenuItem(); multifileMaskToolStripMenuItem = new ToolStripMenuItem(); ToolStripSeparator2 = new ToolStripSeparator(); - loadProjectToolStripMenuItem = new ToolStripMenuItem(); - saveProjectToolStripMenuItem = new ToolStripMenuItem(); + loadSessionToolStripMenuItem = new ToolStripMenuItem(); + saveSessionToolStripMenuItem = new ToolStripMenuItem(); exportBookmarksToolStripMenuItem = new ToolStripMenuItem(); ToolStripSeparator3 = new ToolStripSeparator(); lastUsedToolStripMenuItem = new ToolStripMenuItem(); @@ -205,7 +205,7 @@ private void InitializeComponent () // // fileToolStripMenuItem // - fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { openToolStripMenuItem, openURIToolStripMenuItem, closeFileToolStripMenuItem, reloadToolStripMenuItem, newFromClipboardToolStripMenuItem, ToolStripSeparator1, multiFileToolStripMenuItem, ToolStripSeparator2, loadProjectToolStripMenuItem, saveProjectToolStripMenuItem, exportBookmarksToolStripMenuItem, ToolStripSeparator3, lastUsedToolStripMenuItem, exitToolStripMenuItem }); + fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { openToolStripMenuItem, openURIToolStripMenuItem, closeFileToolStripMenuItem, reloadToolStripMenuItem, newFromClipboardToolStripMenuItem, ToolStripSeparator1, multiFileToolStripMenuItem, ToolStripSeparator2, loadSessionToolStripMenuItem, saveSessionToolStripMenuItem, exportBookmarksToolStripMenuItem, ToolStripSeparator3, lastUsedToolStripMenuItem, exitToolStripMenuItem }); fileToolStripMenuItem.Name = "fileToolStripMenuItem"; fileToolStripMenuItem.Size = new Size(37, 19); fileToolStripMenuItem.Text = "File"; @@ -296,21 +296,21 @@ private void InitializeComponent () ToolStripSeparator2.Name = "ToolStripSeparator2"; ToolStripSeparator2.Size = new Size(240, 6); // - // loadProjectToolStripMenuItem + // loadSessionToolStripMenuItem // - loadProjectToolStripMenuItem.Name = "loadProjectToolStripMenuItem"; - loadProjectToolStripMenuItem.Size = new Size(243, 22); - loadProjectToolStripMenuItem.Text = "Load session..."; - loadProjectToolStripMenuItem.ToolTipText = "Load a saved session (list of log files)"; - loadProjectToolStripMenuItem.Click += OnLoadProjectToolStripMenuItemClick; + loadSessionToolStripMenuItem.Name = "loadSessionToolStripMenuItem"; + loadSessionToolStripMenuItem.Size = new Size(243, 22); + loadSessionToolStripMenuItem.Text = "Load session..."; + loadSessionToolStripMenuItem.ToolTipText = "Load a saved session (list of log files)"; + loadSessionToolStripMenuItem.Click += OnLoadSessionToolStripMenuItemClick; // - // saveProjectToolStripMenuItem + // saveSessionToolStripMenuItem // - saveProjectToolStripMenuItem.Name = "saveProjectToolStripMenuItem"; - saveProjectToolStripMenuItem.Size = new Size(243, 22); - saveProjectToolStripMenuItem.Text = "Save session..."; - saveProjectToolStripMenuItem.ToolTipText = "Save a session (all open tabs)"; - saveProjectToolStripMenuItem.Click += OnSaveProjectToolStripMenuItemClick; + saveSessionToolStripMenuItem.Name = "saveSessionToolStripMenuItem"; + saveSessionToolStripMenuItem.Size = new Size(243, 22); + saveSessionToolStripMenuItem.Text = "Save session..."; + saveSessionToolStripMenuItem.ToolTipText = "Save a session (all open tabs)"; + saveSessionToolStripMenuItem.Click += OnSaveSessionToolStripMenuItemClick; // // exportBookmarksToolStripMenuItem // @@ -1169,8 +1169,8 @@ private void InitializeComponent () private ToolStripMenuItem closeOtherTabsToolStripMenuItem; private ToolStripMenuItem closeAllTabsToolStripMenuItem; private ToolStripMenuItem tabColorToolStripMenuItem; - private ToolStripMenuItem loadProjectToolStripMenuItem; - private ToolStripMenuItem saveProjectToolStripMenuItem; + private ToolStripMenuItem loadSessionToolStripMenuItem; + private ToolStripMenuItem saveSessionToolStripMenuItem; private ToolStripButton toolStripButtonBubbles; private ToolStripMenuItem copyPathToClipboardToolStripMenuItem; private ToolStripMenuItem findInExplorerToolStripMenuItem; diff --git a/src/LogExpert.UI/Dialogs/MissingFilesDialog.cs b/src/LogExpert.UI/Dialogs/MissingFilesDialog.cs index 74e85f4e..91dec327 100644 --- a/src/LogExpert.UI/Dialogs/MissingFilesDialog.cs +++ b/src/LogExpert.UI/Dialogs/MissingFilesDialog.cs @@ -16,7 +16,7 @@ public partial class MissingFilesDialog : Form { #region Fields - private readonly ProjectValidationResult _validationResult; + private readonly SessionValidationResult _validationResult; private readonly Dictionary _fileItems; private readonly bool _hasLayoutData; @@ -50,7 +50,7 @@ public partial class MissingFilesDialog : Form /// Validation result containing file information /// Whether to show layout restoration options /// Whether the project has layout data to restore - public MissingFilesDialog (ProjectValidationResult validationResult, bool hasLayoutData = false) + public MissingFilesDialog (SessionValidationResult validationResult, bool hasLayoutData = false) { ArgumentNullException.ThrowIfNull(validationResult); @@ -78,7 +78,7 @@ public MissingFilesDialog (ProjectValidationResult validationResult, bool hasLay /// Validation result /// Whether the project has layout data /// Tuple containing the dialog result, whether to update session file, and selected alternatives - public static (MissingFilesDialogResult Result, bool UpdateSessionFile, Dictionary SelectedAlternatives) ShowDialog (ProjectValidationResult validationResult, bool hasLayoutData) + public static (MissingFilesDialogResult Result, bool UpdateSessionFile, Dictionary SelectedAlternatives) ShowDialog (SessionValidationResult validationResult, bool hasLayoutData) { using var dialog = new MissingFilesDialog(validationResult, hasLayoutData); _ = dialog.ShowDialog(); diff --git a/src/LogExpert.UI/Dialogs/ProjectLoadDlg.Designer.cs b/src/LogExpert.UI/Dialogs/SessionLoadDlg.Designer.cs similarity index 96% rename from src/LogExpert.UI/Dialogs/ProjectLoadDlg.Designer.cs rename to src/LogExpert.UI/Dialogs/SessionLoadDlg.Designer.cs index 3e5677c3..4e38dbdb 100644 --- a/src/LogExpert.UI/Dialogs/ProjectLoadDlg.Designer.cs +++ b/src/LogExpert.UI/Dialogs/SessionLoadDlg.Designer.cs @@ -1,6 +1,6 @@ namespace LogExpert.Dialogs; -partial class ProjectLoadDlg +partial class SessionLoadDlg { /// /// Required designer variable. @@ -28,7 +28,7 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ProjectLoadDlg)); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SessionLoadDlg)); this.labelInformational = new System.Windows.Forms.Label(); this.buttonCloseTabs = new System.Windows.Forms.Button(); this.buttonNewWindow = new System.Windows.Forms.Button(); @@ -88,7 +88,7 @@ private void InitializeComponent() this.labelChooseHowToProceed.TabIndex = 4; this.labelChooseHowToProceed.Text = "Please choose how to proceed:"; // - // ProjectLoadDlg + // SessionLoadDlg // this.ClientSize = new System.Drawing.Size(258, 196); this.Controls.Add(this.labelChooseHowToProceed); @@ -100,7 +100,7 @@ private void InitializeComponent() this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MaximizeBox = false; this.MinimizeBox = false; - this.Name = "ProjectLoadDlg"; + this.Name = "SessionLoadDlg"; this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Loading Session"; diff --git a/src/LogExpert.UI/Dialogs/ProjectLoadDlg.cs b/src/LogExpert.UI/Dialogs/SessionLoadDlg.cs similarity index 80% rename from src/LogExpert.UI/Dialogs/ProjectLoadDlg.cs rename to src/LogExpert.UI/Dialogs/SessionLoadDlg.cs index 5b0d78ff..8d8c3f19 100644 --- a/src/LogExpert.UI/Dialogs/ProjectLoadDlg.cs +++ b/src/LogExpert.UI/Dialogs/SessionLoadDlg.cs @@ -6,7 +6,7 @@ namespace LogExpert.Dialogs; [SupportedOSPlatform("windows")] -internal partial class ProjectLoadDlg : Form +internal partial class SessionLoadDlg : Form { #region Fields @@ -14,7 +14,7 @@ internal partial class ProjectLoadDlg : Form #region cTor - public ProjectLoadDlg () + public SessionLoadDlg () { SuspendLayout(); @@ -43,7 +43,7 @@ private void ApplyResources () #region Properties [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] - public ProjectLoadDlgResult ProjectLoadResult { get; set; } = ProjectLoadDlgResult.Cancel; + public SessionLoadDlgResult SessionLoadResult { get; set; } = SessionLoadDlgResult.Cancel; #endregion @@ -51,19 +51,19 @@ private void ApplyResources () private void OnButtonCloseTabsClick (object sender, EventArgs e) { - ProjectLoadResult = ProjectLoadDlgResult.CloseTabs; + SessionLoadResult = SessionLoadDlgResult.CloseTabs; Close(); } private void OnButtonNewWindowClick (object sender, EventArgs e) { - ProjectLoadResult = ProjectLoadDlgResult.NewWindow; + SessionLoadResult = SessionLoadDlgResult.NewWindow; Close(); } private void OnButtonIgnoreClick (object sender, EventArgs e) { - ProjectLoadResult = ProjectLoadDlgResult.IgnoreLayout; + SessionLoadResult = SessionLoadDlgResult.IgnoreLayout; Close(); } diff --git a/src/LogExpert.UI/Dialogs/ProjectLoadDlg.resx b/src/LogExpert.UI/Dialogs/SessionLoadDlg.resx similarity index 100% rename from src/LogExpert.UI/Dialogs/ProjectLoadDlg.resx rename to src/LogExpert.UI/Dialogs/SessionLoadDlg.resx diff --git a/src/LogExpert.UI/Interface/IFileOperationService.cs b/src/LogExpert.UI/Interface/IFileOperationService.cs index ec64a34f..0547f4b4 100644 --- a/src/LogExpert.UI/Interface/IFileOperationService.cs +++ b/src/LogExpert.UI/Interface/IFileOperationService.cs @@ -33,7 +33,7 @@ internal interface IFileOperationService /// /// Convenience for AddFileTab with DoNotAddToDockPanel = true. - /// Used by LoadProject for layout-restored windows. + /// Used by LoadSession for layout-restored windows. /// LogWindow AddFileTabDeferred (string fileName, bool isTempFile, string? title, bool forcePersistenceLoading, ILogLineMemoryColumnizer? preProcessColumnizer); diff --git a/src/LogExpert.UI/Interface/IProjectFileHandler.cs b/src/LogExpert.UI/Interface/IProjectFileHandler.cs deleted file mode 100644 index dff9d966..00000000 --- a/src/LogExpert.UI/Interface/IProjectFileHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -using LogExpert.Core.Classes.Persister; -using LogExpert.UI.Services.ProjectFileHandlerService; - -namespace LogExpert.UI.Interface; - -internal interface IProjectFileHandler -{ - ProjectLoadOutcome LoadProject (string projectFileName); - - ContinueLoadResult ContinueLoad (ProjectLoadOutcome loadOutcome, MissingFilesResolution? resolution, bool restoreLayout); - - bool SaveProject (string projectFileName, ProjectData projectData, out string? errorMessage); -} \ No newline at end of file diff --git a/src/LogExpert.UI/Interface/ISessionHandler.cs b/src/LogExpert.UI/Interface/ISessionHandler.cs new file mode 100644 index 00000000..cd5b913c --- /dev/null +++ b/src/LogExpert.UI/Interface/ISessionHandler.cs @@ -0,0 +1,13 @@ +using LogExpert.Core.Classes.Persister; +using LogExpert.UI.Services.SessionHandlerService; + +namespace LogExpert.UI.Interface; + +internal interface ISessionHandler +{ + SessionLoadOutcome LoadSession (string sessionFileName); + + ContinueLoadResult ContinueLoad (SessionLoadOutcome loadOutcome, MissingFilesResolution? resolution, bool restoreLayout); + + bool SaveSession (string sessionFileName, SessionData sessionData, out string? errorMessage); +} \ No newline at end of file diff --git a/src/LogExpert.UI/Services/FileOperationService/FileOperationService.cs b/src/LogExpert.UI/Services/FileOperationService/FileOperationService.cs index 56a6a3f6..a8a77d51 100644 --- a/src/LogExpert.UI/Services/FileOperationService/FileOperationService.cs +++ b/src/LogExpert.UI/Services/FileOperationService/FileOperationService.cs @@ -22,7 +22,7 @@ internal sealed class FileOperationService ( IPluginRegistry pluginRegistry, Func logWindowFactory, Func clipboardTextProvider, - Action projectFileCallback) : IFileOperationService + Action loadSessionCallback) : IFileOperationService { private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); @@ -32,7 +32,7 @@ internal sealed class FileOperationService ( private readonly IPluginRegistry _pluginRegistry = pluginRegistry; private readonly Func _logWindowFactory = logWindowFactory; private readonly Func _clipboardTextProvider = clipboardTextProvider; - private readonly Action _projectFileCallback = projectFileCallback; + private readonly Action _loadSessionCallback = loadSessionCallback; public event EventHandler? FileHistoryChanged; public event EventHandler? FileOpened; @@ -189,8 +189,9 @@ public LogWindow AddTempFileTab (string fileName, string title) { foreach (var sessionFile in fileNames.Where(f => f.EndsWith(".lxj", StringComparison.OrdinalIgnoreCase))) { - _projectFileCallback(sessionFile, false); + _loadSessionCallback(sessionFile, false); } + var logs = fileNames.Where(f => !f.EndsWith(".lxj", StringComparison.OrdinalIgnoreCase)).ToArray(); return logs.Length > 0 ? AddMultiFileTab(logs) : null; } @@ -231,7 +232,7 @@ public MultiFileDecision LoadFilesWithOption (string[] fileNames, bool invertLog { if (fileNames[0].EndsWith(".lxj", StringComparison.OrdinalIgnoreCase)) { - _projectFileCallback(fileNames[0], true); + _loadSessionCallback(fileNames[0], true); return MultiFileDecision.Cancel; // Already handled } @@ -269,7 +270,7 @@ public void AddFileTabs (string[] fileNames) { if (fileName.EndsWith(".lxj", StringComparison.OrdinalIgnoreCase)) { - _projectFileCallback(fileName, false); + _loadSessionCallback(fileName, false); } else { diff --git a/src/LogExpert.UI/Services/ProjectFileHandlerService/ContinueLoadResult.cs b/src/LogExpert.UI/Services/SessionHandlerService/ContinueLoadResult.cs similarity index 77% rename from src/LogExpert.UI/Services/ProjectFileHandlerService/ContinueLoadResult.cs rename to src/LogExpert.UI/Services/SessionHandlerService/ContinueLoadResult.cs index ee71c88a..250eaa02 100644 --- a/src/LogExpert.UI/Services/ProjectFileHandlerService/ContinueLoadResult.cs +++ b/src/LogExpert.UI/Services/SessionHandlerService/ContinueLoadResult.cs @@ -1,4 +1,4 @@ -namespace LogExpert.UI.Services.ProjectFileHandlerService; +namespace LogExpert.UI.Services.SessionHandlerService; internal readonly record struct ContinueLoadResult { diff --git a/src/LogExpert.UI/Services/ProjectFileHandlerService/MissingFilesResolution.cs b/src/LogExpert.UI/Services/SessionHandlerService/MissingFilesResolution.cs similarity index 82% rename from src/LogExpert.UI/Services/ProjectFileHandlerService/MissingFilesResolution.cs rename to src/LogExpert.UI/Services/SessionHandlerService/MissingFilesResolution.cs index 0a14b1b3..53d83334 100644 --- a/src/LogExpert.UI/Services/ProjectFileHandlerService/MissingFilesResolution.cs +++ b/src/LogExpert.UI/Services/SessionHandlerService/MissingFilesResolution.cs @@ -1,4 +1,4 @@ -namespace LogExpert.UI.Services.ProjectFileHandlerService; +namespace LogExpert.UI.Services.SessionHandlerService; internal readonly record struct MissingFilesResolution { diff --git a/src/LogExpert.UI/Services/ProjectFileHandlerService/ProjectFileHandler.cs b/src/LogExpert.UI/Services/SessionHandlerService/SessionHandler.cs similarity index 62% rename from src/LogExpert.UI/Services/ProjectFileHandlerService/ProjectFileHandler.cs rename to src/LogExpert.UI/Services/SessionHandlerService/SessionHandler.cs index 6071fca1..2178b37c 100644 --- a/src/LogExpert.UI/Services/ProjectFileHandlerService/ProjectFileHandler.cs +++ b/src/LogExpert.UI/Services/SessionHandlerService/SessionHandler.cs @@ -8,71 +8,71 @@ using NLog; -namespace LogExpert.UI.Services.ProjectFileHandlerService; +namespace LogExpert.UI.Services.SessionHandlerService; [SupportedOSPlatform("windows")] -internal sealed class ProjectFileHandler ( +internal sealed class SessionHandler ( IPluginRegistry pluginRegistry, Func addFileTab) - : IProjectFileHandler + : ISessionHandler { private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); - public ProjectLoadOutcome LoadProject (string projectFileName) + public SessionLoadOutcome LoadSession (string sessionFileName) { try { - if (!File.Exists(projectFileName)) + if (!File.Exists(sessionFileName)) { - _logger.Warn("LoadProject: File does not exist: {FileName}", projectFileName); + _logger.Warn("LoadSession: File does not exist: {FileName}", sessionFileName); - return new ProjectLoadOutcome + return new SessionLoadOutcome { - Status = ProjectLoadOutcome.LoadStatus.Error, - ErrorMessage = $"Project file not found: {projectFileName}" + Status = SessionLoadOutcome.LoadStatus.Error, + ErrorMessage = $"Project file not found: {sessionFileName}" }; } - var loadResult = ProjectPersister.LoadProjectData(projectFileName, pluginRegistry); + var loadResult = SessionPersister.LoadSessionData(sessionFileName, pluginRegistry); - if (loadResult?.ProjectData == null) + if (loadResult?.SessionData == null) { - _logger.Warn("LoadProject: ProjectData is null for {FileName}", projectFileName); + _logger.Warn("LoadSession: SessionData is null for {FileName}", sessionFileName); - return new ProjectLoadOutcome + return new SessionLoadOutcome { - Status = ProjectLoadOutcome.LoadStatus.Error, - ErrorMessage = Resources.LoadProject_UI_Message_Error_FileMaybeCorruptedOrInaccessible + Status = SessionLoadOutcome.LoadStatus.Error, + ErrorMessage = Resources.LoadSession_UI_Message_Error_FileMaybeCorruptedOrInaccessible }; } - var projectData = loadResult.ProjectData; + var sessionData = loadResult.SessionData; - if (projectData.FileNames.Count == 0) + if (sessionData.FileNames.Count == 0) { - _logger.Warn("LoadProject: No files in project {FileName}", projectFileName); + _logger.Warn("LoadSession: No files in project {FileName}", sessionFileName); - return new ProjectLoadOutcome + return new SessionLoadOutcome { - Status = ProjectLoadOutcome.LoadStatus.EmptyProject, - ErrorMessage = Resources.LoadProject_UI_Message_Message_FilesForSessionCouldNotBeFound + Status = SessionLoadOutcome.LoadStatus.EmptySession, + ErrorMessage = Resources.LoadSession_UI_Message_Message_FilesForSessionCouldNotBeFound }; } - var layoutXml = projectData.TabLayoutXml; + var layoutXml = sessionData.TabLayoutXml; return loadResult.RequiresUserIntervention - ? new ProjectLoadOutcome + ? new SessionLoadOutcome { - Status = ProjectLoadOutcome.LoadStatus.NeedsIntervention, - ProjectData = projectData, + Status = SessionLoadOutcome.LoadStatus.NeedsIntervention, + SessionData = sessionData, ValidationResult = loadResult.ValidationResult, LayoutXml = layoutXml } - : new ProjectLoadOutcome + : new SessionLoadOutcome { - Status = ProjectLoadOutcome.LoadStatus.Success, - ProjectData = projectData, + Status = SessionLoadOutcome.LoadStatus.Success, + SessionData = sessionData, LayoutXml = layoutXml }; } @@ -81,50 +81,50 @@ UnauthorizedAccessException or InvalidOperationException or Newtonsoft.Json.JsonException) { - _logger.Error(ex, "LoadProject: Exception loading {FileName}", projectFileName); + _logger.Error(ex, "LoadSession: Exception loading {FileName}", sessionFileName); - return new ProjectLoadOutcome + return new SessionLoadOutcome { - Status = ProjectLoadOutcome.LoadStatus.Error, + Status = SessionLoadOutcome.LoadStatus.Error, ErrorMessage = $"Error loading project: {ex.Message}" }; } } - public ContinueLoadResult ContinueLoad (ProjectLoadOutcome loadOutcome, MissingFilesResolution? resolution, bool restoreLayout) + public ContinueLoadResult ContinueLoad (SessionLoadOutcome loadOutcome, MissingFilesResolution? resolution, bool restoreLayout) { ArgumentNullException.ThrowIfNull(loadOutcome); - ArgumentNullException.ThrowIfNull(loadOutcome.ProjectData); + ArgumentNullException.ThrowIfNull(loadOutcome.SessionData); - var projectData = loadOutcome.ProjectData; + var sessionData = loadOutcome.SessionData; // Apply selected alternatives to file paths (in-place, exact string match) if (resolution?.SelectedAlternatives is { } alternatives) { - for (int i = 0; i < projectData.FileNames.Count; i++) + for (int i = 0; i < sessionData.FileNames.Count; i++) { - var originalPath = projectData.FileNames[i]; + var originalPath = sessionData.FileNames[i]; if (alternatives.TryGetValue(originalPath, out var replacement)) { - projectData.FileNames[i] = replacement; + sessionData.FileNames[i] = replacement; } } } // Save updated session file if requested - if (resolution is { UpdateSessionFile: true } && !string.IsNullOrEmpty(projectData.ProjectFilePath)) + if (resolution is { UpdateSessionFile: true } && !string.IsNullOrEmpty(sessionData.SessionFilePath)) { try { - ProjectPersister.SaveProjectData(projectData.ProjectFilePath, projectData); - _logger.Info("ContinueLoad: Updated session file {FileName}", projectData.ProjectFilePath); + SessionPersister.SaveSessionData(sessionData.SessionFilePath, sessionData); + _logger.Info("ContinueLoad: Updated session file {FileName}", sessionData.SessionFilePath); } catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or InvalidOperationException or ArgumentException) { - _logger.Error(ex, "ContinueLoad: Failed to update session file {FileName}", projectData.ProjectFilePath); + _logger.Error(ex, "ContinueLoad: Failed to update session file {FileName}", sessionData.SessionFilePath); } } @@ -132,7 +132,7 @@ InvalidOperationException or if (resolution is { OpenInNewWindow: true }) { var resolvedFiles = PersisterHelpers.FindFilenameForSettings( - projectData.FileNames.AsReadOnly(), pluginRegistry); + sessionData.FileNames.AsReadOnly(), pluginRegistry); return new ContinueLoadResult { @@ -146,7 +146,7 @@ InvalidOperationException or bool deferForLayout = loadOutcome.HasLayoutData && restoreLayout; int openedCount = 0; - foreach (var fileName in projectData.FileNames) + foreach (var fileName in sessionData.FileNames) { var request = new FileTabRequest { @@ -181,11 +181,11 @@ and not InvalidProgramException }; } - public bool SaveProject (string projectFileName, ProjectData projectData, out string? errorMessage) + public bool SaveSession (string sessionFileName, SessionData sessionData, out string? errorMessage) { try { - ProjectPersister.SaveProjectData(projectFileName, projectData); + SessionPersister.SaveSessionData(sessionFileName, sessionData); errorMessage = null; return true; } @@ -194,7 +194,7 @@ UnauthorizedAccessException or InvalidOperationException or ArgumentException) { - _logger.Error(ex, "SaveProject: Failed to save {FileName}", projectFileName); + _logger.Error(ex, "SaveSession: Failed to save {FileName}", sessionFileName); errorMessage = $"Error saving project: {ex.Message}"; return false; } diff --git a/src/LogExpert.UI/Services/ProjectFileHandlerService/ProjectLoadOutcome.cs b/src/LogExpert.UI/Services/SessionHandlerService/SessionLoadOutcome.cs similarity index 60% rename from src/LogExpert.UI/Services/ProjectFileHandlerService/ProjectLoadOutcome.cs rename to src/LogExpert.UI/Services/SessionHandlerService/SessionLoadOutcome.cs index d1e352e7..146d6c27 100644 --- a/src/LogExpert.UI/Services/ProjectFileHandlerService/ProjectLoadOutcome.cs +++ b/src/LogExpert.UI/Services/SessionHandlerService/SessionLoadOutcome.cs @@ -1,22 +1,22 @@ using LogExpert.Core.Classes.Persister; -namespace LogExpert.UI.Services.ProjectFileHandlerService; +namespace LogExpert.UI.Services.SessionHandlerService; -internal sealed class ProjectLoadOutcome +internal sealed class SessionLoadOutcome { public enum LoadStatus { Success, NeedsIntervention, Error, - EmptyProject + EmptySession } public required LoadStatus Status { get; init; } - public ProjectData? ProjectData { get; init; } + public SessionData? SessionData { get; init; } - public ProjectValidationResult? ValidationResult { get; init; } + public SessionValidationResult? ValidationResult { get; init; } public string? LayoutXml { get; init; } diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index 1a47eae3..e387aad4 100644 --- a/src/PluginRegistry/PluginHashGenerator.Generated.cs +++ b/src/PluginRegistry/PluginHashGenerator.Generated.cs @@ -10,7 +10,7 @@ public static partial class PluginValidator { /// /// Gets pre-calculated SHA256 hashes for built-in plugins. - /// Generated: 2026-05-28 14:55:19 UTC + /// Generated: 2026-05-28 19:52:00 UTC /// Configuration: Release /// Plugin count: 21 /// @@ -18,27 +18,27 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "3EA41A42F0A17C1DDAE3807E899B8F0E4B6A3CFCD0412FB04370C29F218A34C9", + ["AutoColumnizer.dll"] = "EDF4B48F71CF2192A99F63B9FE493661521A2E671D9185CCEB181B513DF0C1A5", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "31343A536E79669DBC292CBFC728CB63F77EA661C4E9F36FE4E3025166BA444B", - ["CsvColumnizer.dll (x86)"] = "31343A536E79669DBC292CBFC728CB63F77EA661C4E9F36FE4E3025166BA444B", - ["DefaultPlugins.dll"] = "3820088CC0BFB6584BB48BA3F45710D2DD3076163F82FDC41400BF467611444C", - ["FlashIconHighlighter.dll"] = "07518957DFE748B01D86870CFC3AE05AE8DE2EBE602C268997585A2F6AE3D505", - ["GlassfishColumnizer.dll"] = "77DC1A20B32491055DDE72F159894AE6889218BA2D1F2AF709D3388E83371AF5", - ["JsonColumnizer.dll"] = "846B45CFD8B3E24E12B065AC0D0F0252BFCDEDA8CA8E4E06A67B2200662CB5AE", - ["JsonCompactColumnizer.dll"] = "0E9BC4CEF045D3C2C8BDACA96B40A469EF65BB80326332A0F8325769855D65D8", - ["Log4jXmlColumnizer.dll"] = "665546EBF0D55758214034CBD026D54B6B018FF90D240076659E674B2350E7CD", - ["LogExpert.Resources.dll"] = "202FE9534087C11461DFF3A8653410181EF481C8A3C40AB96868389189B49DCE", + ["CsvColumnizer.dll"] = "397CCA331A6FD1687C8CBDF78DF6DF9C080B7A16860C511F2DC0E275A5257C4F", + ["CsvColumnizer.dll (x86)"] = "397CCA331A6FD1687C8CBDF78DF6DF9C080B7A16860C511F2DC0E275A5257C4F", + ["DefaultPlugins.dll"] = "58B2ED626556C1B0E1B02D62DF0A0658618FA05B309BFAA2C2582B180594B7E6", + ["FlashIconHighlighter.dll"] = "0861244E3F7B52D44B8487732724E277658143E5940AACFF977AA74048FF1B9D", + ["GlassfishColumnizer.dll"] = "B75D7808007E9CA1235E282B07431C70A16D378B647B58BAEE605075D7B8FF08", + ["JsonColumnizer.dll"] = "A469FEC6C1E6F7B209D960CE7C3416CF80EFD5A2A062BCAD7B0E8AE6A3988ABF", + ["JsonCompactColumnizer.dll"] = "F6735973634AE8A986BC9FD8B89F8817D36458E488B6A7BCD2320883DD4BA5BC", + ["Log4jXmlColumnizer.dll"] = "D148D1FEC9A0152714AAE8CBAB0A9BDDF9ACFBFED3FF0CEB8D5966908DC24760", + ["LogExpert.Resources.dll"] = "A25FDA182EECF5BCC828C0C3F145FD774530980E9C72AC17591043677FA9E201", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll (x86)"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.Logging.Abstractions.dll"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", ["Microsoft.Extensions.Logging.Abstractions.dll (x86)"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", - ["RegexColumnizer.dll"] = "4461F126C06332E732970E2D90DD8289C2F004B5B4BFF26AAC1EE6132717FEEA", - ["SftpFileSystem.dll"] = "F77A0C6C717E5BDB3CDB03DBE68DF1E39413E004893B4DDD2BEE5E59B7DB140B", - ["SftpFileSystem.dll (x86)"] = "6DD7950C31A3BDEE31B283BE78C17FEEDE8D2CDA13869A98FBEEA2F6C9BFFC16", - ["SftpFileSystem.Resources.dll"] = "4ED850E091351011208213BA1D8DDC2371D115316D87A7410B0C1AC901492BE8", - ["SftpFileSystem.Resources.dll (x86)"] = "4ED850E091351011208213BA1D8DDC2371D115316D87A7410B0C1AC901492BE8", + ["RegexColumnizer.dll"] = "C0DC4D43DB02C2015A490BC4C62549D7CDD1B107C6944F20DB0B4C4285371288", + ["SftpFileSystem.dll"] = "AAD426FB4E53916B8B26F427374EA3E6706B940564F4CD0E059E69A613CBE02B", + ["SftpFileSystem.dll (x86)"] = "6FEC9B0EC9B9241ACA09999C55AD96F33FD52A2A1A14DC513D06BC42BF2C1B86", + ["SftpFileSystem.Resources.dll"] = "0F8C4D65FE7E8A79A11DF521116E36E65AC28DE732C67264A3790AD2F1E4CBB8", + ["SftpFileSystem.Resources.dll (x86)"] = "0F8C4D65FE7E8A79A11DF521116E36E65AC28DE732C67264A3790AD2F1E4CBB8", }; }