diff --git a/HaWeb/Controllers/XMLStateController.cs b/HaWeb/Controllers/XMLStateController.cs index 0545695..4effd7b 100644 --- a/HaWeb/Controllers/XMLStateController.cs +++ b/HaWeb/Controllers/XMLStateController.cs @@ -13,12 +13,10 @@ public class XMLStateController : Controller { private IHaDocumentWrappper _lib; private readonly IXMLInteractionService _xmlService; private readonly IXMLFileProvider _xmlProvider; - private readonly IMonitorLoop _loop; - public XMLStateController(IMonitorLoop loop, IHaDocumentWrappper lib, IXMLInteractionService xmlService, IXMLFileProvider xmlProvider) { + public XMLStateController(IHaDocumentWrappper lib, IXMLInteractionService xmlService, IXMLFileProvider xmlProvider) { _lib = lib; _xmlService = xmlService; _xmlProvider = xmlProvider; - _loop = loop; } [HttpGet] @@ -26,7 +24,6 @@ public class XMLStateController : Controller { [FeatureGate(Features.AdminService)] [GenerateAntiforgeryTokenCookie] public IActionResult Index() { - _loop.StartMonitorLoop(); var library = _lib.GetLibrary(); var roots = _xmlService.GetRootsList(); if (roots == null) return error404(); @@ -39,6 +36,7 @@ public class XMLStateController : Controller { var model = new XMLStateViewModel("Dateiübersicht", gD, roots, hF, mF, vS) { ActiveFile = activeF, + SyntaxCheck = _xmlService.Test() }; return View("~/Views/Admin/Dynamic/XMLState.cshtml", model); } diff --git a/HaWeb/FileHelpers/ConfigurationMonitor.cs b/HaWeb/FileHelpers/ConfigurationMonitor.cs index 530056a..a5ed77d 100644 --- a/HaWeb/FileHelpers/ConfigurationMonitor.cs +++ b/HaWeb/FileHelpers/ConfigurationMonitor.cs @@ -4,10 +4,12 @@ namespace HaWeb.FileHelpers; public class ConfigurationMonitor { private System.Timers.Timer? _timer; + private string[] _paths; private (string, byte[])[]? _h; private IServiceProvider _serviceProvider; public ConfigurationMonitor(string[] paths, IServiceProvider services) { + _paths = paths; _h = _getHash(paths); _serviceProvider = services; } @@ -33,27 +35,25 @@ public class ConfigurationMonitor { return true; } - public void InvokeChanged(string[] paths) { - var h = _getHash(paths); + public void InvokeChanged(IHostEnvironment _) { + var h = _getHash(_paths); if (_timer == null && !isEqual(h, _h)) { _h = h; - _timer = new(5000) { AutoReset = false }; + _timer = new(8000) { AutoReset = false }; _timer.Enabled = true; - _timer.Elapsed += Action; + _timer.Elapsed += OnChanged; } } - private void Action(Object source, System.Timers.ElapsedEventArgs e) { + private void OnChanged(Object source, System.Timers.ElapsedEventArgs e) { Console.WriteLine("Configuration changed (ConfigurationMonitor Class)"); using IServiceScope serviceScope = _serviceProvider.CreateScope(); IServiceProvider provider = serviceScope.ServiceProvider; - var cP = provider.GetRequiredService(); var hP = provider.GetRequiredService(); hP.ParseConfiguration(cP); var fP = provider.GetRequiredService(); - fP.Reload(cP); - + fP.ParseConfiguration(cP); // _lifetime.StopApplication(); _timer = null; } diff --git a/HaWeb/FileHelpers/IXMLFileProvider.cs b/HaWeb/FileHelpers/IXMLFileProvider.cs index e308caf..dae23ec 100644 --- a/HaWeb/FileHelpers/IXMLFileProvider.cs +++ b/HaWeb/FileHelpers/IXMLFileProvider.cs @@ -10,7 +10,7 @@ public interface IXMLFileProvider { public IFileInfo? SaveHamannFile(XElement element, string basefilepath, ModelStateDictionary ModelState); public List? GetHamannFiles(); public (DateTime PullTime, string Hash)? GetGitData(); - public void Reload(IConfiguration config); + public void ParseConfiguration(IConfiguration config); public bool HasChanged(); public void DeleteHamannFile(string filename); public void Scan(); diff --git a/HaWeb/FileHelpers/XMLFileProvider.cs b/HaWeb/FileHelpers/XMLFileProvider.cs index 6bf89ca..b67e602 100644 --- a/HaWeb/FileHelpers/XMLFileProvider.cs +++ b/HaWeb/FileHelpers/XMLFileProvider.cs @@ -49,19 +49,19 @@ public class XMLFileProvider : IXMLFileProvider { } _HamannFiles = _ScanHamannFiles(); - // Check if hamann file already is current working tree status + // Check if hamann file already is current working tree status // -> YES: Load up the file via _lib.SetLibrary(); if (_IsAlreadyParsed()) { - _Lib.SetLibrary(_HamannFiles.First(), null, null); + _Lib.SetLibrary(_HamannFiles!.First(), null, null); if (_Lib.GetLibrary() != null) return; } // -> NO: Try to create a new file - var created = xmlservice.TryCreate(); + var created = _XMLService.TryCreate(); if (created != null) { var file = SaveHamannFile(created, _hamannFileProvider.GetFileInfo("./").PhysicalPath, null); if (file != null) { - _lib.SetLibrary(file, created.Document, null); + _Lib.SetLibrary(file, created.Document, null); if (_Lib.GetLibrary() != null) return; } } @@ -75,36 +75,32 @@ public class XMLFileProvider : IXMLFileProvider { // -> There is none? Use Fallback: else { var options = new HaWeb.Settings.HaDocumentOptions(); - if (_lib.SetLibrary(null, null, null) == null) { + if (_Lib.SetLibrary(null, null, null) == null) { throw new Exception("Die Fallback Hamann.xml unter " + options.HamannXMLFilePath + " kann nicht geparst werden."); } } } - public void Reload(IConfiguration config) { + public void ParseConfiguration(IConfiguration config) { _Branch = config.GetValue("RepositoryBranch"); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - _hamannFileProvider = new PhysicalFileProvider(config.GetValue("HamannFileStoreWindows")); - _bareRepositoryFileProvider = new PhysicalFileProvider(config.GetValue("BareRepositoryPathWindows")); - _workingTreeFileProvider = new PhysicalFileProvider(config.GetValue("WorkingTreePathWindows")); - } - else { - _hamannFileProvider = new PhysicalFileProvider(config.GetValue("HamannFileStoreLinux")); - _bareRepositoryFileProvider = new PhysicalFileProvider(config.GetValue("BareRepositoryPathLinux")); - _workingTreeFileProvider = new PhysicalFileProvider(config.GetValue("WorkingTreePathLinux")); - } - // Create File Lists; Here and in xmlservice, which does preliminary checking Scan(); + // Reset XMLInteractionService if (_WorkingTreeFiles != null && _WorkingTreeFiles.Any()) { _XMLService.Collect(_WorkingTreeFiles); } _HamannFiles = _ScanHamannFiles(); + if (_HamannFiles != null && _HamannFiles.Select(x => x.Name).Contains(_Lib.GetActiveFile().Name)) { + _Lib.SetLibrary(_Lib.GetActiveFile(), null, null); + if (_Lib.GetLibrary() != null) return; + } + + // Failed to reload File, reload it all, same procedure as above: // Check if hamann file already is current working tree status // -> YES: Load up the file via _lib.SetLibrary(); if (_IsAlreadyParsed()) { - _Lib.SetLibrary(_HamannFiles.First(), null, null); + _Lib.SetLibrary(_HamannFiles!.First(), null, null); if (_Lib.GetLibrary() != null) return; } diff --git a/HaWeb/Models/FileList.cs b/HaWeb/Models/FileList.cs index 8f33135..ce9aaa8 100644 --- a/HaWeb/Models/FileList.cs +++ b/HaWeb/Models/FileList.cs @@ -27,13 +27,4 @@ public class FileList { public List? GetFileList() => this._Files != null ? this._Files.ToList() : null; - - public FileList Clone() { - var ret = new FileList(this.XMLRoot); - if (_Files != null) - foreach (var file in _Files) { - ret.Add(file); - } - return ret; - } } \ No newline at end of file diff --git a/HaWeb/Models/StartEndYear.cs b/HaWeb/Models/StartEndYear.cs deleted file mode 100644 index b938cbd..0000000 --- a/HaWeb/Models/StartEndYear.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace HaWeb.Models; -using System.ComponentModel.DataAnnotations; -public class YearSetting { - [Required] - public int EndYear { get; set; } -} \ No newline at end of file diff --git a/HaWeb/Models/SyntaxCheckModel.cs b/HaWeb/Models/SyntaxCheckModel.cs index 5b5f5ee..0ffc053 100644 --- a/HaWeb/Models/SyntaxCheckModel.cs +++ b/HaWeb/Models/SyntaxCheckModel.cs @@ -1,22 +1,22 @@ namespace HaWeb.Models; public class SyntaxCheckModel { - public string Prefix { get; private set; } - public List? Errors { get; set; } - public List? Warnings { get; set; } + public string File { get; private set; } + public List? Errors { get; private set; } - public SyntaxCheckModel(string prefix) { - Prefix = prefix; + public SyntaxCheckModel(string file) { + File = file; + } + + public void Log(int? line, int? column, string msg) { + if (String.IsNullOrWhiteSpace(msg)) return; + if (Errors == null) Errors = new(); + // var prefix = DateTime.Now.ToLongDateString() + ": "; + Errors.Add(new SyntaxError(line, column, msg)); + } + + public void ResetLog() { + Errors = null; } } -public class SyntaxError { - public string Message { get; private set; } - public string? File { get; set; } - public string? Line { get; set; } - public string? Column { get; set; } - - public SyntaxError(string message) { - Message = message; - } -} \ No newline at end of file diff --git a/HaWeb/Models/SyntaxErrorModel.cs b/HaWeb/Models/SyntaxErrorModel.cs new file mode 100644 index 0000000..57c1d4a --- /dev/null +++ b/HaWeb/Models/SyntaxErrorModel.cs @@ -0,0 +1,13 @@ +namespace HaWeb.Models; + +public class SyntaxError { + public string Message { get; private set; } + public int? Line { get; set; } + public int? Column { get; set; } + + public SyntaxError(int? line, int? column, string message) { + Line = line; + Column = column; + Message = message; + } +} \ No newline at end of file diff --git a/HaWeb/Models/XMLStateViewModel.cs b/HaWeb/Models/XMLStateViewModel.cs index 50759b2..c653b6e 100644 --- a/HaWeb/Models/XMLStateViewModel.cs +++ b/HaWeb/Models/XMLStateViewModel.cs @@ -19,6 +19,9 @@ public class XMLStateViewModel { // Verfügbare (Gesamt-)Dateien public List? HamannFiles { get; set; } + // Syntax-Check-Resultate + public Dictionary? SyntaxCheck { get; set; } + public XMLStateViewModel( string title, (DateTime PullTime, string Hash)? gitData, diff --git a/HaWeb/Program.cs b/HaWeb/Program.cs index 077cb97..2745755 100644 --- a/HaWeb/Program.cs +++ b/HaWeb/Program.cs @@ -26,8 +26,6 @@ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { builder.Configuration.AddJsonFile(p, optional: true, reloadOnChange: true); } - - // Create initial Data var tS = new XMLTestService(); var XMLIS = new XMLInteractionService(builder.Configuration, tS); @@ -59,7 +57,7 @@ var cM = new ConfigurationMonitor(configpaths.ToArray(), app.Services); ChangeToken.OnChange( () => app.Configuration.GetReloadToken(), (state) => cM.InvokeChanged(state), - configpaths.ToArray() + app.Environment ); // Configure the HTTP request pipeline. diff --git a/HaWeb/Views/Admin/Dynamic/XMLState.cshtml b/HaWeb/Views/Admin/Dynamic/XMLState.cshtml index 95036e5..f827f4d 100644 --- a/HaWeb/Views/Admin/Dynamic/XMLState.cshtml +++ b/HaWeb/Views/Admin/Dynamic/XMLState.cshtml @@ -25,8 +25,18 @@ @f.GetLastModified() @if (f.IsValid) { Valid! @f.GetLog() + + @if (Model.SyntaxCheck[f.FileName] != null && Model.SyntaxCheck[f.FileName].Errors != null) { +
    + @foreach(var e in Model.SyntaxCheck[f.FileName]?.Errors) { +
  • @e.Line @e.Column @e.Message
  • + } +
+ } + } else { @f.GetLog() + } } diff --git a/HaWeb/XMLParser/IXMLInteractionService.cs b/HaWeb/XMLParser/IXMLInteractionService.cs index 782904e..02a0c27 100644 --- a/HaWeb/XMLParser/IXMLInteractionService.cs +++ b/HaWeb/XMLParser/IXMLInteractionService.cs @@ -16,6 +16,7 @@ public interface IXMLInteractionService { public List? GetRootsList(); public void CreateSearchables(XDocument document); public List? GetManagedFiles(); + public Dictionary? Test(); public List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? SearchCollection(string collection, string searchword, IReaderService reader, ILibrary? lib); public List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? GetPreviews(List<(string, List)> places, IReaderService reader, ILibrary lib); } \ No newline at end of file diff --git a/HaWeb/XMLParser/XMLInteractionService.cs b/HaWeb/XMLParser/XMLInteractionService.cs index 9c396ed..6b0c893 100644 --- a/HaWeb/XMLParser/XMLInteractionService.cs +++ b/HaWeb/XMLParser/XMLInteractionService.cs @@ -86,11 +86,12 @@ public class XMLInteractionService : IXMLInteractionService { public void Collect(List files) { if (files == null || !files.Any()) return; _ValidState = true; - List res = new List(); + Dictionary? lF = new Dictionary(); + List fM = new List(); foreach (var f in files) { var sb = new StringBuilder(); var m = _CreateFileModel(f, null); - res.Add(m); + fM.Add(m); // 1. Open File for Reading try { using (Stream file = f.CreateReadStream()) { @@ -118,9 +119,8 @@ public class XMLInteractionService : IXMLInteractionService { // Success! File can be recognized and parsed. m.Validate(); foreach (var d in docs) { - if (_Loaded == null) _Loaded = new Dictionary(); - if (!_Loaded.ContainsKey(d.Prefix)) _Loaded.Add(d.Prefix, new FileList(d.XMLRoot)); - _Loaded[d.Prefix]!.Add(d); + if (!lF.ContainsKey(d.Prefix)) lF.Add(d.Prefix, new FileList(d.XMLRoot)); + lF[d.Prefix]!.Add(d); } } } catch (Exception ex) { @@ -128,20 +128,26 @@ public class XMLInteractionService : IXMLInteractionService { continue; } } - if(res.Any()) this._ManagedFiles = res; - // Set validity + // Set data + this._ManagedFiles = fM; + this._Loaded = lF; foreach (var f in _ManagedFiles) { - if (!f.IsValid) _ValidState = false; + if (!f.IsValid) this._ValidState = false; break; } + } - // TODO: Speed up this: + public Dictionary? Test() { + if (_Loaded == null) return null; + // TODO: Speed up this, move it into a background task: var sw = new Stopwatch(); sw.Start(); - _testService.Test(this); + var res = this._Loaded?.SelectMany(x => x.Value?.GetFileList()?.Select(x => x.File)).Distinct().Select(x => x.FileName); + var ret = _testService.Test(this._Loaded, res.ToDictionary(x => x, y => new SyntaxCheckModel(y))); sw.Stop(); Console.WriteLine("Syntaxcheck " + sw.ElapsedMilliseconds.ToString() + " ms"); + return ret; } public XElement? TryCreate() { diff --git a/HaWeb/XMLTests/IXMLTestService.cs b/HaWeb/XMLTests/IXMLTestService.cs index 33d90d1..6bf6d4c 100644 --- a/HaWeb/XMLTests/IXMLTestService.cs +++ b/HaWeb/XMLTests/IXMLTestService.cs @@ -1,3 +1,4 @@ +using HaWeb.Models; using HaWeb.XMLParser; namespace HaWeb.XMLTests; @@ -7,5 +8,5 @@ public interface IXMLTestService { public Dictionary? Ruleset { get; } public Dictionary? CollectionRuleset { get; } - public void Test(IXMLInteractionService _XMLService); + public Dictionary? Test(Dictionary? _LoadedFiles, Dictionary _Results); } \ No newline at end of file diff --git a/HaWeb/XMLTests/XMLTestService.cs b/HaWeb/XMLTests/XMLTestService.cs index d3c28e3..42f456c 100644 --- a/HaWeb/XMLTests/XMLTestService.cs +++ b/HaWeb/XMLTests/XMLTestService.cs @@ -1,4 +1,6 @@ namespace HaWeb.XMLTests; + +using HaWeb.Models; using HaWeb.XMLParser; public class XMLTestService : IXMLTestService { @@ -7,28 +9,26 @@ public class XMLTestService : IXMLTestService { public XMLTestService() { var roottypes = _GetAllTypesThatImplementInterface().ToList(); roottypes.ForEach( x => { - if (this.Ruleset == null) this.Ruleset = new Dictionary(); + if (this.Ruleset == null) this.Ruleset = new(); var instance = (INodeRule)Activator.CreateInstance(x)!; if (instance != null) this.Ruleset.Add(instance.Name, instance); }); var collectionruleset = _GetAllTypesThatImplementInterface().ToList(); collectionruleset.ForEach( x => { - if (this.CollectionRuleset == null) this.CollectionRuleset = new Dictionary(); + if (this.CollectionRuleset == null) this.CollectionRuleset = new(); var instance = (ICollectionRule)Activator.CreateInstance(x)!; if (instance != null) this.CollectionRuleset.Add(instance.Name, instance); }); } - public void Test(IXMLInteractionService _XMLService) { - var docs = _XMLService.GetLoaded(); - if (docs == null) return; - var tester = new XMLTester(this, docs); - tester.Test(); + public Dictionary? Test(Dictionary? _Loaded, Dictionary _Results) { + if (_Loaded == null) return null; + var tester = new XMLTester(this, _Loaded, _Results); + return tester.Test(); } - private IEnumerable _GetAllTypesThatImplementInterface() - { + private IEnumerable _GetAllTypesThatImplementInterface() { return System.Reflection.Assembly.GetExecutingAssembly() .GetTypes() .Where(type => typeof(T).IsAssignableFrom(type) && !type.IsInterface); diff --git a/HaWeb/XMLTests/XMLTester.cs b/HaWeb/XMLTests/XMLTester.cs index 49f85c2..6d87973 100644 --- a/HaWeb/XMLTests/XMLTester.cs +++ b/HaWeb/XMLTests/XMLTester.cs @@ -9,12 +9,14 @@ public class XMLTester { private Dictionary? _Ruleset; private Dictionary? _CollectionRuleset; private List? _Documents; + private Dictionary _Results; private Dictionary>? _IDs; private Dictionary>? _CollectionIDs; private Dictionary?> _XPathEvaluated; - public XMLTester (IXMLTestService testService, Dictionary? filelists) { + public XMLTester (IXMLTestService testService, Dictionary? filelists, Dictionary results) { _Ruleset = testService.Ruleset; _CollectionRuleset = testService.CollectionRuleset; + _Results = results; if (filelists != null) { foreach (var fl in filelists) { if (fl.Value != null) { @@ -27,20 +29,21 @@ public class XMLTester { _XPathEvaluated = new Dictionary?>(); } - public void Test() { - if (_Ruleset == null) return; + public Dictionary? Test() { + if (_Ruleset == null) return null; _IDs = new Dictionary>(); foreach (var rule in _Ruleset) { buildIDs(rule.Value); checkRequiredAttributes(rule.Value); checkReferences(rule.Value); } - if (_CollectionRuleset == null) return; + if (_CollectionRuleset == null) return null; _CollectionIDs = new Dictionary>(); foreach (var collectionrule in _CollectionRuleset) { buildIDs(collectionrule.Value); checkReferences(collectionrule.Value); } + return _Results; } private void checkReferences(INodeRule rule) { @@ -54,7 +57,8 @@ public class XMLTester { if (_IDs != null && _IDs.ContainsKey(keyname) && hasattr) { var val = e.Item1.Attribute(r.LinkAttribute)!.Value; if (!_IDs[keyname].Contains(val)) { - e.Item2.File.Log(generateLogMessage(e.Item1) + "Verlinktes Element " + val + " nicht gefunden."); + var lc = getLineColumn(e.Item1); + _Results[e.Item2.File.FileName].Log(lc.Item1, lc.Item2, "Verlinktes Element " + val + " nicht gefunden."); } } } @@ -69,7 +73,8 @@ public class XMLTester { if (elemens != null && elemens.Any()) { foreach(var r in rule.GenerateBacklinkString(elemens)) { if (!r.Item4 && !_CollectionIDs[rule.Name].Contains(r.Item1)) { - r.Item3.File.Log(generateLogMessage(r.Item2) + "Verlinktes Element " + r.Item1 + " nicht gefunden."); + var lc = getLineColumn(r.Item2); + _Results[r.Item3.File.FileName].Log(lc.Item1, lc.Item2, "Verlinktes Element " + r.Item1 + " nicht gefunden."); } if (r.Item4) { var coll = _CollectionIDs[rule.Name]; @@ -77,15 +82,18 @@ public class XMLTester { var searchterm = items[0]; var found = coll.Where(x => x.StartsWith(searchterm)); if (items[0] == "NA" || found == null || !found.Any()) { - r.Item3.File.Log(generateLogMessage(r.Item2) + "Verlinktes Element " + r.Item1 + " nicht gefunden."); + var lc = getLineColumn(r.Item2); + _Results[r.Item3.File.FileName].Log(lc.Item1, lc.Item2, "Verlinktes Element " + r.Item1 + " nicht gefunden."); } else { for (var i = 1; i < items.Length; i++) { if (items[i] == "NA") break; else { searchterm = searchterm + "-" + items[i]; found = found.Where(x => x.StartsWith(searchterm)); - if (found == null || !found.Any()) - r.Item3.File.Log(generateLogMessage(r.Item2) + "Verlinktes Element " + r.Item1 + " nicht gefunden."); + if (found == null || !found.Any()) { + var lc = getLineColumn(r.Item2); + _Results[r.Item3.File.FileName].Log(lc.Item1, lc.Item2, "Verlinktes Element " + r.Item1 + " nicht gefunden."); + } } } } @@ -126,7 +134,8 @@ public class XMLTester { if (elemens != null && elemens.Any()) { foreach (var r in rule.GenerateIdentificationStrings(elemens)) { if (!hs.Add(r.Item1)) { - r.Item3.File.Log(generateLogMessage(r.Item2) + "Brief-Seite-Zeile " + r.Item1 + " mehrdeutig."); + var lc = getLineColumn(r.Item2); + _Results[r.Item3.File.FileName].Log(lc.Item1, lc.Item2, "Brief-Seite-Zeile " + r.Item1 + " mehrdeutig."); } } } @@ -143,7 +152,8 @@ public class XMLTester { foreach (var e in elements) { if (checkAttribute(e.Item1, attribute, e.Item2)) { if (!hs.Add(e.Item1.Attribute(attribute)!.Value)) { - e.Item2.File.Log(generateLogMessage(e.Item1) + "Attributwert " + e.Item1.Attribute(attribute)!.Value + " doppelt."); + var lc = getLineColumn(e.Item1); + _Results[e.Item2.File.FileName].Log(lc.Item1, lc.Item2, "Attributwert " + e.Item1.Attribute(attribute)!.Value + " doppelt."); } } } @@ -152,18 +162,17 @@ public class XMLTester { private bool checkAttribute(XElement element, string attributename, XMLRootDocument doc, bool log = true) { if (!element.HasAttributes || element.Attribute(attributename) == null) { - if (log) doc.File.Log(generateLogMessage(element) + "Attribut " + attributename + " fehlt."); + if (log) { + var lc = getLineColumn(element); + _Results[doc.File.FileName].Log(lc.Item1, lc.Item2,"Attribut " + attributename + " fehlt."); + }; return false; } return true; } - private string generateLogMessage(XElement element) { - return "Zeile " + - ((IXmlLineInfo)element).LineNumber.ToString() + - ", Element " + - element.Name + - ": "; + private (int, int) getLineColumn(XElement element) { + return (((IXmlLineInfo)element).LineNumber, ((IXmlLineInfo)element).LinePosition); } // Cache for XPATH evaluation