Skip to content

Commit 5bf8015

Browse files
authored
Merge pull request #519 from LogExperts/514-loading-as-session-file-leads-to-broken-ui-if-a-log-file-in-session-is-not-found
This pull request introduces several enhancements and new utilities for managing and validating project file references, especially in the context of handling `.lxp` persistence files and improving file history and error handling. The changes add robust helpers for resolving, validating, and mapping project file references, provide mechanisms for suggesting alternative file locations, and improve error resilience in file operations. **Key changes include new helpers for file resolution and validation, improvements to project file loading, and enhanced error handling.** ### Project File Resolution & Validation * Added `ProjectFileResolver` and `ProjectFileValidator` utility classes to resolve project file references (including `.lxp` persistence files) to actual log files and to validate file existence, suggest alternatives, and handle plugin-based file systems. (`src/LogExpert.Core/Classes/Persister/ProjectFileResolver.cs` [[1]](diffhunk://#diff-c97308928b24e1acaf08b01001575c38fde84c91fd58a08ec8f0f0dc724dd32cR1-R34) `src/LogExpert.Core/Classes/Persister/ProjectFileValidator.cs` [[2]](diffhunk://#diff-25f80a76f76ad97c518796ad90a24c99899d5333c15ce34773dd4d52369f594aR1-R251) * Introduced `ProjectLoadResult` to encapsulate project loading results, including validation status, mappings from log files to original references, and user intervention flags. (`src/LogExpert.Core/Classes/Persister/ProjectLoadResult.cs` [src/LogExpert.Core/Classes/Persister/ProjectLoadResult.csR1-R35](diffhunk://#diff-81f8d066d1d4e13a51237b6d95b0a0f887b86309d110527010010592e32b0996R1-R35)) ### Project Persistence and Data Model * Enhanced `ProjectPersister.LoadProjectData` to use the new validation and resolution helpers, returning a `ProjectLoadResult` instead of raw `ProjectData`. (`src/LogExpert.Core/Classes/Persister/ProjectPersister.cs` [[1]](diffhunk://#diff-3ef68afcef4c2dd062a4eac57011dd212a9b67cbec4d3d536d3147fde607ac42R3-R4) [[2]](diffhunk://#diff-3ef68afcef4c2dd062a4eac57011dd212a9b67cbec4d3d536d3147fde607ac42L16-R24) * Updated `ProjectData` to include a `ProjectFilePath` property for improved context in file validation and alternative search. (`src/LogExpert.Core/Classes/Persister/ProjectData.cs` [src/LogExpert.Core/Classes/Persister/ProjectData.csR18-R22](diffhunk://#diff-b2b14f679e01b1ca86200e829986b5b05e21178471200d744f3f688de5715f9eR18-R22)) ### File Reference Resolution Helpers * Added `PersisterHelpers` with methods to resolve `.lxp` settings files to their contained log file paths, supporting plugin-based file systems and collections of file names. (`src/LogExpert.Core/Classes/Persister/PersisterHelpers.cs` [src/LogExpert.Core/Classes/Persister/PersisterHelpers.csR1-R71](diffhunk://#diff-9f15d9ff4e5401af8904f6820f7925779dcf6a8695eef85c558ea48fdb749c0dR1-R71)) ### Configuration and Error Handling Improvements * Improved error handling in config and persistence loading by including additional exception types (e.g., `JsonSerializationException`, `FileNotFoundException`) in catch blocks. (`src/LogExpert.Configuration/ConfigManager.cs` [[1]](diffhunk://#diff-82e64762f3f98762e436cfbe90148c10be3527395d43c7f4182c274020abb63cL810-R849) `src/LogExpert.Core/Classes/Persister/PersisterXML.cs` [[2]](diffhunk://#diff-344fe0500e61ba45bffd7be7db326b8af8860bfc091f9512646e25d7fd26ba39L604-R605) * Added methods to manage file history in `ConfigManager`, including limiting history size and clearing the last open files list. (`src/LogExpert.Configuration/ConfigManager.cs` [[1]](diffhunk://#diff-82e64762f3f98762e436cfbe90148c10be3527395d43c7f4182c274020abb63cR49) [[2]](diffhunk://#diff-82e64762f3f98762e436cfbe90148c10be3527395d43c7f4182c274020abb63cR255-R291) These changes collectively make project file handling more robust, user-friendly, and extensible, especially in environments with diverse file systems and potential for missing or relocated files.
2 parents 88ff2f0 + fe6f6d1 commit 5bf8015

28 files changed

+3115
-252
lines changed

src/LogExpert.Configuration/ConfigManager.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public class ConfigManager : IConfigManager
4646
};
4747

4848
private const string SETTINGS_FILE_NAME = "settings.json";
49+
private const int MAX_FILE_HISTORY = 10;
4950

5051
#endregion
5152

@@ -251,6 +252,43 @@ public void ImportHighlightSettings (FileInfo fileInfo, ExportImportFlags import
251252
Save(SettingsFlags.All);
252253
}
253254

255+
/// <summary>
256+
/// Adds the specified file name to the file history list, moving it to the top if it already exists.
257+
/// </summary>
258+
/// <remarks>If the file name already exists in the history, it is moved to the top of the list. The file
259+
/// history list is limited to a maximum number of entries; the oldest entries are removed if the limit is exceeded.
260+
/// This method is supported only on Windows platforms.</remarks>
261+
/// <param name="fileName">The name of the file to add to the file history list. Comparison is case-insensitive.</param>
262+
[SupportedOSPlatform("windows")]
263+
public void AddToFileHistory (string fileName)
264+
{
265+
bool findName (string s) => s.ToUpperInvariant().Equals(fileName.ToUpperInvariant(), StringComparison.Ordinal);
266+
267+
var index = Instance.Settings.FileHistoryList.FindIndex(findName);
268+
269+
if (index != -1)
270+
{
271+
Instance.Settings.FileHistoryList.RemoveAt(index);
272+
}
273+
274+
Instance.Settings.FileHistoryList.Insert(0, fileName);
275+
276+
while (Instance.Settings.FileHistoryList.Count > MAX_FILE_HISTORY)
277+
{
278+
Instance.Settings.FileHistoryList.RemoveAt(Instance.Settings.FileHistoryList.Count - 1);
279+
}
280+
281+
Save(SettingsFlags.FileHistory);
282+
}
283+
284+
public void ClearLastOpenFilesList ()
285+
{
286+
lock (_loadSaveLock)
287+
{
288+
Instance.Settings.LastOpenFilesList.Clear();
289+
}
290+
}
291+
254292
#endregion
255293

256294
#region Private Methods
@@ -807,7 +845,8 @@ IOException or
807845
NotSupportedException or
808846
PathTooLongException or
809847
UnauthorizedAccessException or
810-
SecurityException)
848+
SecurityException or
849+
JsonSerializationException)
811850
{
812851
_logger.Error($"Error while deserializing config data: {e}");
813852
newGroups = [];
@@ -1060,6 +1099,7 @@ private bool ValidateSettings (Settings settings)
10601099

10611100
return true;
10621101
}
1102+
10631103
#endregion
10641104

10651105
/// <summary>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System.Collections.ObjectModel;
2+
3+
using LogExpert.Core.Interface;
4+
5+
namespace LogExpert.Core.Classes.Persister;
6+
7+
public static class PersisterHelpers
8+
{
9+
10+
private const string LOCAL_FILE_SYSTEM_NAME = "LocalFileSystem";
11+
12+
/// <summary>
13+
/// Checks if the file name is a settings file (.lxp). If so, the contained logfile name
14+
/// is returned. If not, the given file name is returned unchanged.
15+
/// </summary>
16+
/// <param name="fileName">The file name to resolve</param>
17+
/// <param name="pluginRegistry">Plugin registry for file system resolution (optional)</param>
18+
/// <returns>The resolved log file path</returns>
19+
public static string FindFilenameForSettings (string fileName, IPluginRegistry pluginRegistry)
20+
{
21+
ArgumentException.ThrowIfNullOrWhiteSpace(fileName, nameof(fileName));
22+
23+
if (fileName.EndsWith(".lxp", StringComparison.OrdinalIgnoreCase))
24+
{
25+
var persistenceData = Persister.Load(fileName);
26+
if (persistenceData == null)
27+
{
28+
return fileName;
29+
}
30+
31+
if (!string.IsNullOrEmpty(persistenceData.FileName))
32+
{
33+
if (pluginRegistry != null)
34+
{
35+
var fs = pluginRegistry.FindFileSystemForUri(persistenceData.FileName);
36+
// Use file system plugin for non-local files (network, SFTP, etc.)
37+
if (fs != null && fs.GetType().Name != LOCAL_FILE_SYSTEM_NAME)
38+
{
39+
return persistenceData.FileName;
40+
}
41+
}
42+
43+
// Handle rooted paths (absolute paths)
44+
if (Path.IsPathRooted(persistenceData.FileName))
45+
{
46+
return persistenceData.FileName;
47+
}
48+
49+
// Handle relative paths in .lxp files
50+
var dir = Path.GetDirectoryName(fileName);
51+
return Path.Join(dir, persistenceData.FileName);
52+
}
53+
}
54+
55+
return fileName;
56+
}
57+
58+
public static ReadOnlyCollection<string> FindFilenameForSettings (ReadOnlyCollection<string> fileNames, IPluginRegistry pluginRegistry)
59+
{
60+
ArgumentNullException.ThrowIfNull(fileNames);
61+
62+
var foundFiles = new List<string>(fileNames.Count);
63+
64+
foreach (var fileName in fileNames)
65+
{
66+
foundFiles.Add(FindFilenameForSettings(fileName, pluginRegistry));
67+
}
68+
69+
return foundFiles.AsReadOnly();
70+
}
71+
}

src/LogExpert.Core/Classes/Persister/PersisterXML.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,8 @@ public static PersistenceData Load (string fileName)
601601
}
602602
catch (Exception xmlParsingException) when (xmlParsingException is XmlException or
603603
UnauthorizedAccessException or
604-
IOException)
604+
IOException or
605+
FileNotFoundException)
605606
{
606607
_logger.Error(xmlParsingException, $"Error loading persistence data from {fileName}, unknown format, parsing xml or json was not possible");
607608
return null;

src/LogExpert.Core/Classes/Persister/ProjectData.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,10 @@ public class ProjectData
1515
/// </summary>
1616
public string TabLayoutXml { get; set; }
1717

18+
/// <summary>
19+
/// Gets or sets the full file path to the project file.
20+
/// </summary>
21+
public string ProjectFilePath { get; set; }
22+
1823
#endregion
1924
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System.Collections.ObjectModel;
2+
3+
using LogExpert.Core.Interface;
4+
5+
namespace LogExpert.Core.Classes.Persister;
6+
7+
/// <summary>
8+
/// Helper class to resolve project file references to actual log files.
9+
/// Handles .lxp (persistence) files by extracting the actual log file path.
10+
/// </summary>
11+
public static class ProjectFileResolver
12+
{
13+
/// <summary>
14+
/// Resolves project file names to actual log files.
15+
/// If a file is a .lxp persistence file, extracts the log file path from it.
16+
/// </summary>
17+
/// <param name="projectData">The project data containing file references</param>
18+
/// <param name="pluginRegistry">Plugin registry for file system resolution (optional)</param>
19+
/// <returns>List of tuples containing (logFilePath, originalFilePath)</returns>
20+
public static ReadOnlyCollection<(string LogFile, string OriginalFile)> ResolveProjectFiles (ProjectData projectData, IPluginRegistry pluginRegistry = null)
21+
{
22+
ArgumentNullException.ThrowIfNull(projectData);
23+
24+
var resolved = new List<(string LogFile, string OriginalFile)>();
25+
26+
foreach (var fileName in projectData.FileNames)
27+
{
28+
var logFile = PersisterHelpers.FindFilenameForSettings(fileName, pluginRegistry);
29+
resolved.Add((logFile, fileName));
30+
}
31+
32+
return resolved.AsReadOnly();
33+
}
34+
}

0 commit comments

Comments
 (0)