Skip to content

Commit

Permalink
Configuration saving
Browse files Browse the repository at this point in the history
  • Loading branch information
Nice3point committed Sep 21, 2024
1 parent a9ed083 commit caf8bde
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 85 deletions.
107 changes: 89 additions & 18 deletions source/RevitLookup/Core/Modules/Configuration/RevitConfigurator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// (Rights in Technical Data and Computer Software), as applicable.

using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text;
using RevitLookup.ViewModels.ObservableObjects;
Expand All @@ -28,11 +29,22 @@ namespace RevitLookup.Core.Modules.Configuration;
[SuppressMessage("ReSharper", "ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator")]
public sealed class RevitConfigurator
{
private const string BackupSuffix = "_RevitLookupBackup_";
private const int DefaultBufferSize = 4096; // From System.IO.File source code

private const char CommentChar = ';';
private const string SessionOptionsCategory = "[Jrn.SessionOptions]";
private const string RevitAttributeRecord = " Rvt.Attr.";

private readonly Encoding _encoding;
private readonly SemaphoreSlim _asyncLock = new(1, 1);
private readonly string _userIniPath = Context.Application.CurrentUsersDataFolderPath.AppendPath("Revit.ini");

private readonly string _defaultIniPath = Environment
.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)
.AppendPath("Autodesk", $"RVT {Context.Application.VersionNumber}", "UserDataCache", "Revit.ini");

private bool _backupDone;

public RevitConfigurator()
{
Expand All @@ -42,30 +54,28 @@ public RevitConfigurator()
_encoding = Encoding.GetEncoding(1251);
}

public List<ObservableRevitSettingsEntry> ParseSources()
public async Task<List<ObservableRevitSettingsEntry>> ReadAsync()
{
var userIniPath = Context.Application.CurrentUsersDataFolderPath.AppendPath("Revit.ini");
var defaultIniPath = Environment
.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)
.AppendPath("Autodesk", $"RVT {Context.Application.VersionNumber}", "UserDataCache", "Revit.ini");

var journalConfigurations = ParseJournalSource();
var userConfigurations = ParseIniFile(userIniPath, false);
var defaultConfigurations = ParseIniFile(defaultIniPath, true);
return await Task.Run(() =>
{
var journalConfigurations = ParseJournalSource();
var userConfigurations = ParseIniFile(false);
var defaultConfigurations = ParseIniFile(true);

return MergeSources(journalConfigurations, userConfigurations, defaultConfigurations);
return MergeSources(journalConfigurations, userConfigurations, defaultConfigurations);
});
}

private List<ObservableRevitSettingsEntry> ParseJournalSource()
{
var currentJournal = Context.Application.RecordingJournalFilename;
var journalsPath = Directory.GetParent(currentJournal)!;
var journals = Directory.EnumerateFiles(journalsPath.FullName, "journal*txt").Reverse();

foreach (var journal in journals)
{
if (journal == currentJournal) continue;

var lines = File.ReadLines(journal, _encoding);
foreach (var sessionOptions in lines.Reverse())
{
Expand Down Expand Up @@ -104,12 +114,13 @@ private List<ObservableRevitSettingsEntry> ParseJournalSource()
return [];
}

private List<ObservableRevitSettingsEntry> ParseIniFile(string path, bool isDefault)
private List<ObservableRevitSettingsEntry> ParseIniFile(bool useDefault)
{
var path = useDefault ? _defaultIniPath : _userIniPath;
if (!File.Exists(path)) return [];

var entries = new List<ObservableRevitSettingsEntry>();
var lines = File.ReadLines(path, _encoding);
var lines = File.ReadLines(path, Encoding.Unicode);
var currentCategory = string.Empty;

foreach (var line in lines)
Expand Down Expand Up @@ -142,13 +153,14 @@ private List<ObservableRevitSettingsEntry> ParseIniFile(string path, bool isDefa
Value = value
};

if (isDefault)
if (useDefault)
{
entry.DefaultValue = value;
}
else
{
entry.IsActive = isActive;
entry.UserDefined = true;
}

entries.Add(entry);
Expand All @@ -158,16 +170,18 @@ private List<ObservableRevitSettingsEntry> ParseIniFile(string path, bool isDefa
}

private List<ObservableRevitSettingsEntry> MergeSources(

Check notice on line 172 in source/RevitLookup/Core/Modules/Configuration/RevitConfigurator.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Member can be made static (shared) (private accessibility)

Method 'MergeSources' can be made static
List<ObservableRevitSettingsEntry> journalEntries,
List<ObservableRevitSettingsEntry> userEntries,
List<ObservableRevitSettingsEntry> journalEntries,
List<ObservableRevitSettingsEntry> userEntries,
List<ObservableRevitSettingsEntry> defaultEntries)
{
foreach (var userEntry in userEntries)
{
var existingEntry = journalEntries.FirstOrDefault(entry => entry.Category == userEntry.Category && entry.Property == userEntry.Property);
if (existingEntry != null)
{
existingEntry.Value = userEntry.Value;
existingEntry.IsActive = userEntry.IsActive;
existingEntry.UserDefined = userEntry.UserDefined;
}
else
{
Expand All @@ -190,4 +204,61 @@ private List<ObservableRevitSettingsEntry> MergeSources(

return journalEntries;
}


public async Task WriteAsync(List<ObservableRevitSettingsEntry> entries)
{
var lines = new List<string>();

var sortedEntries = entries
.Where(entry => entry.UserDefined)
.OrderBy(entry => entry.Category)
.ThenBy(entry => entry.Property)
.GroupBy(entry => entry.Category)
.ToArray();

foreach (var entryGroup in sortedEntries)
{
lines.Add($"[{entryGroup.Key}]");
foreach (var entry in entryGroup)
{
var lineBuilder = new StringBuilder();

if (!entry.IsActive) lineBuilder.Append(CommentChar);
lineBuilder.Append(entry.Property);
lineBuilder.Append("=");
lineBuilder.Append(entry.Value);

lines.Add(lineBuilder.ToString());
}
}

try
{
await _asyncLock.WaitAsync();

var filePath = Path.GetDirectoryName(_userIniPath)!;
var fileName = Path.GetFileNameWithoutExtension(_userIniPath);
if (!_backupDone && File.Exists(_userIniPath))
{
var backupFileName = $"{fileName}{BackupSuffix}{DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture)}.ini";
File.Copy(_userIniPath!, Path.Combine(filePath, backupFileName));
_backupDone = true;
}

using var stream = new FileStream(_userIniPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, DefaultBufferSize, FileOptions.Asynchronous);
using var writer = new StreamWriter(stream, Encoding.Unicode);
foreach (var line in lines)
{
await writer.WriteLineAsync(line);
}

stream.SetLength(stream.Position);
await writer.FlushAsync();
}
finally
{
_asyncLock.Release();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,15 @@ namespace RevitLookup.ViewModels.ObservableObjects;
#nullable enable
public sealed partial class ObservableRevitSettingsEntry : ObservableValidator
{
[ObservableProperty] private bool _isActive;
[ObservableProperty] [Required] [NotifyDataErrorInfo] private string _category = string.Empty;
[ObservableProperty] [Required] [NotifyDataErrorInfo] private string _property = string.Empty;
[ObservableProperty] private string _value = string.Empty;
[ObservableProperty] private string? _defaultValue;
[ObservableProperty] private bool _isActive;
[ObservableProperty] private bool _isModified;

[RelayCommand]
private void RestoreDefault()
{
Value = DefaultValue ?? string.Empty;
}

public bool UserDefined { get; set; }

public ObservableRevitSettingsEntry Clone()
{
return new ObservableRevitSettingsEntry
Expand All @@ -48,6 +44,16 @@ public ObservableRevitSettingsEntry Clone()
};
}

public void Validate()
{
ValidateAllProperties();
}

partial void OnIsActiveChanged(bool value)

Check warning on line 52 in source/RevitLookup/ViewModels/ObservableObjects/ObservableRevitSettingsEntry.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Unused parameter in partial method

Parameter 'value' is never used
{
UserDefined = true;
}

partial void OnValueChanged(string value)
{
IsModified = DefaultValue is not null && value != DefaultValue;
Expand Down
Loading

0 comments on commit caf8bde

Please sign in to comment.