diff --git a/AGENTS.md b/AGENTS.md index dd435e7..154bb33 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,8 +47,5 @@ dotnet test Schematron.slnx ## Conventions -- **Target framework**: `net48` for all projects (WebServices requires .NET Framework due to `System.Web.Services`). -- **Versioning**: Managed by the [GitInfo](https://github.com/devlooped/GitInfo) package; base version comes from the `VERSION` file at the repo root. -- **NuGet metadata**: Defined in `src/Directory.Build.props` and per-project ``. - **C# language version**: `latest` — use modern idioms (expression-bodied members, `var`, `string.IsNullOrEmpty`, `throw;`, pattern matching). - **No custom build scripts**: Use standard `dotnet` CLI; no `build.cmd` / `build.proj`. diff --git a/src/Schematron.Tests/ReferenceSchematronRunner.cs b/src/Schematron.Tests/ReferenceSchematronRunner.cs index 9deb40a..28b4e7d 100644 --- a/src/Schematron.Tests/ReferenceSchematronRunner.cs +++ b/src/Schematron.Tests/ReferenceSchematronRunner.cs @@ -16,9 +16,9 @@ static class ReferenceSchematronRunner const string XsltDir = "./Content/xslt"; const string SvrlNs = "http://purl.oclc.org/dsdl/svrl"; - static readonly XslCompiledTransform _include = LoadXslt(Path.Combine(XsltDir, "iso_dsdl_include.xsl")); - static readonly XslCompiledTransform _abstract = LoadXslt(Path.Combine(XsltDir, "iso_abstract_expand.xsl")); - static readonly XslCompiledTransform _svrl = LoadXslt(Path.Combine(XsltDir, "iso_svrl_for_xslt1.xsl")); + static readonly XslCompiledTransform include = LoadXslt(Path.Combine(XsltDir, "iso_dsdl_include.xsl")); + static readonly XslCompiledTransform abstractExpand = LoadXslt(Path.Combine(XsltDir, "iso_abstract_expand.xsl")); + static readonly XslCompiledTransform svrl = LoadXslt(Path.Combine(XsltDir, "iso_svrl_for_xslt1.xsl")); static XslCompiledTransform LoadXslt(string path) { @@ -36,14 +36,14 @@ static XslCompiledTransform LoadXslt(string path) public static SvrlResult Validate(string schemaPath, string xmlContent, string? phase = null) { // Step 1: resolve includes - string included = ApplyTransform(_include, ReadFile(schemaPath), baseUri: Path.GetFullPath(schemaPath)); + string included = ApplyTransform(include, ReadFile(schemaPath), baseUri: Path.GetFullPath(schemaPath)); // Step 2: expand abstract patterns - string expanded = ApplyTransform(_abstract, included); + string expanded = ApplyTransform(abstractExpand, included); // Step 3: generate validator XSLT from schema var svrlArgs = new XsltArgumentList(); if (!string.IsNullOrEmpty(phase)) svrlArgs.AddParam("phase", "", phase); - string validatorXslt = ApplyTransform(_svrl, expanded, args: svrlArgs); + string validatorXslt = ApplyTransform(ReferenceSchematronRunner.svrl, expanded, args: svrlArgs); // Step 4: apply validator XSLT to the XML instance var validator = new XslCompiledTransform(); using (var validatorReader = XmlReader.Create(new StringReader(validatorXslt))) diff --git a/src/Schematron.Tests/Schematron.Tests.csproj b/src/Schematron.Tests/Schematron.Tests.csproj index 794e96a..bdd4310 100644 --- a/src/Schematron.Tests/Schematron.Tests.csproj +++ b/src/Schematron.Tests/Schematron.Tests.csproj @@ -5,24 +5,15 @@ - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - + + + - - all - runtime; build; native; contentfiles; analyzers - + diff --git a/src/Schematron.Tests/ValidatorTests.cs b/src/Schematron.Tests/ValidatorTests.cs index e277a2d..268fbc9 100644 --- a/src/Schematron.Tests/ValidatorTests.cs +++ b/src/Schematron.Tests/ValidatorTests.cs @@ -1,4 +1,3 @@ -using System.IO; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; @@ -78,7 +77,7 @@ public void ValidateShouldReturnSchematronValidationResultWhenSchematronConstrai using (var stream = new MemoryStream(System.Text.Encoding.Unicode.GetBytes(ex.Message))) using (var reader = XmlReader.Create(stream)) { - var obj = (Schematron.Serialization.SchematronValidationResultTempObjectModel.Output)serializer.Deserialize(reader); + var obj = (Schematron.Serialization.SchematronValidationResultTempObjectModel.Output?)serializer.Deserialize(reader); // Assert @@ -282,7 +281,7 @@ public void SchematronValidationResultIncludesExpandedValueElements() using (var doc = XmlReader.Create(XmlContentLocation)) { - var result = (IXPathNavigable)null; + IXPathNavigable? result = default; try { @@ -292,7 +291,7 @@ public void SchematronValidationResultIncludesExpandedValueElements() { System.Diagnostics.Debug.WriteLine(ex.Message); string expectedMessage = "Attributes sex (Female) and title (Mr) must have compatible values on element customer."; - Xunit.Assert.True(ex.Message.Contains(expectedMessage)); + Xunit.Assert.Contains(expectedMessage, ex.Message); } Xunit.Assert.Null(result); } diff --git a/src/Schematron/Assert.cs b/src/Schematron/Assert.cs index e735f49..2b779f4 100644 --- a/src/Schematron/Assert.cs +++ b/src/Schematron/Assert.cs @@ -21,8 +21,6 @@ public class Assert : Test /// Constructs a new Assert object. /// XPath expression to test. /// Message to display if the assert fails. - internal protected Assert(string test, string message) : base(test, message) - { - } + internal protected Assert(string test, string message) : base(test, message) { } } diff --git a/src/Schematron/BadSchemaException.cs b/src/Schematron/BadSchemaException.cs index 2f1949b..81b5be0 100644 --- a/src/Schematron/BadSchemaException.cs +++ b/src/Schematron/BadSchemaException.cs @@ -13,36 +13,26 @@ public class BadSchemaException : ApplicationException /// /// Initializes a new instance of the class. /// - public BadSchemaException() - { - } + public BadSchemaException() { } /// /// Initializes a new instance of the class. /// /// The error message that explains the reason for the exception. - public BadSchemaException(string message) : base(message) - { - } + public BadSchemaException(string message) : base(message) { } /// /// Initializes a new instance of the class. /// /// Info /// Context - protected BadSchemaException(SerializationInfo info, StreamingContext context) : - base(info, context) - { - } + protected BadSchemaException(SerializationInfo info, StreamingContext context) : base(info, context) { } /// /// Initializes a new instance of the class. /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception. - public BadSchemaException(string message, Exception innerException) : - base(message, innerException) - { - } + public BadSchemaException(string message, Exception innerException) : base(message, innerException) { } } diff --git a/src/Schematron/CompiledExpressions.cs b/src/Schematron/CompiledExpressions.cs index 9c2288a..5efa072 100644 --- a/src/Schematron/CompiledExpressions.cs +++ b/src/Schematron/CompiledExpressions.cs @@ -1,4 +1,3 @@ -using System.Xml; using System.Xml.XPath; namespace Schematron; @@ -32,7 +31,7 @@ class CompiledExpressions static CompiledExpressions() { - XmlNamespaceManager mgr = Config.DefaultNsManager; + var mgr = Config.DefaultNsManager; Schema = Config.DefaultNavigator.Compile("//sch:schema"); Schema.SetContext(mgr); @@ -75,8 +74,6 @@ static CompiledExpressions() // Diagnostics.SetContext(mgr); } - CompiledExpressions() - { - } + CompiledExpressions() { } } diff --git a/src/Schematron/Config.cs b/src/Schematron/Config.cs index 7005e03..8802431 100644 --- a/src/Schematron/Config.cs +++ b/src/Schematron/Config.cs @@ -11,87 +11,83 @@ namespace Schematron; /// This class is public to allow inheritors of Schematron elements /// to use these global settings. /// -/// -/// public class Config { + static readonly IFormatter formatter; + static readonly XPathNavigator navigator; + static readonly XmlNamespaceManager nsmanager; + static readonly Schema full; + static readonly Schema embedded; + static readonly string uid = string.Intern(Guid.NewGuid().ToString()); + /// /// Initializes global settings. /// static Config() { // Default formatter outputs in text format a log with results. - _formatter = new LogFormatter(); + formatter = new LogFormatter(); //TODO: create and load the schematron full and embedded versions for validation. - _embedded = new Schema(); - _embedded.Phases.Add(_embedded.CreatePhase(Phase.All)); - _full = new Schema(); - _full.Phases.Add(_full.CreatePhase(Phase.All)); + embedded = new Schema(); + embedded.Phases.Add(embedded.CreatePhase(Phase.All)); + full = new Schema(); + full.Phases.Add(full.CreatePhase(Phase.All)); //TODO: should we move all the schema language elements to a resource file? - _navigator = new XmlDocument().CreateNavigator(); - _navigator.NameTable.Add("active"); - _navigator.NameTable.Add("pattern"); - _navigator.NameTable.Add("assert"); - _navigator.NameTable.Add("test"); - _navigator.NameTable.Add("role"); - _navigator.NameTable.Add("id"); - _navigator.NameTable.Add("diagnostics"); - _navigator.NameTable.Add("icon"); - _navigator.NameTable.Add("subject"); - _navigator.NameTable.Add("diagnostic"); - _navigator.NameTable.Add("dir"); - _navigator.NameTable.Add("emph"); - _navigator.NameTable.Add("extends"); - _navigator.NameTable.Add("rule"); - _navigator.NameTable.Add("key"); - _navigator.NameTable.Add("name"); - _navigator.NameTable.Add("path"); - _navigator.NameTable.Add("ns"); - _navigator.NameTable.Add("uri"); - _navigator.NameTable.Add("prefix"); - _navigator.NameTable.Add("p"); - _navigator.NameTable.Add("class"); - _navigator.NameTable.Add("see"); - _navigator.NameTable.Add("phase"); - _navigator.NameTable.Add("fpi"); - _navigator.NameTable.Add("report"); - _navigator.NameTable.Add("context"); - _navigator.NameTable.Add("abstract"); - _navigator.NameTable.Add("schema"); - _navigator.NameTable.Add("schemaVersion"); - _navigator.NameTable.Add("defaultPhase"); - _navigator.NameTable.Add("version"); - _navigator.NameTable.Add("span"); - _navigator.NameTable.Add("title"); - _navigator.NameTable.Add("value-of"); - _navigator.NameTable.Add("select"); - _navigator.NameTable.Add(Schema.IsoNamespace); - _navigator.NameTable.Add(Schema.LegacyNamespace); + navigator = new XmlDocument().CreateNavigator(); + navigator.NameTable.Add("active"); + navigator.NameTable.Add("pattern"); + navigator.NameTable.Add("assert"); + navigator.NameTable.Add("test"); + navigator.NameTable.Add("role"); + navigator.NameTable.Add("id"); + navigator.NameTable.Add("diagnostics"); + navigator.NameTable.Add("icon"); + navigator.NameTable.Add("subject"); + navigator.NameTable.Add("diagnostic"); + navigator.NameTable.Add("dir"); + navigator.NameTable.Add("emph"); + navigator.NameTable.Add("extends"); + navigator.NameTable.Add("rule"); + navigator.NameTable.Add("key"); + navigator.NameTable.Add("name"); + navigator.NameTable.Add("path"); + navigator.NameTable.Add("ns"); + navigator.NameTable.Add("uri"); + navigator.NameTable.Add("prefix"); + navigator.NameTable.Add("p"); + navigator.NameTable.Add("class"); + navigator.NameTable.Add("see"); + navigator.NameTable.Add("phase"); + navigator.NameTable.Add("fpi"); + navigator.NameTable.Add("report"); + navigator.NameTable.Add("context"); + navigator.NameTable.Add("abstract"); + navigator.NameTable.Add("schema"); + navigator.NameTable.Add("schemaVersion"); + navigator.NameTable.Add("defaultPhase"); + navigator.NameTable.Add("version"); + navigator.NameTable.Add("span"); + navigator.NameTable.Add("title"); + navigator.NameTable.Add("value-of"); + navigator.NameTable.Add("select"); + navigator.NameTable.Add(Schema.IsoNamespace); + navigator.NameTable.Add(Schema.LegacyNamespace); //Namespace manager initialization - _nsmanager = new XmlNamespaceManager(_navigator.NameTable); - _nsmanager.AddNamespace(String.Empty, Schema.Namespace); - _nsmanager.AddNamespace("sch", Schema.Namespace); - _nsmanager.AddNamespace("xsd", System.Xml.Schema.XmlSchema.Namespace); - } - - Config() - { + nsmanager = new XmlNamespaceManager(navigator.NameTable); + nsmanager.AddNamespace(string.Empty, Schema.Namespace); + nsmanager.AddNamespace("sch", Schema.Namespace); + nsmanager.AddNamespace("xsd", System.Xml.Schema.XmlSchema.Namespace); } - static IFormatter _formatter; + Config() { } /// /// The default object to use to format messages from validation. /// - public static IFormatter DefaultFormatter - { - get { return _formatter; } - } - - static XPathNavigator _navigator; + public static IFormatter DefaultFormatter => formatter; /// /// A default empty navigator used to pre-compile XPath expressions. @@ -110,56 +106,35 @@ public static IFormatter DefaultFormatter /// /// /// - internal static XPathNavigator DefaultNavigator - { - // Returning a cloned navigator appeared to solve the threading issues - // we had, because a single navigator was being used to compile all the - // expressions in all potential threads. - get { return _navigator.Clone(); } - } - - static XmlNamespaceManager _nsmanager; + /// + /// Returning a cloned navigator appeared to solve the threading issues + /// we had, because a single navigator was being used to compile all the + /// expressions in all potential threads. + /// + internal static XPathNavigator DefaultNavigator => navigator.Clone(); /// /// Manager to use when executing expressions that validate or /// load Schematron and Embedded Schematron schemas. /// - public static XmlNamespaceManager DefaultNsManager - { - get { return _nsmanager; } - } - - static Schema _full; + public static XmlNamespaceManager DefaultNsManager => nsmanager; /// /// A cached schema in Schematron format to validate schematron schemas. /// /// This is the version for standalone schemas. - public static Schema FullSchematron - { - get { return _full; } - } - - static Schema _embedded; + public static Schema FullSchematron => full; /// /// A cached schema in Schematron format to validate schematron schemas. /// /// This is the version for embedded schemas. - public static Schema EmbeddedSchematron - { - get { return _embedded; } - } - - static string _uid = String.Intern(Guid.NewGuid().ToString()); + public static Schema EmbeddedSchematron => embedded; /// /// A unique identifier to use for internal keys. /// - public static string UniqueKey - { - get { return _uid; } - } + public static string UniqueKey => uid; /// /// Force all static constructors in the library. @@ -171,5 +146,4 @@ public static void Setup() System.Diagnostics.Trace.Write(TagExpressions.Dir.RightToLeft); System.Diagnostics.Trace.WriteLine(FormattingUtils.XmlErrorPosition.RightToLeft); } -} - +} \ No newline at end of file diff --git a/src/Schematron/Diagnostic.cs b/src/Schematron/Diagnostic.cs index 6ddb2ea..9eb87fe 100644 --- a/src/Schematron/Diagnostic.cs +++ b/src/Schematron/Diagnostic.cs @@ -8,11 +8,6 @@ namespace Schematron; /// or successful via the @diagnostics IDREFS /// attribute. The <diagnostic> is stored at the schema level and referenced by id. /// -public class Diagnostic -{ - /// Gets or sets the unique ID of this diagnostic (value of the @id attribute). - public string Id { get; set; } = String.Empty; - - /// Gets or sets the raw text content / message of this diagnostic element. - public string Message { get; set; } = String.Empty; -} +/// Gets or sets the unique ID of this diagnostic (value of the @id attribute). +/// Gets or sets the raw text content / message of this diagnostic element. +public record Diagnostic(string Id, string Message); \ No newline at end of file diff --git a/src/Schematron/DomMatchedNodes.cs b/src/Schematron/DomMatchedNodes.cs index b67b190..0ae2288 100644 --- a/src/Schematron/DomMatchedNodes.cs +++ b/src/Schematron/DomMatchedNodes.cs @@ -19,29 +19,15 @@ class DomMatchedNodes : IMatchedNodes /// /// We use a collection for saving the hash codes. /// - List _matched = new List(); - - /// Initializes an instance of the class. - public DomMatchedNodes() - { - } + readonly List matched = []; /// See . - public bool IsMatched(XPathNavigator node) - { - return _matched.Contains(((IHasXmlNode)node).GetNode().GetHashCode()); - } + public bool IsMatched(XPathNavigator node) => matched.Contains(((IHasXmlNode)node).GetNode().GetHashCode()); /// See . - public void AddMatched(XPathNavigator node) - { - _matched.Add(((IHasXmlNode)node).GetNode().GetHashCode()); - } + public void AddMatched(XPathNavigator node) => matched.Add(((IHasXmlNode)node).GetNode().GetHashCode()); /// See . - public void Clear() - { - _matched.Clear(); - } + public void Clear() => matched.Clear(); } diff --git a/src/Schematron/EvaluableExpression.cs b/src/Schematron/EvaluableExpression.cs index 29c01a9..971aff0 100644 --- a/src/Schematron/EvaluableExpression.cs +++ b/src/Schematron/EvaluableExpression.cs @@ -14,36 +14,33 @@ namespace Schematron; /// public abstract class EvaluableExpression { - string _xpath = null!; - XPathExpression _expr = null!; - XmlNamespaceManager? _ns; + string xpath = null!; + XPathExpression expr = null!; + XmlNamespaceManager? ns; /// /// Cache the return type to avoid cloning the expression. /// - XPathResultType _ret; + XPathResultType ret; /// Initializes a new instance of the element with the expression specified. /// The expression to evaluate. - internal protected EvaluableExpression(string xpathExpression) - { - InitializeExpression(xpathExpression); - } + internal protected EvaluableExpression(string xpathExpression) => InitializeExpression(xpathExpression); /// Initializes a new instance of the element. - internal protected EvaluableExpression() - { - } + internal protected EvaluableExpression() { } /// Reinitializes the element with a new expression, /// after the class has already been constructed /// The expression to evaluate. protected void InitializeExpression(string xpathExpression) { - _xpath = xpathExpression; - _expr = Config.DefaultNavigator.Compile(xpathExpression); - _ret = _expr.ReturnType; - if (_ns != null) _expr.SetContext(_ns); + xpath = xpathExpression; + expr = Config.DefaultNavigator.Compile(xpathExpression); + ret = expr.ReturnType; + + if (ns != null) + expr.SetContext(ns); } /// Contains the compiled version of the expression. @@ -51,52 +48,36 @@ protected void InitializeExpression(string xpathExpression) /// A clone of the expression is always returned, because the compiled /// expression is not thread-safe for evaluation. /// - public XPathExpression CompiledExpression - { - get - { - if (_expr != null) return _expr.Clone(); - else return null!; - } - } + public XPathExpression CompiledExpression => expr != null ? expr.Clone() : null!; /// Contains the string version of the expression. - public string Expression - { - get { return _xpath; } - } + public string Expression => xpath; /// Contains the string version of the expression. - public XPathResultType ReturnType - { - get { return _ret; } - } + public XPathResultType ReturnType => ret; /// Returns the manager in use to resolve expression namespaces. - public XmlNamespaceManager? GetContext() - { - return _ns; - } + public XmlNamespaceManager? GetContext() => ns; /// Sets the manager to use to resolve expression namespaces. public void SetContext(XmlNamespaceManager nsManager) { - if (_expr != null) + if (expr != null) { // When the expression contains variable references ($name), .NET requires an // XsltContext (not just XmlNamespaceManager). Use a load-time stub that satisfies // the requirement; actual variable values are injected at evaluation time. try { - _expr.SetContext(nsManager); + expr.SetContext(nsManager); } catch (System.Xml.XPath.XPathException) { // Expression contains variables – use a load-time XsltContext stub. - _expr.SetContext(SchematronXsltContext.ForLoading(nsManager)); + expr.SetContext(SchematronXsltContext.ForLoading(nsManager)); } } - _ns = nsManager; + ns = nsManager; } } diff --git a/src/Schematron/EvaluationContextBase.cs b/src/Schematron/EvaluationContextBase.cs index 0525c26..a00ae75 100644 --- a/src/Schematron/EvaluationContextBase.cs +++ b/src/Schematron/EvaluationContextBase.cs @@ -27,6 +27,8 @@ namespace Schematron; /// public abstract class EvaluationContextBase { + XPathNavigator? source = null; + /// /// Keeps a list of nodes already matched. /// @@ -35,15 +37,7 @@ public abstract class EvaluationContextBase /// strategy for matching nodes is initialized, depending on the specific /// implementation of the in use. /// - protected IMatchedNodes Matched = null!; - - /// Creates the evaluation context - public EvaluationContextBase() - { - } - - #region Properties - IFormatter _formatter = Config.DefaultFormatter; + protected IMatchedNodes? Matched { get; set; } /// Gets or sets the class to use to format messages. /// @@ -51,70 +45,39 @@ public EvaluationContextBase() /// Usually, it will be changed based on parameters passed to the validator class, /// or exposed directly by it. /// - public IFormatter Formatter - { - get { return _formatter; } - set { _formatter = value; } - } - - StringBuilder _messages = new StringBuilder(); + public IFormatter Formatter { get; set; } = Config.DefaultFormatter; /// Gets or sets the messages generated by the validation process. /// /// Specific implementations of this class read/write this property /// while they accumulate validation messages. /// - public StringBuilder Messages - { - get { return _messages; } - set { _messages = value; } - } - - bool _haserrors = false; + public StringBuilder Messages { get; set; } = new(); /// Indicates if errors were found during the current evaluation. - public bool HasErrors - { - get { return _haserrors; } - set { _haserrors = value; } - } - - string _phase = String.Empty; + public bool HasErrors { get; set; } /// Gets or sets the specific validation phase to run. /// /// Schematron supports the concept of phases, where different sets of /// patterns can be executed at different times. This phase is initialized - /// to , which will mean all patterns are run. + /// to , which will mean all patterns are run. /// - public string Phase - { - get { return _phase; } - set { _phase = value; } - } - - Schema _schema = null!; + public string Phase { get; set; } = string.Empty; /// Gets or sets the schema to use for the validation. - public Schema Schema - { - get { return _schema; } - set { _schema = value; } - } - - XPathNavigator _source = null!; + public Schema? Schema { get; set; } - /// Gets or sets the document to validate. /// /// When this property is set, the appropriate /// strategy is picked, to perform optimum for various navigator implementations. /// public XPathNavigator Source { - get { return _source; } + get => source!; set { - _source = value; + source = value; if (value is IHasXmlNode) { Matched = new DomMatchedNodes(); @@ -129,7 +92,6 @@ public XPathNavigator Source } } } - #endregion /// /// Starts the evaluation process. @@ -146,10 +108,6 @@ public XPathNavigator Source /// /// By default, it clears the and sets to false. /// - protected void Reset() - { - _messages = new StringBuilder(); - - } + protected void Reset() => Messages = new StringBuilder(); } diff --git a/src/Schematron/Formatters/BooleanFormatter.cs b/src/Schematron/Formatters/BooleanFormatter.cs index 909c6b6..e57e3a8 100644 --- a/src/Schematron/Formatters/BooleanFormatter.cs +++ b/src/Schematron/Formatters/BooleanFormatter.cs @@ -8,17 +8,8 @@ namespace Schematron.Formatters; /// public class BooleanFormatter : FormatterBase { - /// - public BooleanFormatter() - { - } - /// /// Look at documentation. /// - public override void Format(Schema source, XPathNavigator context, StringBuilder output) - { - output.Append("Validation failed!"); - } -} - + public override void Format(Schema source, XPathNavigator context, StringBuilder output) => output.Append("Validation failed!"); +} \ No newline at end of file diff --git a/src/Schematron/Formatters/FormatterBase.cs b/src/Schematron/Formatters/FormatterBase.cs index 1d1fc60..ee7502a 100644 --- a/src/Schematron/Formatters/FormatterBase.cs +++ b/src/Schematron/Formatters/FormatterBase.cs @@ -9,11 +9,6 @@ namespace Schematron.Formatters; /// public abstract class FormatterBase : IFormatter { - /// - public FormatterBase() - { - } - /// /// Look at documentation. /// @@ -97,16 +92,16 @@ protected static StringBuilder FormatMessage(Test source, XPathNavigator context // As we move on, we have to append starting from the last point, // skipping the and expressions: Substring(offset, name.Index - offset). - int offset = 0; + var offset = 0; - for (int i = 0; i < source.NameValueOfExpressions.Count; i++) + for (var i = 0; i < source.NameValueOfExpressions.Count; i++) { - System.Text.RegularExpressions.Match name = source.NameValueOfExpressions[i]; + var name = source.NameValueOfExpressions[i]; nameExpr = source.NamePaths[i]; selectExpr = source.ValueOfSelects[i]; // Append the text without the expression. - sb.Append(msg.Substring(offset, name.Index - offset)); + sb.Append(msg[offset..name.Index]); // Does the name element have a path attribute? if (nameExpr != null) @@ -123,7 +118,9 @@ protected static StringBuilder FormatMessage(Test source, XPathNavigator context result = nodes.Current.Name; } else + { result = context.Evaluate(nameExpr) as string; + } if (result != null) sb.Append(result); @@ -133,16 +130,18 @@ protected static StringBuilder FormatMessage(Test source, XPathNavigator context { SetExpressionContext(selectExpr, source, ambientCtx); - string? result = null; + string? result; if (selectExpr.ReturnType == XPathResultType.NodeSet) { var nodes = (XPathNodeIterator)context.Evaluate(selectExpr); - result = String.Empty; + result = string.Empty; while (nodes.MoveNext()) result += nodes.Current.Value; } else + { result = context.Evaluate(selectExpr) as string; + } if (result != null) sb.Append(result); @@ -154,7 +153,7 @@ protected static StringBuilder FormatMessage(Test source, XPathNavigator context offset = name.Index + name.Length; } - sb.Append(msg.Substring(offset)); + sb.Append(msg[offset..]); return sb; } diff --git a/src/Schematron/Formatters/FormattingUtils.cs b/src/Schematron/Formatters/FormattingUtils.cs index 9927a42..1b619c0 100644 --- a/src/Schematron/Formatters/FormattingUtils.cs +++ b/src/Schematron/Formatters/FormattingUtils.cs @@ -9,13 +9,13 @@ namespace Schematron.Formatters; /// public class FormattingUtils { - static Regex _normalize; - static Regex _removeprefix; + static readonly Regex normalize; + static readonly Regex removeprefix; static FormattingUtils() { - _normalize = new Regex(@"\s+", RegexOptions.Compiled); - _removeprefix = new Regex(" .*", RegexOptions.Compiled); + normalize = new Regex(@"\s+", RegexOptions.Compiled); + removeprefix = new Regex(" .*", RegexOptions.Compiled); // Match the position suffix appended by XmlSchemaException.Message: // " An error occurred at {uri}({line}, {col})." @@ -26,14 +26,14 @@ static FormattingUtils() { } - static XPathExpression precedingSiblingsExpr = XPathExpression.Compile("preceding-sibling::*"); + static readonly XPathExpression precedingSiblingsExpr = XPathExpression.Compile("preceding-sibling::*"); /// /// Returns the full path to the context node. Clone the navigator to avoid loosing positioning. /// public static string GetFullNodePosition(XPathNavigator context, string previous, Test source) { - return GetFullNodePosition(context, previous, source, new Hashtable()); + return GetFullNodePosition(context, previous, source, []); } /// @@ -46,12 +46,12 @@ public static string GetFullNodePosition(XPathNavigator context, string previous /// public static string GetFullNodePosition(XPathNavigator context, string previous, Test source, Hashtable namespaces) { - string curr = context.Name; - string pref = String.Empty; + var curr = context.Name; + var pref = string.Empty; - if (context.NamespaceURI != String.Empty) + if (context.NamespaceURI != string.Empty) { - if (context.Prefix == String.Empty) + if (context.Prefix == string.Empty) { pref = source.GetContext()!.LookupPrefix(source.GetContext()!.NameTable.Get(context.NamespaceURI)); } @@ -62,16 +62,16 @@ public static string GetFullNodePosition(XPathNavigator context, string previous if (!namespaces.ContainsKey(context.NamespaceURI)) { - namespaces.Add(context.NamespaceURI, pref != null ? pref : ""); + namespaces.Add(context.NamespaceURI, pref ?? ""); } - else if (((String)namespaces[context.NamespaceURI]) != pref && + else if (((string)namespaces[context.NamespaceURI]) != pref && !namespaces.ContainsKey(context.NamespaceURI + ":" + pref)) { namespaces.Add(context.NamespaceURI + " " + pref, pref); } } - int sibs = 1; + var sibs = 1; foreach (XPathNavigator prev in context.Select(precedingSiblingsExpr)) if (prev.Name == curr) sibs++; @@ -79,7 +79,7 @@ public static string GetFullNodePosition(XPathNavigator context, string previous { var sb = new StringBuilder(); sb.Append("/"); - if (pref != String.Empty) sb.Append(pref).Append(":"); + if (pref != string.Empty) sb.Append(pref).Append(":"); sb.Append(curr).Append("[").Append(sibs).Append("]").Append(previous); return GetFullNodePosition(context, sb.ToString(), source, namespaces); } @@ -94,8 +94,8 @@ public static string GetFullNodePosition(XPathNavigator context, string previous /// public static string GetPositionInFile(XPathNavigator context, string spacing) { - if (!(context is IXmlLineInfo)) - return String.Empty; + if (context is not IXmlLineInfo) + return string.Empty; var sb = new StringBuilder(); sb.Append(spacing); @@ -113,7 +113,7 @@ public static string GetPositionInFile(XPathNavigator context, string spacing) /// public static string GetNodeSummary(XPathNavigator context, string spacing) { - return GetNodeSummary(context, new Hashtable(), spacing); + return GetNodeSummary(context, [], spacing); } /// @@ -124,14 +124,14 @@ public static string GetNodeSummary(XPathNavigator context, string spacing) /// public static string GetNodeSummary(XPathNavigator context, Hashtable namespaces, string spacing) { - XPathNavigator ctx = context.Clone(); + var ctx = context.Clone(); var sb = new StringBuilder(); sb.Append(spacing).Append("<"); // Get the element name XmlQualifiedName name; - if (ctx.NamespaceURI != String.Empty) + if (ctx.NamespaceURI != string.Empty) name = new XmlQualifiedName(ctx.LocalName, namespaces[ctx.NamespaceURI].ToString()); else name = new XmlQualifiedName(ctx.LocalName); @@ -156,24 +156,24 @@ public static string GetNodeSummary(XPathNavigator context, Hashtable namespaces /// public static string GetNamespaceSummary(XPathNavigator context, Hashtable namespaces, string spacing) { - if (namespaces.Count == 0) return String.Empty; + if (namespaces.Count == 0) return string.Empty; var sb = new StringBuilder(); - ICollection keys = namespaces.Keys; - string pref = String.Empty; + var keys = namespaces.Keys; + var pref = string.Empty; - foreach (object key in keys) + foreach (var key in keys) { sb.Append(spacing).Append("xmlns"); pref = namespaces[key].ToString(); - if (pref != String.Empty) + if (pref != string.Empty) sb.Append(":").Append(namespaces[key]); sb.Append("=\""); - if (pref != String.Empty) - sb.Append(_removeprefix.Replace(key.ToString(), String.Empty)); + if (pref != string.Empty) + sb.Append(removeprefix.Replace(key.ToString(), string.Empty)); else sb.Append(key); @@ -199,7 +199,7 @@ public static string NormalizeString(string input) { // Account for encoded strings, such as < (<) and > (>). return System.Web.HttpUtility.HtmlDecode( - _normalize.Replace(input, " ").Trim()); + normalize.Replace(input, " ").Trim()); } } diff --git a/src/Schematron/Formatters/LogFormatter.cs b/src/Schematron/Formatters/LogFormatter.cs index 8ed9302..ac804d4 100644 --- a/src/Schematron/Formatters/LogFormatter.cs +++ b/src/Schematron/Formatters/LogFormatter.cs @@ -10,19 +10,14 @@ namespace Schematron.Formatters; /// public class LogFormatter : FormatterBase { - /// - public LogFormatter() - { - } - /// /// Look at documentation. /// public override void Format(Test source, XPathNavigator context, StringBuilder output) { - StringBuilder sb = FormatMessage(source, context, source.Message); + var sb = FormatMessage(source, context, source.Message); // Finally remove any non-name schematron tag in the message. - string res = TagExpressions.AllSchematron.Replace(sb.ToString(), String.Empty); + var res = TagExpressions.AllSchematron.Replace(sb.ToString(), string.Empty); sb = new StringBuilder(); if (source is Assert) { @@ -39,11 +34,11 @@ public override void Format(Test source, XPathNavigator context, StringBuilder o //Accumulate namespaces found during traversal of node for its position. var ns = new Hashtable(); - sb.Append("\r\n\tAt: ").Append(FormattingUtils.GetFullNodePosition(context.Clone(), String.Empty, source, ns)); + sb.Append("\r\n\tAt: ").Append(FormattingUtils.GetFullNodePosition(context.Clone(), string.Empty, source, ns)); sb.Append(FormattingUtils.GetNodeSummary(context, ns, "\r\n\t ")); res = FormattingUtils.GetPositionInFile(context, "\r\n\t "); - if (res != String.Empty) sb.Append(res); + if (res != string.Empty) sb.Append(res); res = FormattingUtils.GetNamespaceSummary(context, ns, "\r\n\t "); if (res != string.Empty) sb.Append(res); @@ -65,7 +60,7 @@ public override void Format(Pattern source, XPathNavigator context, StringBuilde /// public override void Format(Schema source, XPathNavigator context, StringBuilder output) { - if (source.Title != String.Empty) + if (source.Title != string.Empty) output.Insert(0, source.Title + "\r\n"); else output.Insert(0, "Results from Schematron validation\r\n"); @@ -79,7 +74,7 @@ public override void Format(Schema source, XPathNavigator context, StringBuilder public override void Format(ValidationEventArgs source, StringBuilder output) { output.Append(" Error: "); - output.Append(FormattingUtils.XmlErrorPosition.Replace(source.Message, String.Empty)); + output.Append(FormattingUtils.XmlErrorPosition.Replace(source.Message, string.Empty)); output.Append("\r\n At: (Line: ").Append(source.Exception.LineNumber); output.Append(", Column: ").Append(source.Exception.LinePosition).Append(")\r\n"); } diff --git a/src/Schematron/Formatters/SimpleFormatter.cs b/src/Schematron/Formatters/SimpleFormatter.cs index d71a5a4..5714e72 100644 --- a/src/Schematron/Formatters/SimpleFormatter.cs +++ b/src/Schematron/Formatters/SimpleFormatter.cs @@ -13,17 +13,12 @@ namespace Schematron.Formatters; /// public class SimpleFormatter : LogFormatter { - /// - public SimpleFormatter() - { - } - /// /// Look at documentation. /// public override void Format(Test source, XPathNavigator context, StringBuilder output) { - StringBuilder sb = FormatMessage(source, context, source.Message); + var sb = FormatMessage(source, context, source.Message); if (!string.IsNullOrEmpty(source.Severity)) sb.Insert(0, "[" + source.Severity + "] "); @@ -34,7 +29,7 @@ public override void Format(Test source, XPathNavigator context, StringBuilder o sb.Insert(0, "\tReport: "); var ns = new Hashtable(); - sb.Append("\r\n\tAt: " + FormattingUtils.GetFullNodePosition(context.Clone(), String.Empty, source, ns)); + sb.Append("\r\n\tAt: " + FormattingUtils.GetFullNodePosition(context.Clone(), string.Empty, source, ns)); sb.Append("\r\n"); output.Append(sb.ToString()); diff --git a/src/Schematron/Formatters/XmlFormatter.cs b/src/Schematron/Formatters/XmlFormatter.cs index af6aef9..f949fcc 100644 --- a/src/Schematron/Formatters/XmlFormatter.cs +++ b/src/Schematron/Formatters/XmlFormatter.cs @@ -11,11 +11,6 @@ namespace Schematron.Formatters; /// public class XmlFormatter : FormatterBase { - /// - public XmlFormatter() - { - } - /// /// Namespace of generated output. /// @@ -26,10 +21,12 @@ public XmlFormatter() /// public override void Format(Test source, XPathNavigator context, StringBuilder output) { - string msg = source.Message; - var writer = new XmlTextWriter(new StringWriter(output)); - //Temporary disable namespace support. - writer.Namespaces = false; + var msg = source.Message; + var writer = new XmlTextWriter(new StringWriter(output)) + { + //Temporary disable namespace support. + Namespaces = false + }; // Start element declaration. writer.WriteStartElement("message"); @@ -44,7 +41,7 @@ public override void Format(Test source, XPathNavigator context, StringBuilder o msg = FormatMessage(source, context, msg).ToString(); // Finally remove any non-name schematron tag in the message. - string res = TagExpressions.AllSchematron.Replace(msg, String.Empty); + var res = TagExpressions.AllSchematron.Replace(msg, string.Empty); //Accumulate namespaces found during traversal of node for its position. var ns = new Hashtable(); @@ -52,11 +49,11 @@ public override void Format(Test source, XPathNavigator context, StringBuilder o // Write element. writer.WriteElementString("text", res); // Write element. - writer.WriteElementString("path", FormattingUtils.GetFullNodePosition(context.Clone(), String.Empty, source, ns)); + writer.WriteElementString("path", FormattingUtils.GetFullNodePosition(context.Clone(), string.Empty, source, ns)); // Write element. //writer.WriteElementString("summary", FormattingUtils.GetNodeSummary(context, ns, String.Empty)); writer.WriteStartElement("summary"); - writer.WriteRaw(FormattingUtils.GetNodeSummary(context, ns, String.Empty)); + writer.WriteRaw(FormattingUtils.GetNodeSummary(context, ns, string.Empty)); writer.WriteEndElement(); // Write element. @@ -78,8 +75,8 @@ public override void Format(Test source, XPathNavigator context, StringBuilder o /// public override void Format(Rule source, XPathNavigator context, StringBuilder output) { - string res = " public override void Format(Pattern source, XPathNavigator context, StringBuilder output) { - string elemName = source is Group ? "group" : "pattern"; - string res = "<" + elemName + " name=\"" + source.Name + "\" "; - if (source.Id != String.Empty) res += "id=\"" + source.Id + "\" "; + var elemName = source is Group ? "group" : "pattern"; + var res = "<" + elemName + " name=\"" + source.Name + "\" "; + if (source.Id != string.Empty) res += "id=\"" + source.Id + "\" "; res += ">"; output.Insert(0, res); @@ -125,8 +122,8 @@ public override void Format(Schema source, XPathNavigator context, StringBuilder source.NsManager.LookupNamespace(source.NsManager.NameTable.Get(prefix))); } - if (source.Title != String.Empty) writer.WriteAttributeString("title", source.Title); - if (source.SchematronEdition != String.Empty) writer.WriteAttributeString("schematronEdition", source.SchematronEdition); + if (source.Title != string.Empty) writer.WriteAttributeString("title", source.Title); + if (source.SchematronEdition != string.Empty) writer.WriteAttributeString("schematronEdition", source.SchematronEdition); writer.WriteRaw(output.ToString()); writer.WriteEndElement(); @@ -145,7 +142,7 @@ public override void Format(ValidationEventArgs source, StringBuilder output) writer.WriteStartElement("message"); // Write element. - writer.WriteElementString("text", FormattingUtils.XmlErrorPosition.Replace(source.Message, String.Empty)); + writer.WriteElementString("text", FormattingUtils.XmlErrorPosition.Replace(source.Message, string.Empty)); // Write element. writer.WriteStartElement("position"); @@ -215,8 +212,10 @@ public override void Format(StringBuilder output) output.Remove(0, output.Length); // Create indented output. - writer = new XmlTextWriter(new StringWriter(output)); - writer.Formatting = Formatting.Indented; + writer = new XmlTextWriter(new StringWriter(output)) + { + Formatting = Formatting.Indented + }; writer.WriteStartDocument(); writer.WriteNode(new XmlTextReader(new StringReader(sb.ToString())), false); writer.WriteEndDocument(); diff --git a/src/Schematron/GenericMatchedNodes.cs b/src/Schematron/GenericMatchedNodes.cs index 57e1738..98215ba 100644 --- a/src/Schematron/GenericMatchedNodes.cs +++ b/src/Schematron/GenericMatchedNodes.cs @@ -20,34 +20,24 @@ class GenericMatchedNodes : IMatchedNodes /// /// Uses a simple arraylist to keep the navigators. /// - ArrayList _matched = new ArrayList(); - - /// Initializes an instance of the class. - public GenericMatchedNodes() - { - } + readonly ArrayList matched = []; /// See . public bool IsMatched(XPathNavigator node) { - foreach (XPathNavigator nav in _matched) + foreach (XPathNavigator nav in matched) { - if (node.IsSamePosition(nav)) return true; + if (node.IsSamePosition(nav)) + return true; } return false; } /// See . - public void AddMatched(XPathNavigator node) - { - _matched.Add(node.Clone()); - } + public void AddMatched(XPathNavigator node) => matched.Add(node.Clone()); /// See . - public void Clear() - { - _matched.Clear(); - } + public void Clear() => matched.Clear(); } diff --git a/src/Schematron/Group.cs b/src/Schematron/Group.cs index 38f63d9..7d18b22 100644 --- a/src/Schematron/Group.cs +++ b/src/Schematron/Group.cs @@ -7,6 +7,16 @@ namespace Schematron; /// public class Group : Pattern { + /// + /// Initializes a new instance of the class with a name and identifier. + /// + /// The name of the group. + /// The unique identifier of the group. internal protected Group(string name, string id) : base(name, id) { } + + /// + /// Initializes a new instance of the class with a name. + /// + /// The name of the group. internal protected Group(string name) : base(name) { } } diff --git a/src/Schematron/InvalidExpressionException.cs b/src/Schematron/InvalidExpressionException.cs index 9874bef..47830c9 100644 --- a/src/Schematron/InvalidExpressionException.cs +++ b/src/Schematron/InvalidExpressionException.cs @@ -10,27 +10,20 @@ namespace Schematron; public class InvalidExpressionException : ApplicationException { /// Initializes a new instance of the exception class. - public InvalidExpressionException() : base() - { - } + public InvalidExpressionException() : base() { } /// /// Initializes an instance of the class with a specified error message. /// /// The error message that explains the reason for the exception. - public InvalidExpressionException(string message) : base(message) - { - } + public InvalidExpressionException(string message) : base(message) { } /// /// For serialization purposes. /// /// Info /// Context - protected InvalidExpressionException(SerializationInfo info, StreamingContext context) : - base(info, context) - { - } + protected InvalidExpressionException(SerializationInfo info, StreamingContext context) : base(info, context) { } /// /// Initializes an instance of the class with a specified error message @@ -38,9 +31,6 @@ protected InvalidExpressionException(SerializationInfo info, StreamingContext co /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception. - public InvalidExpressionException(string message, Exception innerException) : - base(message, innerException) - { - } + public InvalidExpressionException(string message, Exception innerException) : base(message, innerException) { } } diff --git a/src/Schematron/Let.cs b/src/Schematron/Let.cs index ba795f7..0725391 100644 --- a/src/Schematron/Let.cs +++ b/src/Schematron/Let.cs @@ -8,20 +8,9 @@ namespace Schematron; /// in sibling and descendant and elements. /// In ISO Schematron 2025 the optional @as attribute declares the variable's expected type. /// -public class Let -{ - /// Gets or sets the variable name (value of the @name attribute). - public string Name { get; set; } = String.Empty; - - /// - /// Gets or sets the variable value expression (value of the @value attribute). - /// May be when the value is supplied as element content. - /// - public string? Value { get; set; } - - /// - /// Gets or sets the optional declared type for the variable (value of the @as attribute, - /// introduced in ISO Schematron 2025). - /// - public string? As { get; set; } -} +/// Gets or sets the variable name (value of the @name attribute). +/// Gets or sets the variable value expression (value of the @value attribute). +/// May be when the value is supplied as element content. +/// Gets or sets the optional declared type for the variable (value of the @as attribute, +/// introduced in ISO Schematron 2025). +public record Let(string Name, string? Value = default, string? As = default); \ No newline at end of file diff --git a/src/Schematron/NMatrix.Schematron.xml b/src/Schematron/NMatrix.Schematron.xml deleted file mode 100644 index a2c0eef..0000000 --- a/src/Schematron/NMatrix.Schematron.xml +++ /dev/null @@ -1,1900 +0,0 @@ - - - - Schematron - - - - - Represents an assert element of the Schematron schema. - - - As stated in the , this is - the lowest element in a Schematron schema. This element contains the expression - to execute in the context of its parent . - If the results of the execution of the expression are False, the - assert fails and the correponding message will be displayed. - - Constructor is not public. To programatically create an instance of this - class use the factory method. - - - - - - - Constructs a new Assert object. - XPath expression to test. - Message to display if the assert fails. - - - - Represents the an error in the Schematron schema. - - - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The error message that explains the reason for the exception. - - - - Initializes a new instance of the class. - - Info - Context - - - - Initializes a new instance of the class. - - The error message that explains the reason for the exception. - The exception that is the cause of the current exception. - - - - This class keeps static precompiled expressions used - in the Schematron schema loading and validation processes. - - - All expressions are compiled against the - object. All the objects are initialized with - the for schematron and XML Schema - namespaces resolution. - - - - - - - Provides global settings for Schematron validation. - - - This class is public to allow inheritors of Schematron elements - to use these global settings. - - - - - - - Initializes global settings. - - - - - The default object to use to format messages from validation. - - - - - A default empty navigator used to pre-compile XPath expressions. - - - Compiling doesn't involve any namespace, - name table or other specific processing. It's only a parsing procedure that - builds the abstract syntax tree for later evaluation. So we can safely - use an empty to compile them against. - - - expr = Config.DefaultNavigator.Compile("//sch:pattern"); - other code; - - - - - - - - - Manager to use when executing expressions that validate or - load Schematron and Embedded Schematron schemas. - - - - - A cached schema in Schematron format to validate schematron schemas. - - This is the version for standalone schemas. - - - - A cached schema in Schematron format to validate schematron schemas. - - This is the version for embedded schemas. - - - - A unique identifier to use for internal keys. - - - - - Force all static constructors in the library. - - - - - Strategy class for matching and keeping references to nodes in an xml document. - - - When an is created from an , - it implements the interface, which is used to gain - access to the underlying node. - - - - - - - We use an optimized collection for saving the hash codes. - - - - Initializes an instance of the class. - - - See . - - - See . - - - See . - - - - Base class for elements that can be evaluated by an XPath expression. - - - This class performs the expression compilation, and provides - access to the context through two methods. - - - - - - - Cache the return type to avoid cloning the expression. - - - - Initializes a new instance of the element with the expression specified. - The expression to evaluate. - - - Initializes a new instance of the element. - - - Reinitializes the element with a new expression, - after the class has already been constructed - The expression to evaluate. - - - Contains the compiled version of the expression. - - A clone of the expression is always returned, because the compiled - expression is not thread-safe for evaluation. - - - - Contains the string version of the expression. - - - Contains the string version of the expression. - - - Returns the manager in use to resolve expression namespaces. - - - Sets the manager to use to resolve expression namespaces. - - - - Base class for Schematron evaluation contexts. - - - The schematron elements don't provide evaluation code. They just - represent the elements in the . - - For evaluation purposes, this class provides the iteration and execution - process, accumulates results, moves the cursor, etc. Here is where the - actual evaluation takes place. This way we isolate the schema design - from the different execution models we can use, for example, - (we may add asynchonous execution later). - - - The validator classes provided use instances of this strategy object to - validate documents provided by the user. - - - - - - - - Keeps a list of nodes already matched. - - - When the property is set, the appropriate - strategy for matching nodes is initialized, depending on the specific - implementation of the in use. - - - - Creates the evaluation context - - - Gets or sets the class to use to format messages. - - This object is initialized to the instance. - Usually, it will be changed based on parameters passed to the validator class, - or exposed directly by it. - - - - Gets or sets the messages generated by the validation process. - - Specific implementations of this class read/write this property - while they accumulate validation messages. - - - - Indicates if errors were found during the current evaluation. - - - Gets or sets the specific validation phase to run. - - Schematron supports the concept of phases, where different sets of - patterns can be executed at different times. This phase is initialized - to , which will mean all patterns are run. - - - - Gets or sets the schema to use for the validation. - - - Gets or sets the document to validate. - - When this property is set, the appropriate - strategy is picked, to perform optimum for various navigator implementations. - - - - - Starts the evaluation process. - - - When the process is finished, the results are placed - in the property. - - - - - Resets the state of the current context. - - - By default, it clears the and sets to false. - - - - - Provides a simple failure message, without any details of specific validation errors. - - - - - - - - Look at documentation. - - - - - Look at documentation. - - - - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - - - - Returns the full path to the context node. Clone the navigator to avoid loosing positioning. - - - - - Returns the full path to the context node. Clone the navigator to avoid loosing positioning. - - - Cloning is not performed inside this method because it is called recursively. - Keeping positioning is only relevant to the calling procedure, not subsequent - recursive calls. This way we avoid creating unnecessary objects. - - - - - Returns line positioning information if supported by the XPathNavigator implementation. - - - - - Returns abreviated node information, including attribute values. - - - - - Returns abreviated node information, including attribute values. - - - The namespaces param is optionally filled in . - - - - - Outputs the xmlns declaration for each namespace found in the parameter. - - - - - Allows to match the string stating the node position from System.Xml error messages. - - - This regular expression is used to remove the node position from the validation error - message, to maintain consistency with schematron messages. - - - - - Returns a decoded string, with spaces trimmed. - - - - - Interface for custom formatters, which are used to generate - output from schema validation. - - - Provides formatting methods for Schematron specific elements and - for validation through the . - Custom formatters implement methods to provide message formatting. -

- Am abstract base implementation is provided in to ease - the extending process. -

-
-
- - - Provides formatting of both and elements. - - - Implementations may use the received context to add details to the output message. - The contains messages accumulated so far in the validation process. - - The current navigator node where the test failed. - The message to output. - The or element - which failed. - - - - Provides formatting for a element. - - - Implementations may use the received context to add details to the output message. - The contains messages accumulated so far in the validation process. - - The navigator where the inner or - elements failed. - The message to output. - The element containing failed - or elements. - - - - Provides formatting for a element. - - - Implementations may use the received context to add details to the output message. - The contains messages accumulated so far in the validation process. - - The source document navigator where evaluation took place. - The message to output. - The element containing failed elements. - - - - Provides formatting for a element. - - - Implementations may use the received context to add details to the output message. - The contains messages accumulated so far in the validation process. - - The source document navigator where evaluation took place. - The message to output. - The being evaluated. - - - - Provides formatting for a element. - - - Implementations may use the received context to add details to the output message. - The contains messages accumulated so far in the validation process. - - The source document navigator where evaluation took place. - The message to output. - The being evaluated. - - - - Provides formatting for a element being validated - through a . - - - Usually will output schema-level formatting for XmlSchema validation. Recall that - multiple schemas may have been configured with the reader and validated simultaneously. - The contains messages accumulated so far in the validation process. - - The message to output. - The reader in use to validate the schema. - - - - Formats the output of XmlSchema validation. - - - The contains messages accumulated so far in the validation process. - - The message to output. - The argument received by the handler - during XmlSchema validation. - - - - Enclosing message for all schemas being validated. - - - Usually will add any enclosing message to the results of the global Xml validation. - The contains messages accumulated so far in the validation process. - - The message to output. - The collection of schemas in use for validation. - - - - Enclosing message for all schemas being validated. - - - Usually will add any enclosing message to the results of the global Schematron validation. - The contains messages accumulated so far in the validation process. - - The message to output. - The collection of schemas in use for validation. - - - - Formats the whole message built so far. - - - Usually will perform any last-minute formatting of the whole message before - being returned by the calling application. For example, the - uses this method to enclose the whole message in an <output> element. - The contains messages accumulated so far in the validation process. - - The message to output. - - - - Provides a complete log of validation errors in text format. - - - - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Provides a simplified log of errors. - - - Similar output as , but doesn't provide - node position in file and namespace summary text. - - - - - - - - Look at documentation. - - - - - Provides an Xml output from validation. - - - - - - - - Namespace of generated output. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Strategy class for matching and keeping references to nodes in - an unknown implementation of . - - - This implementation uses the standard - to know if a navigator has already been matched. This is not optimum because - a complete traversal of nodes matched so far has to be performed, but it will - work with all implementations of . - - - - - - - Uses a simple arraylist to keep the navigators. - - - - Initializes an instance of the class. - - - See . - - - See . - - - See . - - - - Defines the common interface used by the different node-matching strategies. - - - As different can exist, and even be developed - in the future, we have to take into account that the data store can change. - So in order to be efficient at keeping nodes matched so far, to satisfy the - , we provide a common interface and an - implementation optimized for specific stores. - - Each navigator implementation typically provides an interface to let - applications get access to the underlying store, such as the - or interfaces, implemented in navigators create by - or classes. - - - - - - - Checks if an specific node has already been matched. - The node to check. - - - Adds a node to the list of nodes matched so far. - The node to add. - - - Clears the list of matched nodes. - - - - An optimized collection for holding values. - - - This class was generated automatically by TextBox. - Original author is Shawn Van Ness. The code contains comments by - him, together with a summary of features and acknowledgments :). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The exception that is thrown when an invalid XPath expression is used. - - - - - - Initializes a new instance of the exception class. - - - - Initializes an instance of the class with a specified error message. - - The error message that explains the reason for the exception. - - - - For serialization purposes. - - Info - Context - - - - Initializes an instance of the class with a specified error message - and a reference to the inner exception that is the cause for this exception. - - The error message that explains the reason for the exception. - The exception that is the cause of the current exception. - - - - Return type to use as the validation result. - - - - - Use an for validation and return type. - - - - - Use an for validation and return type. - - - - - Use the default type, equal to , for validation and return type. - - - - - Represents the valid output formats. - - - Items will be added to the list to reflect the - additional implementations we - will develop. - - - - - - Use the class. - - - Use the class. - - - Use the class. - - - Use the default formatter, which is the . - - - Use the class. - - - - A Pattern element, containing elements. - - - Constructor is not public. To programatically create an instance of this - class use the factory method. - - - - - - Gets or sets the pattern's name. - - - Gets or sets the pattern's Id. - - This property is important because it is used by the - to activate certain patterns. - - - - Gets the rules contained in this pattern. - - - Initializes the pattern with the name specified. - The name of the new pattern. - - - Initializes the pattern with the name and id specified. - The name of the new pattern. - The id of the new pattern. - - - Creates a new rule instance. - - Inheritors should override this method to create instances - of their own rule implementations. - - - - Creates a new rule instance with the context specified. - - Inheritors should override this method to create instances - of their own rule implementations. - - - The context for the new rule. - - - - A collection of Pattern elements. - - - - - - - - Returns the Pattern element at the specified position. - - - - - - - - - - - - - - - - - - - - - - - - - - - - A Phase element, containing the active elements. - - - The allows a certaing degree of workflow - through the use of phases. A document can have several states, and - therefore different sets of rules should be checked against it. - - This element allows execution of a set of 'active' patterns. - - - Constructor is not public. To programatically create an instance of this - class use the factory method. - - - - - - - - The identifier to check for All phases. - - Causes all the patterns in a schema to be checked, - irrespective of the phases where they are activated. - - - Initializes a new instance of the class with the specified Id. - The Id of the new phase. - - - Initializes a new instance of the class. - - - Gets or sets the phase identifier. - - - Gets the collection of child elements. - - - Creates a new pattern instance. - - Inheritors should override this method to create instances - of their own pattern implementations. - - The name of the new pattern. - The unique identifier of the new pattern. - - - Creates a new pattern instance. - - This method calls the overloaded version passing a default - value for the pattern's id. - Inheritors can override this method if they want to provide - a different default value. - - The name of the new pattern. - - - A collection of Phase elements - - - - - - - - Required indexer. - - - - - - - - - - - - - - - - - - - A Report element. - - - As stated in the , this is the other - lowest element (aside from ) in a Schematron schema. - This element contains the expression - to execute in the context of its parent . - If the results of the execution of the expression are true, the - report succeeds and the correponding message will be displayed. - - Constructor is not public. To programatically create an instance of this - class use the factory method. - - - - - - - Initializes a new instance of the class with the parameters specified. - The XPath expression to test. - The message to return. - - - - A Rule element. - - - Rules enclose the and - elements, providing the context for their evaluation. - According to the , nodes can be evaluated - by only one rule inside a , so typical schema - design includes placing the most exceptional rules first, down to - more generally applicable ones. - - Constructor is not public. To programatically create an instance of this - class use the factory method. - - - - Should we add support for the key element? - - - - - - Creates an abstract rule, without context. - - - - Initializes a new instance of the class, with the received context. - The rule's context to evaluate. - If passed a null or an , this is implicitly an abstract rule. - - - Initializes the context for the rule. - The rule's context to evaluate. - - If passed a null or an , this is implicitly an abstract rule. - - - Rules are evaluated through all the document (//), unless they - explicitly want to start from the root (/). This is consistent - with XSLT template match behavior. So we have to split the expression - per union (|) to add the root expression in these cases. - - - - Creates a new assert instance. - - Inheritors should override this method to create instances - of their own assert implementations. - - - The XPath expression to test for the assert. See - . - - - The message to display if the assert fails. See - . - - - - Creates a new report instance. - - Inheritors should override this method to create instances - of their own report implementations. - - - The XPath expression to test for the report. See - . - - - The message to display if the report succeeds. See - . - - - - - - - - - - - - - - - - - - - - - - - - - - - Only abstract rules can be used as a base for extensions. - - - A collection of Rule elements. - - - - - - - - Returns the Rule element at the specified position. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Lacks attributes defined in Schematron, but not in use currently. - - - - The Schematron namespace. - - - - - - - - - - - - - - - - - - - - Loads the schema from the specified URI. - - - - - Loads the schema from the reader. Closing the reader is responsibility of the caller. - - - - - Loads the schema from the stream. Closing the stream is responsibility of the caller. - - - - - Loads the schema from the reader. Closing the reader is responsibility of the caller. - - - - - - - - - - - - - - - - - - - - - - - - - A collection of schematron schemas. - - - - - - - - Returns the Schema element at the specified position. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This code was generated by VS Paste Special Xml to Class. It therefore may feel like overkill and a poor model to support deserialization operations - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Evaluates all the schema elements synchronously. - - - - - See for a description of the purpose and - of evaluation contexts, and where are they used. - - - - Creates the evaluation context - - - - Starts the evaluation process. - - - When the process is finished, the results are placed - in the property. - - - - - Evaluates the selected . - - - As most of the other evaluation methods, it repositions the - navigator on the root node. - - The to evaluate. - Contains the builder to accumulate messages in. - A boolean indicating the presence of errors (true). - - - - Evaluates the selected . - - - As most of the other evaluation methods, it repositions the - navigator on the root node. - - Clears the object before - proceeding, as the restriction about node mathing (see ) - applies only inside a single pattern. - - - The to evaluate. - Contains the builder to accumulate messages in. - A boolean indicating if a new message was added. - - - - Evaluates the selected . - - - and are iterated - and each and is executed against - the context selected by the . - - Nodes matched are added to the list of - nodes to skip in the next rule, using the method. - This object is a strategy object which implements different algorithms for matching and - saving node references, as the actual implementation provides - different methods for accessing the underlying source. - - This makes the implementation both performant and compliant with - the restriction about node mathing (see ) in the spec. - - - - - - - As most of the other evaluation methods, it repositions the - navigator on the root node. - - - The to evaluate. - Contains the builder to accumulate messages in. - A boolean indicating if a new message was added. - - The rule to evaluate is abstract (see ). - - - - - Performs the evaluation of the . - - - This is where the actual assert expression is evaluated. If - the returns false, - a formated message is generated from the property. - - The to evaluate. - The context node for the execution. - Contains the builder to accumulate messages in. - A boolean indicating if a new message was added. - - - - Performs the evaluation of the . - - - This is where the actual report expression is evaluated. If - the returns true, - a formated message is generated from the property. - - The to evaluate. - The context node for the execution. - Contains the builder to accumulate messages in. - A boolean indicating if a new message was added. - - - - - - - The compiled regular expression to replace the special name tag inside a message. - - - Replaces each instance of name tags with the value un the current context element. - - - - - - - - Base class for testing units of Schematron, such as Assert or Report elements. - - - - - - - - - - - - - - - - - - - - - - - - - - - A collection of Test elements. - - - - - - - - Returns the Test element at the specified position. - - - - - - - - - - - - - - - - - - - - - - - - - - - - Represents the an error in the Schematron schema. - - - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The error message that explains the reason for the exception. - - - - Initializes a new instance of the class. - - Info - Context - - - - Initializes a new instance of the class. - - The error message that explains the reason for the exception. - The exception that is the cause of the current exception. - - - - Performs validation of Schematron elements and schemas. - - - Can handle either standalone or embedded schematron schemas. If the schematron - is embedded in an XML Schema, the input document is validated against both at - the same time. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class, using the specified output format for error messages. - - Output format of error messages. - - - - Initializes a new instance of the class, using the specified return type. - - The type to use for validation and return type. - - - - Initializes a new instance of the class, using the specified options. - - Output format of error messages. - The type to use for validation and return type. - - - - Initializes the validator with the options received from the constructor overloads. - - Output format of error messages. - The type to use for validation and return type. - - - Creates the evaluation context to use. - - Inheritors can override this method should they want to - use a different strategy for node traversal and evaluation - against the source file. - - - - - - - - - - - - - - - - - Exposes the schematron schemas to use for validation. - - - - - Exposes the XML schemas to use for validation. - - - - - Adds an XML Schema to the collection to use for validation. - - - - - Adds a Schematron schema to the collection to use for validation. - - - - - Adds a set of XML Schemas to the collection to use for validation. - - - - - Adds a set of Schematron schemas to the collection to use for validation. - - - - - Adds a schema to the collection to use for validation from the specified URL. - - - - - Adds a schema to the collection to use for validation. - - - - - Adds a schema to the collection to use for validation. - - - - - Adds a schema to the collection to use for validation. - - Processing takes place here. - - - - Performs Schematron-only validation. - - - Even when implements IXPathNavigable, WXS - validation can't be performed once it has been loaded becasue a - validating reader has to be used. - - - The document is invalid with respect to the loaded schemas. - - - - - Performs Schematron-only validation. - - - The document is invalid with respect to the loaded schemas. - - - - Performs validation of the document at the specified URI. - The document location. - - The document is invalid with respect to the loaded schemas. - - The loaded instance. - - - Performs validation of the document using the specified reader. - The reader pointing to the document to validate. - The loaded instance. - - The document is invalid with respect to the loaded schemas. - - The loaded instance. - - - Performs validation of the document using the specified stream. - The stream with the document to validate. - - The document is invalid with respect to the loaded schemas. - - The loaded instance. - - - Performs validation of the document using the received reader. - Where the actual work takes place - The reader pointing to the document to validate. - - The document is invalid with respect to the loaded schemas. - - The loaded instance. - - - - Strategy class for matching and keeping references to nodes in an . - - - - - When an is created from an , - it implements the interface, which is used to gain - access to the underlying node position. - - - - - The table contains an item for each line, and the item value - is an instance of our class for - optimized value types storage. - - - - Initializes an instance of the class. - - - See . - - - See . - - - See . - - - diff --git a/src/Schematron/Param.cs b/src/Schematron/Param.cs index f41b3c2..50ef169 100644 --- a/src/Schematron/Param.cs +++ b/src/Schematron/Param.cs @@ -12,8 +12,8 @@ namespace Schematron; public class Param { /// Gets or sets the parameter name (matches the $name placeholder in the abstract pattern). - public string Name { get; set; } = String.Empty; + public string Name { get; set; } = string.Empty; /// Gets or sets the substitution value. - public string Value { get; set; } = String.Empty; + public string Value { get; set; } = string.Empty; } diff --git a/src/Schematron/Pattern.cs b/src/Schematron/Pattern.cs index 7d1a0cc..ea55c42 100644 --- a/src/Schematron/Pattern.cs +++ b/src/Schematron/Pattern.cs @@ -7,70 +7,46 @@ namespace Schematron; /// Constructor is not public. To programatically create an instance of this /// class use the factory method. /// -/// -/// public class Pattern { - string _name = String.Empty; - string _id = String.Empty; - RuleCollection _rules = new RuleCollection(); - LetCollection _lets = new LetCollection(); + readonly RuleCollection rules = []; + readonly LetCollection lets = []; - #region Properties /// Gets or sets the pattern's name. - public string Name - { - get { return _name; } - set { _name = value; } - } + public string Name { get; set; } = string.Empty; /// Gets or sets the pattern's Id. /// /// This property is important because it is used by the /// to activate certain patterns. /// - public string Id - { - get { return _id; } - set { _id = value; } - } + public string Id { get; set; } = string.Empty; /// Gets the rules contained in this pattern. - public RuleCollection Rules - { - get { return _rules; } - } - #endregion + public RuleCollection Rules => rules; /// Initializes the pattern with the name specified. /// The name of the new pattern. - internal protected Pattern(string name) - { - _name = name; - } + internal protected Pattern(string name) => Name = name; /// Initializes the pattern with the name and id specified. /// The name of the new pattern. /// The id of the new pattern. internal protected Pattern(string name, string id) { - _name = name; - _id = id; + Name = name; + Id = id; } /// Gets the variable bindings declared in this pattern (<let> elements). - public LetCollection Lets => _lets; + public LetCollection Lets => lets; - #region Overridable Factory Methods /// Creates a new rule instance. /// /// Inheritors should override this method to create instances /// of their own rule implementations. /// - public virtual Rule CreateRule() - { - return new Rule(); - } + public virtual Rule CreateRule() => new(); /// Creates a new rule instance with the context specified. /// @@ -80,10 +56,6 @@ public virtual Rule CreateRule() /// /// The context for the new rule. /// - public virtual Rule CreateRule(string context) - { - return new Rule(context); - } - #endregion + public virtual Rule CreateRule(string context) => new(context); } diff --git a/src/Schematron/PatternCollection.cs b/src/Schematron/PatternCollection.cs index dc62b46..9c0f2b4 100644 --- a/src/Schematron/PatternCollection.cs +++ b/src/Schematron/PatternCollection.cs @@ -1,76 +1,5 @@ -using System.Collections; - namespace Schematron; -/// A collection of Pattern elements. -/// -/// -public class PatternCollection : CollectionBase -{ - /// - public PatternCollection() - { - } - - /// Returns the Pattern element at the specified position. - public Pattern this[int index] - { - get { return (Pattern)InnerList[index]; } - set { InnerList[index] = value; } - } - - /// - public int Add(Pattern value) - { - return InnerList.Add(value); - } - - /// - public void AddRange(Pattern[] values) - { - foreach (Pattern elem in values) - Add(elem); - } - - /// - public void AddRange(PatternCollection values) - { - foreach (Pattern elem in values) - Add(elem); - } - - /// - public bool Contains(Pattern value) - { - return InnerList.Contains(value); - } - - /// - public void CopyTo(Pattern[] array, int index) - { - InnerList.CopyTo(array, index); - } - - /// - public int IndexOf(Pattern value) - { - return InnerList.IndexOf(value); - } - - /// - public void Insert(int index, Pattern value) - { - InnerList.Insert(index, value); - } - - /// - public void Remove(Pattern value) - { - int index = IndexOf(value); - if (index < 0) - throw (new ArgumentException("The specified object is not found in the collection")); - - RemoveAt(index); - } -} +/// A collection of Pattern elements. +public class PatternCollection : List; \ No newline at end of file diff --git a/src/Schematron/Phase.cs b/src/Schematron/Phase.cs index b02bd0f..3a3a37d 100644 --- a/src/Schematron/Phase.cs +++ b/src/Schematron/Phase.cs @@ -19,10 +19,7 @@ namespace Schematron; /// public class Phase { - string _id = String.Empty; - string _from = String.Empty; - string _when = String.Empty; - PatternCollection _patterns = new PatternCollection(); + readonly PatternCollection patterns = []; /// /// The identifier to check for All phases. @@ -33,38 +30,23 @@ public class Phase /// Initializes a new instance of the class with the specified Id. /// The Id of the new phase. - internal protected Phase(string id) - { - Id = id; - } + internal protected Phase(string id) => Id = id; /// Initializes a new instance of the class. - internal protected Phase() - { - } + internal protected Phase() { } - #region Properties /// - public string Id - { - get { return _id; } - set { _id = value; } - } + public string Id { get; set; } = string.Empty; /// Gets or sets the scope restriction path for this phase (@from attribute, ISO Schematron 2025). - public string From { get => _from; set => _from = value; } + public string From { get; set; } = string.Empty; /// Gets or sets the enabling condition for this phase (@when attribute, ISO Schematron 2025). - public string When { get => _when; set => _when = value; } + public string When { get; set; } = string.Empty; /// Gets the collection of child elements. - public PatternCollection Patterns - { - get { return _patterns; } - } - #endregion + public PatternCollection Patterns => patterns; - #region Overridable Factory Methods /// Creates a new pattern instance. /// /// Inheritors should override this method to create instances @@ -72,23 +54,16 @@ public PatternCollection Patterns /// /// The name of the new pattern. /// The unique identifier of the new pattern. - public virtual Pattern CreatePattern(string name, string id) - { - return new Pattern(name, id); - } + public virtual Pattern CreatePattern(string name, string id) => new Pattern(name, id); /// Creates a new pattern instance. /// /// This method calls the overloaded version passing a default - /// value for the pattern's id. + /// value for the pattern's id. /// Inheritors can override this method if they want to provide /// a different default value. /// /// The name of the new pattern. - public virtual Pattern CreatePattern(string name) - { - return CreatePattern(name, String.Empty); - } - #endregion + public virtual Pattern CreatePattern(string name) => CreatePattern(name, string.Empty); } diff --git a/src/Schematron/PhaseCollection.cs b/src/Schematron/PhaseCollection.cs index 40969b4..f206898 100644 --- a/src/Schematron/PhaseCollection.cs +++ b/src/Schematron/PhaseCollection.cs @@ -3,8 +3,6 @@ namespace Schematron; /// A collection of Phase elements -/// -/// public class PhaseCollection : DictionaryBase { @@ -29,7 +27,7 @@ public void Add(Phase value) /// public void AddRange(Phase[] values) { - foreach (Phase elem in values) + foreach (var elem in values) Add(elem); } diff --git a/src/Schematron/Report.cs b/src/Schematron/Report.cs index 17b0eee..177855b 100644 --- a/src/Schematron/Report.cs +++ b/src/Schematron/Report.cs @@ -22,8 +22,6 @@ public class Report : Test /// Initializes a new instance of the class with the parameters specified. /// The XPath expression to test. /// The message to return. - internal protected Report(string test, string message) : base(test, message) - { - } + internal protected Report(string test, string message) : base(test, message) { } } diff --git a/src/Schematron/Rule.cs b/src/Schematron/Rule.cs index 64c9204..55d1b56 100644 --- a/src/Schematron/Rule.cs +++ b/src/Schematron/Rule.cs @@ -22,34 +22,24 @@ namespace Schematron; public class Rule : EvaluableExpression { // TODO: add support to child elements? - - TestCollection _asserts = new TestCollection(); - TestCollection _reports = new TestCollection(); - LetCollection _lets = new LetCollection(); - string _id = String.Empty; - bool _abstract = true; - IReadOnlyList _flag = Array.Empty(); - string _visitEach = String.Empty; + readonly TestCollection asserts = []; + readonly TestCollection reports = []; + readonly LetCollection lets = []; /// /// Creates an abstract rule, without context. /// - internal protected Rule() - { - } + internal protected Rule() { } /// Initializes a new instance of the class, with the received context. /// The rule's context to evaluate. - /// If passed a null or an , this is implicitly an abstract rule. - internal protected Rule(string context) - { - InitContext(context); - } + /// If passed a null or an , this is implicitly an abstract rule. + internal protected Rule(string context) => InitContext(context); /// Initializes the context for the rule. /// The rule's context to evaluate. /// - /// If passed a null or an , this is implicitly an abstract rule. + /// If passed a null or an , this is implicitly an abstract rule. /// /// /// Rules are evaluated through all the document (//), unless they @@ -61,7 +51,7 @@ void InitContext(string context) { if (string.IsNullOrEmpty(context)) { - _abstract = true; + IsAbstract = true; return; } @@ -71,7 +61,7 @@ void InitContext(string context) // We have to split per union (|) to add the root expression. var parts = context.Split('|'); - for (int i = 0; i < parts.Length; i++) + for (var i = 0; i < parts.Length; i++) { parts[i] = parts[i].Trim(); @@ -80,10 +70,9 @@ void InitContext(string context) } InitializeExpression(string.Join(" | ", parts)); - _abstract = false; + IsAbstract = false; } - #region Overridable Factory Methods /// Creates a new assert instance. /// /// Inheritors should override this method to create instances @@ -97,10 +86,7 @@ void InitContext(string context) /// The message to display if the assert fails. See /// . /// - public virtual Assert CreateAssert(string test, string message) - { - return new Assert(test, message); - } + public virtual Assert CreateAssert(string test, string message) => new Assert(test, message); /// Creates a new report instance. /// @@ -115,66 +101,39 @@ public virtual Assert CreateAssert(string test, string message) /// The message to display if the report succeeds. See /// . /// - public virtual Report CreateReport(string test, string message) - { - return new Report(test, message); - } - #endregion + public virtual Report CreateReport(string test, string message) => new Report(test, message); /// - public string Id - { - get { return _id; } - set { _id = value; } - } + public string Id { get; set; } = string.Empty; /// - public string Context - { - get { return base.Expression; } - set { InitContext(value); } - } + public string Context => base.Expression; /// - public bool IsAbstract - { - get { return (_abstract); } - } + public bool IsAbstract { get; private set; } = true; /// Gets the variable bindings declared in this rule (<let> elements). - public LetCollection Lets => _lets; + public LetCollection Lets => lets; /// Gets or sets the flag values declared on this rule (@flag attribute). - public IReadOnlyList Flag { get => _flag; set => _flag = value; } + public IReadOnlyList Flag { get; set; } = []; /// Gets or sets the secondary visit path (@visit-each attribute, ISO Schematron 2025). /// For each node matched by , the @visit-each expression is evaluated /// and each resulting node is tested against the rule's asserts/reports. - public string VisitEach { get => _visitEach; set => _visitEach = value; } + public string VisitEach { get; set; } = string.Empty; /// - public TestCollection Asserts - { - get { return _asserts; } - } + public TestCollection Asserts => asserts; /// - public TestCollection Reports - { - get { return _reports; } - } + public TestCollection Reports => reports; /// - public void AddAssert(string test, string message) - { - _asserts.Add(CreateAssert(test, message)); - } + public void AddAssert(string test, string message) => asserts.Add(CreateAssert(test, message)); /// - public void AddReport(string test, string message) - { - _reports.Add(CreateReport(test, message)); - } + public void AddReport(string test, string message) => reports.Add(CreateReport(test, message)); /// /// @@ -184,11 +143,11 @@ public void Extend(Rule parent) if (!parent.IsAbstract) throw new ArgumentException("The rule to extend must be abstract.", "parent"); - foreach (Assert asr in parent._asserts) - _asserts.Add(asr); + foreach (Assert asr in parent.asserts) + asserts.Add(asr); - foreach (Report rpt in parent._reports) - _reports.Add(rpt); + foreach (Report rpt in parent.reports) + reports.Add(rpt); } } diff --git a/src/Schematron/RuleCollection.cs b/src/Schematron/RuleCollection.cs index c280c22..b974f0a 100644 --- a/src/Schematron/RuleCollection.cs +++ b/src/Schematron/RuleCollection.cs @@ -1,76 +1,4 @@ -using System.Collections; - namespace Schematron; /// A collection of Rule elements. -/// -/// -public class RuleCollection : CollectionBase -{ - /// - public RuleCollection() - { - } - - /// Returns the Rule element at the specified position. - public Rule this[int index] - { - get { return (Rule)InnerList[index]; } - set { InnerList[index] = value; } - } - - /// - public int Add(Rule value) - { - return InnerList.Add(value); - } - - /// - public void AddRange(Rule[] values) - { - foreach (Rule elem in values) - Add(elem); - } - - /// - public void AddRange(RuleCollection values) - { - foreach (Rule elem in values) - Add(elem); - } - - /// - public bool Contains(Rule value) - { - return InnerList.Contains(value); - } - - /// - public void CopyTo(Rule[] array, int index) - { - InnerList.CopyTo(array, index); - } - - /// - public int IndexOf(Rule value) - { - return InnerList.IndexOf(value); - } - - /// - public void Insert(int index, Rule value) - { - InnerList.Insert(index, value); - } - - /// - public void Remove(Rule value) - { - int index = IndexOf(value); - if (index < 0) - throw (new ArgumentException("The specified object is not found in the collection")); - - RemoveAt(index); - } -} - +public class RuleCollection : List; \ No newline at end of file diff --git a/src/Schematron/Schema.cs b/src/Schematron/Schema.cs index 2b4ea8d..b0f0ae4 100644 --- a/src/Schematron/Schema.cs +++ b/src/Schematron/Schema.cs @@ -4,9 +4,14 @@ namespace Schematron; /// +/// Represents an ISO/IEC 19757-3 Schematron schema used for validating XML documents. +/// +/// This class manages schema components including phases, patterns, variable bindings (lets), +/// diagnostic messages, and parameters. It supports loading schemas from multiple sources +/// (URI, streams, readers, and XPath navigators) and provides factory methods for creating +/// phases. The class maintains namespace management for XML processing and supports both +/// the current ISO standard namespace and legacy ASCC namespace for backward compatibility. /// -/// -/// Lacks attributes defined in Schematron, but not in use currently. public class Schema { /// The ISO/IEC 19757-3 Schematron namespace (official, current standard). @@ -19,54 +24,27 @@ public class Schema public const string Namespace = LegacyNamespace; /// Returns if is a recognized Schematron namespace URI. - public static bool IsSchematronNamespace(string? uri) => - uri == IsoNamespace || uri == LegacyNamespace; - - SchemaLoader _loader; - string _title = String.Empty; - string _schematronEdition = String.Empty; - string _defaultphase = String.Empty; - bool _isLibrary = false; - PhaseCollection _phases = new PhaseCollection(); - PatternCollection _patterns = new PatternCollection(); - LetCollection _lets = new LetCollection(); - DiagnosticCollection _diagnostics = new DiagnosticCollection(); - ParamCollection _params = new ParamCollection(); - XmlNamespaceManager _ns = null!; + public static bool IsSchematronNamespace(string? uri) => uri == IsoNamespace || uri == LegacyNamespace; + + readonly LetCollection lets = []; + readonly DiagnosticCollection diagnostics = []; + readonly ParamCollection paramItems = []; /// - public Schema() - { - _loader = CreateLoader(); - } + public Schema() => Loader = CreateLoader(); /// - public Schema(string title) : this() - { - _title = title; - } + public Schema(string title) : this() => Title = title; - #region Overridable Factory Methods /// - internal protected virtual SchemaLoader CreateLoader() - { - return new SchemaLoader(this); - } + internal protected virtual SchemaLoader CreateLoader() => new(this); /// - public virtual Phase CreatePhase(string id) - { - return new Phase(id); - } + public virtual Phase CreatePhase(string id) => new(id); /// - public virtual Phase CreatePhase() - { - return new Phase(); - } - #endregion + public virtual Phase CreatePhase() => new(); - #region Overloaded Load methods /// /// Loads the schema from the specified URI. /// @@ -79,18 +57,12 @@ public void Load(string uri) /// /// Loads the schema from the reader. Closing the reader is responsibility of the caller. /// - public void Load(TextReader reader) - { - Load(new XmlTextReader(reader)); - } + public void Load(TextReader reader) => Load(new XmlTextReader(reader)); /// /// Loads the schema from the stream. Closing the stream is responsibility of the caller. /// - public void Load(Stream input) - { - Load(new XmlTextReader(input)); - } + public void Load(Stream input) => Load(new XmlTextReader(input)); /// /// Loads the schema from the reader. Closing the reader is responsibility of the caller. @@ -103,78 +75,40 @@ public void Load(XmlReader schema) } /// - public void Load(XPathNavigator schema) - { - Loader.LoadSchema(schema); - } - #endregion + public void Load(XPathNavigator schema) => Loader.LoadSchema(schema); - #region Properties /// - internal protected SchemaLoader Loader - { - get { return _loader; } - set { _loader = value; } - } + internal protected SchemaLoader Loader { get; set; } = null!; /// - public string DefaultPhase - { - get { return _defaultphase; } - set { _defaultphase = value; } - } + public string DefaultPhase { get; set; } = string.Empty; /// - public string Title - { - get { return _title; } - set { _title = value; } - } + public string Title { get; set; } = string.Empty; /// Gets or sets the Schematron edition declared by the schema's @schematronEdition attribute. /// A value of "2025" indicates ISO Schematron 4th edition. - public string SchematronEdition - { - get { return _schematronEdition; } - set { _schematronEdition = value; } - } + public string SchematronEdition { get; set; } = string.Empty; /// - public PhaseCollection Phases - { - get { return _phases; } - set { _phases = value; } - } + public PhaseCollection Phases { get; set; } = []; /// - public PatternCollection Patterns - { - get { return _patterns; } - set { _patterns = value; } - } + public PatternCollection Patterns { get; set; } = []; /// Gets the variable bindings declared at the schema level (<let> elements). - public LetCollection Lets => _lets; + public LetCollection Lets => lets; /// Gets the diagnostic elements declared in the schema (<diagnostics>/<diagnostic>). - public DiagnosticCollection Diagnostics => _diagnostics; + public DiagnosticCollection Diagnostics => diagnostics; /// Gets the parameter declarations at the schema level (<param> elements). - public ParamCollection Params => _params; + public ParamCollection Params => paramItems; /// Gets or sets a value indicating whether this schema was loaded from a <library> root element (ISO Schematron 2025). - public bool IsLibrary - { - get { return _isLibrary; } - set { _isLibrary = value; } - } + public bool IsLibrary { get; set; } /// - public XmlNamespaceManager NsManager - { - get { return _ns; } - set { _ns = value; } - } - #endregion + public XmlNamespaceManager NsManager { get; set; } = null!; } diff --git a/src/Schematron/SchemaCollection.cs b/src/Schematron/SchemaCollection.cs index 724b6ea..8abdee4 100644 --- a/src/Schematron/SchemaCollection.cs +++ b/src/Schematron/SchemaCollection.cs @@ -1,76 +1,4 @@ -using System.Collections; - namespace Schematron; /// A collection of schematron schemas. -/// -/// -public class SchemaCollection : CollectionBase -{ - /// - public SchemaCollection() - { - } - - /// Returns the Schema element at the specified position. - public Schema this[int index] - { - get { return (Schema)InnerList[index]; } - set { InnerList[index] = value; } - } - - /// - public int Add(Schema value) - { - return InnerList.Add(value); - } - - /// - public void AddRange(Schema[] values) - { - foreach (Schema elem in values) - Add(elem); - } - - /// - public void AddRange(SchemaCollection values) - { - foreach (Schema elem in values) - Add(elem); - } - - /// - public bool Contains(Schema value) - { - return InnerList.Contains(value); - } - - /// - public void CopyTo(Schema[] array, int index) - { - InnerList.CopyTo(array, index); - } - - /// - public int IndexOf(Schema value) - { - return InnerList.IndexOf(value); - } - - /// - public void Insert(int index, Schema value) - { - InnerList.Insert(index, value); - } - - /// - public void Remove(Schema value) - { - int index = IndexOf(value); - if (index < 0) - throw (new ArgumentException("The specified object is not found in the collection")); - - RemoveAt(index); - } -} - +public class SchemaCollection : List; \ No newline at end of file diff --git a/src/Schematron/SchemaLoader.cs b/src/Schematron/SchemaLoader.cs index b0475d0..45998b8 100644 --- a/src/Schematron/SchemaLoader.cs +++ b/src/Schematron/SchemaLoader.cs @@ -1,59 +1,57 @@ using System.Collections; -using System.IO; using System.Xml; using System.Xml.XPath; namespace Schematron; -/// -public class SchemaLoader +/// +/// Loads and parses Schematron schema documents from an XPathNavigator source. +/// Detects the Schematron namespace (ISO or legacy), compiles XPath expressions, +/// and populates a Schema object with phases, patterns, rules, assertions, reports, +/// and diagnostics. Supports abstract pattern instantiation with parameter substitution, +/// rule extension through abstract rules, and schema composition via extends references. +/// +public class SchemaLoader(Schema schema) { - Schema _schema; - XPathNavigator _filenav = null!; - Hashtable? _abstracts = null; + XPathNavigator filenav = null!; + Hashtable? abstracts = null; // Detected Schematron namespace and the namespace manager derived from the source document. - string _schNs = null!; - XmlNamespaceManager _mgr = null!; + string schNs = null!; + XmlNamespaceManager mgr = null!; // Instance-level XPath expressions compiled against the detected namespace. - XPathExpression _exprSchema = null!; - XPathExpression _exprEmbeddedSchema = null!; - XPathExpression _exprPhase = null!; - XPathExpression _exprPattern = null!; - XPathExpression _exprAbstractRule = null!; - XPathExpression _exprConcreteRule = null!; - XPathExpression _exprRuleExtends = null!; - XPathExpression _exprAssert = null!; - XPathExpression _exprReport = null!; - XPathExpression _exprLet = null!; - XPathExpression _exprDiagnostic = null!; - XPathExpression _exprParam = null!; - XPathExpression _exprLibrary = null!; - XPathExpression _exprRulesContainer = null!; - XPathExpression _exprGroup = null!; - - /// - public SchemaLoader(Schema schema) - { - _schema = schema; - } + XPathExpression exprSchema = null!; + XPathExpression exprEmbeddedSchema = null!; + XPathExpression exprPhase = null!; + XPathExpression exprPattern = null!; + XPathExpression exprAbstractRule = null!; + XPathExpression exprConcreteRule = null!; + XPathExpression exprRuleExtends = null!; + XPathExpression exprAssert = null!; + XPathExpression exprReport = null!; + XPathExpression exprLet = null!; + XPathExpression exprDiagnostic = null!; + XPathExpression exprParam = null!; + XPathExpression exprLibrary = null!; + XPathExpression exprRulesContainer = null!; + XPathExpression exprGroup = null!; /// /// public virtual void LoadSchema(XPathNavigator source) { - _schema.NsManager = new XmlNamespaceManager(source.NameTable); + schema.NsManager = new XmlNamespaceManager(source.NameTable); DetectAndBuildExpressions(source); - XPathNodeIterator it = source.Select(_exprSchema); + var it = source.Select(exprSchema); if (it.Count > 1) throw new BadSchemaException("There can be at most one schema element per Schematron schema."); // Always work with the whole document to look for elements. // Embedded schematron will work as well as stand-alone schemas. - _filenav = source; + filenav = source; if (it.Count == 1) { @@ -63,25 +61,23 @@ public virtual void LoadSchema(XPathNavigator source) else { // Check for root element (ISO Schematron 2025) - XPathNodeIterator libIt = source.Select(_exprLibrary); + var libIt = source.Select(exprLibrary); if (libIt.Count == 1) { libIt.MoveNext(); - _schema.IsLibrary = true; + schema.IsLibrary = true; LoadSchemaElement(libIt.Current); } else { // Load child elements from the appinfo element if it exists. - LoadSchemaElements(source.Select(_exprEmbeddedSchema)); + LoadSchemaElements(source.Select(exprEmbeddedSchema)); } } - #region Loading process start RetrieveAbstractRules(); LoadPhases(); LoadPatterns(); - #endregion } /// @@ -90,27 +86,27 @@ public virtual void LoadSchema(XPathNavigator source) /// void DetectAndBuildExpressions(XPathNavigator source) { - _schNs = DetectSchematronNamespace(source); - - _mgr = new XmlNamespaceManager(source.NameTable); - _mgr.AddNamespace("sch", _schNs); - _mgr.AddNamespace("xsd", System.Xml.Schema.XmlSchema.Namespace); - - _exprSchema = Compile("//sch:schema"); - _exprEmbeddedSchema = Compile("xsd:schema/xsd:annotation/xsd:appinfo/*"); - _exprPhase = Compile("descendant-or-self::sch:phase"); - _exprPattern = Compile("//sch:pattern"); - _exprAbstractRule = Compile("//sch:rule[@abstract=\"true\"]"); - _exprConcreteRule = Compile("descendant-or-self::sch:rule[not(@abstract) or @abstract=\"false\"]"); - _exprRuleExtends = Compile("descendant-or-self::sch:extends"); - _exprAssert = Compile("descendant-or-self::sch:assert"); - _exprReport = Compile("descendant-or-self::sch:report"); - _exprLet = Compile("sch:let"); - _exprDiagnostic = Compile("sch:diagnostics/sch:diagnostic"); - _exprParam = Compile("sch:param"); - _exprLibrary = Compile("//sch:library"); - _exprRulesContainer = Compile("//sch:rules/sch:rule"); - _exprGroup = Compile("//sch:group"); + schNs = DetectSchematronNamespace(source); + + mgr = new XmlNamespaceManager(source.NameTable); + mgr.AddNamespace("sch", schNs); + mgr.AddNamespace("xsd", System.Xml.Schema.XmlSchema.Namespace); + + exprSchema = Compile("//sch:schema"); + exprEmbeddedSchema = Compile("xsd:schema/xsd:annotation/xsd:appinfo/*"); + exprPhase = Compile("descendant-or-self::sch:phase"); + exprPattern = Compile("//sch:pattern"); + exprAbstractRule = Compile("//sch:rule[@abstract=\"true\"]"); + exprConcreteRule = Compile("descendant-or-self::sch:rule[not(@abstract) or @abstract=\"false\"]"); + exprRuleExtends = Compile("descendant-or-self::sch:extends"); + exprAssert = Compile("descendant-or-self::sch:assert"); + exprReport = Compile("descendant-or-self::sch:report"); + exprLet = Compile("sch:let"); + exprDiagnostic = Compile("sch:diagnostics/sch:diagnostic"); + exprParam = Compile("sch:param"); + exprLibrary = Compile("//sch:library"); + exprRulesContainer = Compile("//sch:rules/sch:rule"); + exprGroup = Compile("//sch:group"); } /// @@ -143,22 +139,22 @@ static string DetectSchematronNamespace(XPathNavigator source) XPathExpression Compile(string xpath) { var expr = Config.DefaultNavigator.Compile(xpath); - expr.SetContext(_mgr); + expr.SetContext(mgr); return expr; } void LoadSchemaElement(XPathNavigator context) { - string phase = context.GetAttribute("defaultPhase", String.Empty); - if (phase != String.Empty) - _schema.DefaultPhase = phase; + var phase = context.GetAttribute("defaultPhase", string.Empty); + if (phase != string.Empty) + schema.DefaultPhase = phase; - string edition = context.GetAttribute("schematronEdition", String.Empty); - if (edition != String.Empty) - _schema.SchematronEdition = edition; + var edition = context.GetAttribute("schematronEdition", string.Empty); + if (edition != string.Empty) + schema.SchematronEdition = edition; LoadSchemaElements(context.SelectChildren(XPathNodeType.Element)); - LoadLets(_schema.Lets, context); + LoadLets(schema.Lets, context); LoadDiagnostics(context); LoadSchemaParams(context); LoadExtendsHref(context); @@ -168,17 +164,17 @@ void LoadSchemaElements(XPathNodeIterator children) { while (children.MoveNext()) { - if (children.Current.NamespaceURI == _schNs) + if (children.Current.NamespaceURI == schNs) { if (children.Current.LocalName == "title") { - _schema.Title = children.Current.Value; + schema.Title = children.Current.Value; } else if (children.Current.LocalName == "ns") { - _schema.NsManager.AddNamespace( - children.Current.GetAttribute("prefix", String.Empty), - children.Current.GetAttribute("uri", String.Empty)); + schema.NsManager.AddNamespace( + children.Current.GetAttribute("prefix", string.Empty), + children.Current.GetAttribute("uri", string.Empty)); } } } @@ -186,86 +182,86 @@ void LoadSchemaElements(XPathNodeIterator children) void RetrieveAbstractRules() { - _filenav.MoveToRoot(); - XPathNodeIterator it = _filenav.Select(_exprAbstractRule); + filenav.MoveToRoot(); + var it = filenav.Select(exprAbstractRule); // Also check for rules inside containers (implicitly abstract) - _filenav.MoveToRoot(); - XPathNodeIterator rulesContainerIt = _filenav.Select(_exprRulesContainer); + filenav.MoveToRoot(); + var rulesContainerIt = filenav.Select(exprRulesContainer); if (it.Count == 0 && rulesContainerIt.Count == 0) return; - _abstracts = new Hashtable(it.Count + rulesContainerIt.Count); + abstracts = new(it.Count + rulesContainerIt.Count); // Dummy pattern to use for rule creation purposes. // TODO: is there a better factory method implementation? - Pattern pt = _schema.CreatePhase(String.Empty).CreatePattern(String.Empty); + var pt = schema.CreatePhase(string.Empty).CreatePattern(string.Empty); while (it.MoveNext()) { - Rule rule = pt.CreateRule(); - rule.SetContext(_schema.NsManager); - rule.Id = it.Current.GetAttribute("id", String.Empty); + var rule = pt.CreateRule(); + rule.SetContext(schema.NsManager); + rule.Id = it.Current.GetAttribute("id", string.Empty); LoadAsserts(rule, it.Current); LoadReports(rule, it.Current); - _abstracts.Add(rule.Id, rule); + abstracts.Add(rule.Id, rule); } // Also collect rules inside containers (implicitly abstract, even without @abstract="true") while (rulesContainerIt.MoveNext()) { - string ruleId = rulesContainerIt.Current.GetAttribute("id", String.Empty); + var ruleId = rulesContainerIt.Current.GetAttribute("id", string.Empty); if (ruleId.Length == 0) continue; - if (_abstracts.ContainsKey(ruleId)) continue; + if (abstracts.ContainsKey(ruleId)) continue; - Rule rule = pt.CreateRule(); - rule.SetContext(_schema.NsManager); + var rule = pt.CreateRule(); + rule.SetContext(schema.NsManager); rule.Id = ruleId; LoadAsserts(rule, rulesContainerIt.Current); LoadReports(rule, rulesContainerIt.Current); - _abstracts.Add(rule.Id, rule); + abstracts.Add(rule.Id, rule); } } void LoadPhases() { - _filenav.MoveToRoot(); - XPathNodeIterator phases = _filenav.Select(_exprPhase); + filenav.MoveToRoot(); + var phases = filenav.Select(exprPhase); if (phases.Count == 0) return; while (phases.MoveNext()) { - Phase ph = _schema.CreatePhase(phases.Current.GetAttribute("id", String.Empty)); - ph.From = phases.Current.GetAttribute("from", String.Empty); - ph.When = phases.Current.GetAttribute("when", String.Empty); - _schema.Phases.Add(ph); + var ph = schema.CreatePhase(phases.Current.GetAttribute("id", string.Empty)); + ph.From = phases.Current.GetAttribute("from", string.Empty); + ph.When = phases.Current.GetAttribute("when", string.Empty); + schema.Phases.Add(ph); } } void LoadPatterns() { - _filenav.MoveToRoot(); - XPathNodeIterator patterns = _filenav.Select(_exprPattern); - _filenav.MoveToRoot(); - XPathNodeIterator groups = _filenav.Select(_exprGroup); + filenav.MoveToRoot(); + var patterns = filenav.Select(exprPattern); + filenav.MoveToRoot(); + var groups = filenav.Select(exprGroup); if (patterns.Count == 0 && groups.Count == 0) return; // A special #ALL phase which contains all the patterns in the schema. - Phase phase = _schema.CreatePhase(Phase.All); + var phase = schema.CreatePhase(Phase.All); while (patterns.MoveNext()) { // Skip abstract patterns — they are templates; only instantiated via @is-a. - bool isAbstract = patterns.Current.GetAttribute("abstract", String.Empty) == "true"; + var isAbstract = patterns.Current.GetAttribute("abstract", string.Empty) == "true"; if (isAbstract) continue; - Pattern pt = phase.CreatePattern(patterns.Current.GetAttribute("name", String.Empty), - patterns.Current.GetAttribute("id", String.Empty)); + var pt = phase.CreatePattern(patterns.Current.GetAttribute("name", string.Empty), + patterns.Current.GetAttribute("id", string.Empty)); LoadLets(pt.Lets, patterns.Current); - string isA = patterns.Current.GetAttribute("is-a", String.Empty); + var isA = patterns.Current.GetAttribute("is-a", string.Empty); if (isA.Length > 0) { // Instantiate abstract pattern: collect param values, load rules from template. @@ -277,22 +273,22 @@ void LoadPatterns() LoadRules(pt, patterns.Current); } - _schema.Patterns.Add(pt); + schema.Patterns.Add(pt); phase.Patterns.Add(pt); - if (pt.Id != String.Empty) + if (pt.Id != string.Empty) { // Select the phases in which this pattern is active, and add it // to its collection of patterns. // TODO: try to precompile this. Is it possible? - XPathExpression expr = Config.DefaultNavigator.Compile( + var expr = Config.DefaultNavigator.Compile( "//sch:phase[sch:active/@pattern=\"" + pt.Id + "\"]/@id"); - expr.SetContext(_mgr); - XPathNodeIterator phases = _filenav.Select(expr); + expr.SetContext(mgr); + var phases = filenav.Select(expr); while (phases.MoveNext()) { - _schema.Phases[phases.Current.Value].Patterns.Add(pt); + schema.Phases[phases.Current.Value].Patterns.Add(pt); } } } @@ -301,38 +297,38 @@ void LoadPatterns() while (groups.MoveNext()) { var grp = new Group( - groups.Current.GetAttribute("name", String.Empty), - groups.Current.GetAttribute("id", String.Empty)); + groups.Current.GetAttribute("name", string.Empty), + groups.Current.GetAttribute("id", string.Empty)); LoadLets(grp.Lets, groups.Current); LoadRules(grp, groups.Current); - _schema.Patterns.Add(grp); + schema.Patterns.Add(grp); phase.Patterns.Add(grp); - if (grp.Id != String.Empty) + if (grp.Id != string.Empty) { - XPathExpression expr = Config.DefaultNavigator.Compile( + var expr = Config.DefaultNavigator.Compile( "//sch:phase[sch:active/@pattern=\"" + grp.Id + "\"]/@id"); - expr.SetContext(_mgr); - XPathNodeIterator phases = _filenav.Select(expr); + expr.SetContext(mgr); + var phases = filenav.Select(expr); while (phases.MoveNext()) - _schema.Phases[phases.Current.Value].Patterns.Add(grp); + schema.Phases[phases.Current.Value].Patterns.Add(grp); } } - _schema.Phases.Add(phase); + schema.Phases.Add(phase); } static Dictionary LoadParams(XPathNavigator context) { var d = new Dictionary(StringComparer.Ordinal); - XPathNodeIterator it = context.SelectChildren(XPathNodeType.Element); + var it = context.SelectChildren(XPathNodeType.Element); while (it.MoveNext()) { if (it.Current.LocalName == "param") { - string name = it.Current.GetAttribute("name", String.Empty); - string value = it.Current.GetAttribute("value", String.Empty); + var name = it.Current.GetAttribute("name", string.Empty); + var value = it.Current.GetAttribute("value", string.Empty); if (name.Length > 0) d[name] = value; } @@ -343,30 +339,30 @@ static Dictionary LoadParams(XPathNavigator context) void LoadRulesFromAbstractPattern(Pattern target, string abstractId, Dictionary paramValues) { // Find the abstract pattern node in the document. - XPathExpression expr = Config.DefaultNavigator.Compile( + var expr = Config.DefaultNavigator.Compile( "//sch:pattern[@abstract=\"true\" and @id=\"" + abstractId + "\"]"); - expr.SetContext(_mgr); - _filenav.MoveToRoot(); - XPathNodeIterator it = _filenav.Select(expr); + expr.SetContext(mgr); + filenav.MoveToRoot(); + var it = filenav.Select(expr); if (!it.MoveNext()) return; - XPathNodeIterator rules = it.Current.Select(_exprConcreteRule); + var rules = it.Current.Select(exprConcreteRule); while (rules.MoveNext()) { - string ruleContext = SubstituteParams( - rules.Current.GetAttribute("context", String.Empty), paramValues); + var ruleContext = SubstituteParams( + rules.Current.GetAttribute("context", string.Empty), paramValues); - Rule rule = target.CreateRule(ruleContext); - rule.Id = rules.Current.GetAttribute("id", String.Empty); - rule.SetContext(_schema.NsManager); + var rule = target.CreateRule(ruleContext); + rule.Id = rules.Current.GetAttribute("id", string.Empty); + rule.SetContext(schema.NsManager); LoadLets(rule.Lets, rules.Current); - string ruleFlag = rules.Current.GetAttribute("flag", String.Empty); + var ruleFlag = rules.Current.GetAttribute("flag", string.Empty); rule.Flag = string.IsNullOrWhiteSpace(ruleFlag) - ? Array.Empty() + ? [] : ruleFlag.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); - rule.VisitEach = rules.Current.GetAttribute("visit-each", String.Empty); + rule.VisitEach = rules.Current.GetAttribute("visit-each", string.Empty); // Load asserts/reports with parameter substitution applied to test expressions. LoadAssertsWithSubstitution(rule, rules.Current, paramValues); @@ -385,25 +381,25 @@ static string SubstituteParams(string text, Dictionary paramValu void LoadRules(Pattern pattern, XPathNavigator context) { - XPathNodeIterator rules = context.Select(_exprConcreteRule); + var rules = context.Select(exprConcreteRule); if (rules.Count == 0) return; while (rules.MoveNext()) { - Rule rule = pattern.CreateRule(rules.Current.GetAttribute("context", String.Empty)); - rule.Id = rules.Current.GetAttribute("id", String.Empty); - rule.SetContext(_schema.NsManager); + var rule = pattern.CreateRule(rules.Current.GetAttribute("context", string.Empty)); + rule.Id = rules.Current.GetAttribute("id", string.Empty); + rule.SetContext(schema.NsManager); LoadLets(rule.Lets, rules.Current); LoadExtends(rule, rules.Current); LoadAsserts(rule, rules.Current); LoadReports(rule, rules.Current); - string ruleFlag = rules.Current.GetAttribute("flag", String.Empty); + var ruleFlag = rules.Current.GetAttribute("flag", string.Empty); rule.Flag = string.IsNullOrWhiteSpace(ruleFlag) - ? Array.Empty() + ? [] : ruleFlag.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); - rule.VisitEach = rules.Current.GetAttribute("visit-each", String.Empty); + rule.VisitEach = rules.Current.GetAttribute("visit-each", string.Empty); pattern.Rules.Add(rule); } @@ -411,16 +407,14 @@ void LoadRules(Pattern pattern, XPathNavigator context) void LoadLets(LetCollection lets, XPathNavigator context) { - XPathNodeIterator it = context.Select(_exprLet); + var it = context.Select(exprLet); while (it.MoveNext()) { - var let = new Let - { - Name = it.Current.GetAttribute("name", String.Empty), - Value = it.Current.GetAttribute("value", String.Empty), - As = it.Current.GetAttribute("as", String.Empty) is { Length: > 0 } a ? a : null, - }; - if (let.Value?.Length == 0) let.Value = null; + var let = new Let( + it.Current.GetAttribute("name", string.Empty), + it.Current.GetAttribute("value", string.Empty) is { Length: > 0 } value ? value : null, + it.Current.GetAttribute("as", string.Empty) is { Length: > 0 } a ? a : null); + if (!lets.Contains(let.Name)) lets.Add(let); } @@ -428,14 +422,14 @@ void LoadLets(LetCollection lets, XPathNavigator context) void LoadExtends(Rule rule, XPathNavigator context) { - XPathNodeIterator extends = context.Select(_exprRuleExtends); + var extends = context.Select(exprRuleExtends); if (extends.Count == 0) return; while (extends.MoveNext()) { - string ruleName = extends.Current.GetAttribute("rule", String.Empty); - if (_abstracts != null && _abstracts.ContainsKey(ruleName)) - rule.Extend((Rule)_abstracts[ruleName]!); + var ruleName = extends.Current.GetAttribute("rule", string.Empty); + if (abstracts != null && abstracts.ContainsKey(ruleName)) + rule.Extend((Rule)abstracts[ruleName]!); else throw new BadSchemaException("The abstract rule with id=\"" + ruleName + "\" is used but not defined."); } @@ -443,18 +437,18 @@ void LoadExtends(Rule rule, XPathNavigator context) void LoadAsserts(Rule rule, XPathNavigator context) { - XPathNodeIterator asserts = context.Select(_exprAssert); + var asserts = context.Select(exprAssert); if (asserts.Count == 0) return; while (asserts.MoveNext()) { - string testExpr = asserts.Current.GetAttribute("test", String.Empty); - string message = asserts.Current is IHasXmlNode node + var testExpr = asserts.Current.GetAttribute("test", string.Empty); + var message = asserts.Current is IHasXmlNode node ? node.GetNode().InnerXml : asserts.Current.Value; - Assert asr = rule.CreateAssert(testExpr, message); - asr.SetContext(_schema.NsManager); + var asr = rule.CreateAssert(testExpr, message); + asr.SetContext(schema.NsManager); ReadTestAttributes(asr, asserts.Current); rule.Asserts.Add(asr); } @@ -462,18 +456,18 @@ void LoadAsserts(Rule rule, XPathNavigator context) void LoadReports(Rule rule, XPathNavigator context) { - XPathNodeIterator reports = context.Select(_exprReport); + var reports = context.Select(exprReport); if (reports.Count == 0) return; while (reports.MoveNext()) { - string testExpr = reports.Current.GetAttribute("test", String.Empty); - string message = reports.Current is IHasXmlNode node + var testExpr = reports.Current.GetAttribute("test", string.Empty); + var message = reports.Current is IHasXmlNode node ? node.GetNode().InnerXml : reports.Current.Value; - Report rpt = rule.CreateReport(testExpr, message); - rpt.SetContext(_schema.NsManager); + var rpt = rule.CreateReport(testExpr, message); + rpt.SetContext(schema.NsManager); ReadTestAttributes(rpt, reports.Current); rule.Reports.Add(rpt); } @@ -481,35 +475,36 @@ void LoadReports(Rule rule, XPathNavigator context) void LoadDiagnostics(XPathNavigator context) { - XPathNodeIterator it = context.Select(_exprDiagnostic); + var it = context.Select(exprDiagnostic); while (it.MoveNext()) { - string id = it.Current.GetAttribute("id", String.Empty); - if (id.Length == 0) continue; - string msg = it.Current is IHasXmlNode node + if (it.Current.GetAttribute("id", string.Empty) is not { Length: > 0 } id) + continue; + + var msg = it.Current is IHasXmlNode node ? node.GetNode().InnerXml : it.Current.Value; - if (!_schema.Diagnostics.Contains(id)) - _schema.Diagnostics.Add(new Diagnostic { Id = id, Message = msg }); + if (!schema.Diagnostics.Contains(id)) + schema.Diagnostics.Add(new Diagnostic(id, msg)); } } void LoadAssertsWithSubstitution(Rule rule, XPathNavigator context, Dictionary paramValues) { - XPathNodeIterator asserts = context.Select(_exprAssert); + var asserts = context.Select(exprAssert); if (asserts.Count == 0) return; while (asserts.MoveNext()) { - string testExpr = SubstituteParams( - asserts.Current.GetAttribute("test", String.Empty), paramValues); - string message = asserts.Current is IHasXmlNode node + var testExpr = SubstituteParams( + asserts.Current.GetAttribute("test", string.Empty), paramValues); + var message = asserts.Current is IHasXmlNode node ? node.GetNode().InnerXml : asserts.Current.Value; message = SubstituteParams(message, paramValues); - Assert asr = rule.CreateAssert(testExpr, message); - asr.SetContext(_schema.NsManager); + var asr = rule.CreateAssert(testExpr, message); + asr.SetContext(schema.NsManager); ReadTestAttributes(asr, asserts.Current); rule.Asserts.Add(asr); } @@ -517,20 +512,20 @@ void LoadAssertsWithSubstitution(Rule rule, XPathNavigator context, Dictionary paramValues) { - XPathNodeIterator reports = context.Select(_exprReport); - if (reports.Count == 0) return; + if (context.Select(exprReport) is not { Count: > 0 } reports) + return; while (reports.MoveNext()) { - string testExpr = SubstituteParams( - reports.Current.GetAttribute("test", String.Empty), paramValues); - string message = reports.Current is IHasXmlNode node + var testExpr = SubstituteParams( + reports.Current.GetAttribute("test", string.Empty), paramValues); + var message = reports.Current is IHasXmlNode node ? node.GetNode().InnerXml : reports.Current.Value; message = SubstituteParams(message, paramValues); - Report rpt = rule.CreateReport(testExpr, message); - rpt.SetContext(_schema.NsManager); + var rpt = rule.CreateReport(testExpr, message); + rpt.SetContext(schema.NsManager); ReadTestAttributes(rpt, reports.Current); rule.Reports.Add(rpt); } @@ -538,36 +533,37 @@ void LoadReportsWithSubstitution(Rule rule, XPathNavigator context, Dictionary() + ? [] : flag.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); - string diagnostics = nav.GetAttribute("diagnostics", String.Empty); + var diagnostics = nav.GetAttribute("diagnostics", string.Empty); test.DiagnosticRefs = string.IsNullOrWhiteSpace(diagnostics) - ? Array.Empty() + ? [] : diagnostics.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); } void LoadSchemaParams(XPathNavigator context) { - XPathNodeIterator it = context.SelectChildren(XPathNodeType.Element); + var it = context.SelectChildren(XPathNodeType.Element); while (it.MoveNext()) { if (it.Current.LocalName == "param" && Schema.IsSchematronNamespace(it.Current.NamespaceURI)) { - string name = it.Current.GetAttribute("name", String.Empty); - string value = it.Current.GetAttribute("value", String.Empty); - if (name.Length > 0 && !_schema.Params.Contains(name)) + var name = it.Current.GetAttribute("name", string.Empty); + var value = it.Current.GetAttribute("value", string.Empty); + + if (name.Length > 0 && !schema.Params.Contains(name)) { - _schema.Params.Add(new Param { Name = name, Value = value }); + schema.Params.Add(new Param { Name = name, Value = value }); // Also expose as a schema-level let so they're available as variables - if (!_schema.Lets.Contains(name)) - _schema.Lets.Add(new Let { Name = name, Value = value }); + if (!schema.Lets.Contains(name)) + schema.Lets.Add(new Let(name, value is { Length: > 0 } ? value : null)); } } } @@ -575,23 +571,22 @@ void LoadSchemaParams(XPathNavigator context) void LoadExtendsHref(XPathNavigator context) { - XPathNodeIterator children = context.SelectChildren(XPathNodeType.Element); + var children = context.SelectChildren(XPathNodeType.Element); while (children.MoveNext()) { if (children.Current.LocalName != "extends") continue; if (!Schema.IsSchematronNamespace(children.Current.NamespaceURI)) continue; - string href = children.Current.GetAttribute("href", String.Empty); + var href = children.Current.GetAttribute("href", string.Empty); if (string.IsNullOrEmpty(href)) continue; // Resolve the href relative to the schema's base URI string resolvedPath; - string baseUri = context.BaseURI; + var baseUri = context.BaseURI; if (!string.IsNullOrEmpty(baseUri)) { try { - Uri resolved = new Uri(new Uri(baseUri), href); - resolvedPath = resolved.LocalPath; + resolvedPath = new Uri(new Uri(baseUri), href).LocalPath; } catch { @@ -611,14 +606,14 @@ void LoadExtendsHref(XPathNavigator context) extSchema.Load(resolvedPath); // Merge diagnostics - foreach (Diagnostic d in extSchema.Diagnostics) - if (!_schema.Diagnostics.Contains(d.Id)) - _schema.Diagnostics.Add(d); + foreach (var d in extSchema.Diagnostics) + if (!schema.Diagnostics.Contains(d.Id)) + schema.Diagnostics.Add(d); // Merge schema-level lets - foreach (Let let in extSchema.Lets) - if (!_schema.Lets.Contains(let.Name)) - _schema.Lets.Add(let); + foreach (var let in extSchema.Lets) + if (!schema.Lets.Contains(let.Name)) + schema.Lets.Add(let); } catch { /* skip extends that can't be loaded */ } } diff --git a/src/Schematron/Schematron.csproj b/src/Schematron/Schematron.csproj index 3bf411f..8bc013d 100644 --- a/src/Schematron/Schematron.csproj +++ b/src/Schematron/Schematron.csproj @@ -6,10 +6,18 @@ netstandard2.0 true Schematron.NET + Latest - + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Schematron/Schematron.xml b/src/Schematron/Schematron.xml deleted file mode 100644 index cf72004..0000000 --- a/src/Schematron/Schematron.xml +++ /dev/null @@ -1,1969 +0,0 @@ - - - - NMatrix.Schematron - - - - - Represents an assert element of the Schematron schema. - - - As stated in the , this is - the lowest element in a Schematron schema. This element contains the expression - to execute in the context of its parent . - If the results of the execution of the expression are False, the - assert fails and the correponding message will be displayed. - - Constructor is not public. To programatically create an instance of this - class use the factory method. - - - - - - - - Base class for testing units of Schematron, such as Assert or Report elements. - - - - - Base class for elements that can be evaluated by an XPath expression. - - - This class performs the expression compilation, and provides - access to the context through two methods. - - - - - - - Cache the return type to avoid cloning the expression. - - - - Initializes a new instance of the element with the expression specified. - The expression to evaluate. - - - Initializes a new instance of the element. - - - Reinitializes the element with a new expression, - after the class has already been constructed - The expression to evaluate. - - - Returns the manager in use to resolve expression namespaces. - - - Sets the manager to use to resolve expression namespaces. - - - Contains the compiled version of the expression. - - A clone of the expression is always returned, because the compiled - expression is not thread-safe for evaluation. - - - - Contains the string version of the expression. - - - Contains the string version of the expression. - - - - - - - - - - - - - - - - - - - - - - - - - - Constructs a new Assert object. - XPath expression to test. - Message to display if the assert fails. - - - - Represents the an error in the Schematron schema. - - - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The error message that explains the reason for the exception. - - - - Initializes a new instance of the class. - - Info - Context - - - - Initializes a new instance of the class. - - The error message that explains the reason for the exception. - The exception that is the cause of the current exception. - - - - This class keeps static precompiled expressions used - in the Schematron schema loading and validation processes. - - - All expressions are compiled against the - object. All the objects are initialized with - the for schematron and XML Schema - namespaces resolution. - - - - - - - Provides global settings for Schematron validation. - - - This class is public to allow inheritors of Schematron elements - to use these global settings. - - - - - - - Initializes global settings. - - - - - Force all static constructors in the library. - - - - - The default object to use to format messages from validation. - - - - - A default empty navigator used to pre-compile XPath expressions. - - - Compiling doesn't involve any namespace, - name table or other specific processing. It's only a parsing procedure that - builds the abstract syntax tree for later evaluation. So we can safely - use an empty to compile them against. - - - expr = Config.DefaultNavigator.Compile("//sch:pattern"); - other code; - - - - - - - - - Manager to use when executing expressions that validate or - load Schematron and Embedded Schematron schemas. - - - - - A cached schema in Schematron format to validate schematron schemas. - - This is the version for standalone schemas. - - - - A cached schema in Schematron format to validate schematron schemas. - - This is the version for embedded schemas. - - - - A unique identifier to use for internal keys. - - - - - Strategy class for matching and keeping references to nodes in an xml document. - - - When an is created from an , - it implements the interface, which is used to gain - access to the underlying node. - - - - - - - Defines the common interface used by the different node-matching strategies. - - - As different can exist, and even be developed - in the future, we have to take into account that the data store can change. - So in order to be efficient at keeping nodes matched so far, to satisfy the - , we provide a common interface and an - implementation optimized for specific stores. - - Each navigator implementation typically provides an interface to let - applications get access to the underlying store, such as the - or interfaces, implemented in navigators create by - or classes. - - - - - - - Checks if an specific node has already been matched. - The node to check. - - - Adds a node to the list of nodes matched so far. - The node to add. - - - Clears the list of matched nodes. - - - - We use an optimized collection for saving the hash codes. - - - - Initializes an instance of the class. - - - See . - - - See . - - - See . - - - - Base class for Schematron evaluation contexts. - - - The schematron elements don't provide evaluation code. They just - represent the elements in the . - - For evaluation purposes, this class provides the iteration and execution - process, accumulates results, moves the cursor, etc. Here is where the - actual evaluation takes place. This way we isolate the schema design - from the different execution models we can use, for example, - (we may add asynchonous execution later). - - - The validator classes provided use instances of this strategy object to - validate documents provided by the user. - - - - - - - - Keeps a list of nodes already matched. - - - When the property is set, the appropriate - strategy for matching nodes is initialized, depending on the specific - implementation of the in use. - - - - Creates the evaluation context - - - - Starts the evaluation process. - - - When the process is finished, the results are placed - in the property. - - - - - Resets the state of the current context. - - - By default, it clears the and sets to false. - - - - Gets or sets the class to use to format messages. - - This object is initialized to the instance. - Usually, it will be changed based on parameters passed to the validator class, - or exposed directly by it. - - - - Gets or sets the messages generated by the validation process. - - Specific implementations of this class read/write this property - while they accumulate validation messages. - - - - Indicates if errors were found during the current evaluation. - - - Gets or sets the specific validation phase to run. - - Schematron supports the concept of phases, where different sets of - patterns can be executed at different times. This phase is initialized - to , which will mean all patterns are run. - - - - Gets or sets the schema to use for the validation. - - - Gets or sets the document to validate. - - When this property is set, the appropriate - strategy is picked, to perform optimum for various navigator implementations. - - - - - Provides a simple failure message, without any details of specific validation errors. - - - - - Look at documentation. - - - - - Interface for custom formatters, which are used to generate - output from schema validation. - - - Provides formatting methods for Schematron specific elements and - for validation through the . - Custom formatters implement methods to provide message formatting. -

- Am abstract base implementation is provided in to ease - the extending process. -

-
-
- - - Provides formatting of both and elements. - - - Implementations may use the received context to add details to the output message. - The contains messages accumulated so far in the validation process. - - The current navigator node where the test failed. - The message to output. - The or element - which failed. - - - - Provides formatting for a element. - - - Implementations may use the received context to add details to the output message. - The contains messages accumulated so far in the validation process. - - The navigator where the inner or - elements failed. - The message to output. - The element containing failed - or elements. - - - - Provides formatting for a element. - - - Implementations may use the received context to add details to the output message. - The contains messages accumulated so far in the validation process. - - The source document navigator where evaluation took place. - The message to output. - The element containing failed elements. - - - - Provides formatting for a element. - - - Implementations may use the received context to add details to the output message. - The contains messages accumulated so far in the validation process. - - The source document navigator where evaluation took place. - The message to output. - The being evaluated. - - - - Provides formatting for a element. - - - Implementations may use the received context to add details to the output message. - The contains messages accumulated so far in the validation process. - - The source document navigator where evaluation took place. - The message to output. - The being evaluated. - - - - Provides formatting for a element being validated - through a . - - - Usually will output schema-level formatting for XmlSchema validation. Recall that - multiple schemas may have been configured with the reader and validated simultaneously. - The contains messages accumulated so far in the validation process. - - The message to output. - The reader in use to validate the schema. - - - - Formats the output of XmlSchema validation. - - - The contains messages accumulated so far in the validation process. - - The message to output. - The argument received by the handler - during XmlSchema validation. - - - - Enclosing message for all schemas being validated. - - - Usually will add any enclosing message to the results of the global Xml validation. - The contains messages accumulated so far in the validation process. - - The message to output. - The collection of schemas in use for validation. - - - - Enclosing message for all schemas being validated. - - - Usually will add any enclosing message to the results of the global Schematron validation. - The contains messages accumulated so far in the validation process. - - The message to output. - The collection of schemas in use for validation. - - - - Formats the whole message built so far. - - - Usually will perform any last-minute formatting of the whole message before - being returned by the calling application. For example, the - uses this method to enclose the whole message in an <output> element. - The contains messages accumulated so far in the validation process. - - The message to output. - - - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - - - - Look at documentation. - - - - - - - - Returns the full path to the context node. Clone the navigator to avoid loosing positioning. - - - - - Returns the full path to the context node. Clone the navigator to avoid loosing positioning. - - - Cloning is not performed inside this method because it is called recursively. - Keeping positioning is only relevant to the calling procedure, not subsequent - recursive calls. This way we avoid creating unnecessary objects. - - - - - Returns line positioning information if supported by the XPathNavigator implementation. - - - - - Returns abreviated node information, including attribute values. - - - - - Returns abreviated node information, including attribute values. - - - The namespaces param is optionally filled in . - - - - - Outputs the xmlns declaration for each namespace found in the parameter. - - - - - Allows to match the string stating the node position from System.Xml error messages. - - - This regular expression is used to remove the node position from the validation error - message, to maintain consistency with schematron messages. - - - - - Returns a decoded string, with spaces trimmed. - - - - - Provides a complete log of validation errors in text format. - - - - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Provides a simplified log of errors. - - - Similar output as , but doesn't provide - node position in file and namespace summary text. - - - - - - - - Look at documentation. - - - - - Provides an Xml output from validation. - - - - - Namespace of generated output. - - - - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Look at documentation. - - - - - Strategy class for matching and keeping references to nodes in - an unknown implementation of . - - - This implementation uses the standard - to know if a navigator has already been matched. This is not optimum because - a complete traversal of nodes matched so far has to be performed, but it will - work with all implementations of . - - - - - - - Uses a simple arraylist to keep the navigators. - - - - Initializes an instance of the class. - - - See . - - - See . - - - See . - - - - An optimized collection for holding values. - - - This class was generated automatically by TextBox. - Original author is Shawn Van Ness. The code contains comments by - him, together with a summary of features and acknowledgments :). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The exception that is thrown when an invalid XPath expression is used. - - - - - - Initializes a new instance of the exception class. - - - - Initializes an instance of the class with a specified error message. - - The error message that explains the reason for the exception. - - - - For serialization purposes. - - Info - Context - - - - Initializes an instance of the class with a specified error message - and a reference to the inner exception that is the cause for this exception. - - The error message that explains the reason for the exception. - The exception that is the cause of the current exception. - - - - Return type to use as the validation result. - - - - - Use an for validation and return type. - - - - - Use an for validation and return type. - - - - - Use the default type, equal to , for validation and return type. - - - - - Represents the valid output formats. - - - Items will be added to the list to reflect the - additional implementations we - will develop. - - - - - - Use the class. - - - Use the class. - - - Use the class. - - - Use the default formatter, which is the . - - - Use the class. - - - - A Pattern element, containing elements. - - - Constructor is not public. To programatically create an instance of this - class use the factory method. - - - - - - Initializes the pattern with the name specified. - The name of the new pattern. - - - Initializes the pattern with the name and id specified. - The name of the new pattern. - The id of the new pattern. - - - Creates a new rule instance. - - Inheritors should override this method to create instances - of their own rule implementations. - - - - Creates a new rule instance with the context specified. - - Inheritors should override this method to create instances - of their own rule implementations. - - - The context for the new rule. - - - - Gets or sets the pattern's name. - - - Gets or sets the pattern's Id. - - This property is important because it is used by the - to activate certain patterns. - - - - Gets the rules contained in this pattern. - - - A collection of Pattern elements. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Returns the Pattern element at the specified position. - - - - A Phase element, containing the active elements. - - - The allows a certaing degree of workflow - through the use of phases. A document can have several states, and - therefore different sets of rules should be checked against it. - - This element allows execution of a set of 'active' patterns. - - - Constructor is not public. To programatically create an instance of this - class use the factory method. - - - - - - - - The identifier to check for All phases. - - Causes all the patterns in a schema to be checked, - irrespective of the phases where they are activated. - - - Initializes a new instance of the class with the specified Id. - The Id of the new phase. - - - Initializes a new instance of the class. - - - Creates a new pattern instance. - - Inheritors should override this method to create instances - of their own pattern implementations. - - The name of the new pattern. - The unique identifier of the new pattern. - - - Creates a new pattern instance. - - This method calls the overloaded version passing a default - value for the pattern's id. - Inheritors can override this method if they want to provide - a different default value. - - The name of the new pattern. - - - Gets or sets the phase identifier. - - - Gets the collection of child elements. - - - A collection of Phase elements - - - - - - - - - - - - - - - - - - - - - - - Required indexer. - - - - A Report element. - - - As stated in the , this is the other - lowest element (aside from ) in a Schematron schema. - This element contains the expression - to execute in the context of its parent . - If the results of the execution of the expression are true, the - report succeeds and the correponding message will be displayed. - - Constructor is not public. To programatically create an instance of this - class use the factory method. - - - - - - - Initializes a new instance of the class with the parameters specified. - The XPath expression to test. - The message to return. - - - - A Rule element. - - - Rules enclose the and - elements, providing the context for their evaluation. - According to the , nodes can be evaluated - by only one rule inside a , so typical schema - design includes placing the most exceptional rules first, down to - more generally applicable ones. - - Constructor is not public. To programatically create an instance of this - class use the factory method. - - - - Should we add support for the key element? - - - - - - Creates an abstract rule, without context. - - - - Initializes a new instance of the class, with the received context. - The rule's context to evaluate. - If passed a null or an , this is implicitly an abstract rule. - - - Initializes the context for the rule. - The rule's context to evaluate. - - If passed a null or an , this is implicitly an abstract rule. - - - Rules are evaluated through all the document (//), unless they - explicitly want to start from the root (/). This is consistent - with XSLT template match behavior. So we have to split the expression - per union (|) to add the root expression in these cases. - - - - Creates a new assert instance. - - Inheritors should override this method to create instances - of their own assert implementations. - - - The XPath expression to test for the assert. See - . - - - The message to display if the assert fails. See - . - - - - Creates a new report instance. - - Inheritors should override this method to create instances - of their own report implementations. - - - The XPath expression to test for the report. See - . - - - The message to display if the report succeeds. See - . - - - - - - - - - - - - Only abstract rules can be used as a base for extensions. - - - - - - - - - - - - - - - - - - A collection of Rule elements. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Returns the Rule element at the specified position. - - - - - - Lacks attributes defined in Schematron, but not in use currently. - - - - The Schematron namespace. - - - - - - - - - - - - - - - - - - - - Loads the schema from the specified URI. - - - - - Loads the schema from the reader. Closing the reader is responsibility of the caller. - - - - - Loads the schema from the stream. Closing the stream is responsibility of the caller. - - - - - Loads the schema from the reader. Closing the reader is responsibility of the caller. - - - - - - - - - - - - - - - - - - - - - - - - - A collection of schematron schemas. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Returns the Schema element at the specified position. - - - - - - - - - - - - - - - This code was generated by VS Paste Special Xml to Class. It therefore may feel like overkill and a poor model to support deserialization operations - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Evaluates all the schema elements synchronously. - - - - - See for a description of the purpose and - of evaluation contexts, and where are they used. - - - - Creates the evaluation context - - - - Starts the evaluation process. - - - When the process is finished, the results are placed - in the property. - - - - - Evaluates the selected . - - - As most of the other evaluation methods, it repositions the - navigator on the root node. - - The to evaluate. - Contains the builder to accumulate messages in. - A boolean indicating the presence of errors (true). - - - - Evaluates the selected . - - - As most of the other evaluation methods, it repositions the - navigator on the root node. - - Clears the object before - proceeding, as the restriction about node mathing (see ) - applies only inside a single pattern. - - - The to evaluate. - Contains the builder to accumulate messages in. - A boolean indicating if a new message was added. - - - - Evaluates the selected . - - - and are iterated - and each and is executed against - the context selected by the . - - Nodes matched are added to the list of - nodes to skip in the next rule, using the method. - This object is a strategy object which implements different algorithms for matching and - saving node references, as the actual implementation provides - different methods for accessing the underlying source. - - This makes the implementation both performant and compliant with - the restriction about node mathing (see ) in the spec. - - - - - - - As most of the other evaluation methods, it repositions the - navigator on the root node. - - - The to evaluate. - Contains the builder to accumulate messages in. - A boolean indicating if a new message was added. - - The rule to evaluate is abstract (see ). - - - - - Performs the evaluation of the . - - - This is where the actual assert expression is evaluated. If - the returns false, - a formated message is generated from the property. - - The to evaluate. - The context node for the execution. - Contains the builder to accumulate messages in. - A boolean indicating if a new message was added. - - - - Performs the evaluation of the . - - - This is where the actual report expression is evaluated. If - the returns true, - a formated message is generated from the property. - - The to evaluate. - The context node for the execution. - Contains the builder to accumulate messages in. - A boolean indicating if a new message was added. - - - - - - - The compiled regular expression to replace the special name tag inside a message. - - - Replaces each instance of name tags with the value un the current context element. - - - - - - - A collection of Test elements. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Returns the Test element at the specified position. - - - - Attribute applied to webmethods that require Schematron validation. - - - - - Initializes the attribute for a certain schema. - - The schema to validate the webmethod with. - - - - Initializes the attribute for a certain schema. - - The set of schemas to validate the webmethod with. - - - - Initializes the attribute for a certain schema. - - The schema to validate the webmethod with. - The formatting to use for the validation output. - - - - Initializes the attribute for a certain schema. - - The set of schemas to validate the webmethod with. - The formatting to use for the validation output. - - - - Gets the location of the schemas to use. - - - - - Gets/Sets the phase to use for validation. - - - - - Gets/Sets the formatting to use for validation errors. - - - - - Gets/Sets a type that implements to use - to format error messages. If set, this property takes precedence - over . - - - - - Gets the type of this extension attribute, which is always - . - - - - - Gets/Sets the priority of the extension. - - - - - Represents the an error in the Schematron schema. - - - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The error message that explains the reason for the exception. - - - - Initializes a new instance of the class. - - Info - Context - - - - Initializes a new instance of the class. - - The error message that explains the reason for the exception. - The exception that is the cause of the current exception. - - - - Extension that allows Schematron validation on webmethods. - - - - - Performs validation of Schematron elements and schemas. - - - Can handle either standalone or embedded schematron schemas. If the schematron - is embedded in an XML Schema, the input document is validated against both at - the same time. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class, using the specified output format for error messages. - - Output format of error messages. - - - - Initializes a new instance of the class, using the specified return type. - - The type to use for validation and return type. - - - - Initializes a new instance of the class, using the specified options. - - Output format of error messages. - The type to use for validation and return type. - - - - Initializes the validator with the options received from the constructor overloads. - - Output format of error messages. - The type to use for validation and return type. - - - Creates the evaluation context to use. - - Inheritors can override this method should they want to - use a different strategy for node traversal and evaluation - against the source file. - - - - - Adds an XML Schema to the collection to use for validation. - - - - - Adds a Schematron schema to the collection to use for validation. - - - - - Adds a set of XML Schemas to the collection to use for validation. - - - - - Adds a set of Schematron schemas to the collection to use for validation. - - - - - Adds a schema to the collection to use for validation from the specified URL. - - - - - Adds a schema to the collection to use for validation. - - - - - Adds a schema to the collection to use for validation. - - - - - Adds a schema to the collection to use for validation. - - Processing takes place here. - - - - Performs Schematron-only validation. - - - Even when implements IXPathNavigable, WXS - validation can't be performed once it has been loaded becasue a - validating reader has to be used. - - - The document is invalid with respect to the loaded schemas. - - - - - Performs Schematron-only validation. - - - The document is invalid with respect to the loaded schemas. - - - - Performs validation of the document at the specified URI. - The document location. - - The document is invalid with respect to the loaded schemas. - - The loaded instance. - - - Performs validation of the document using the specified reader. - The reader pointing to the document to validate. - The loaded instance. - - The document is invalid with respect to the loaded schemas. - - The loaded instance. - - - Performs validation of the document using the specified stream. - The stream with the document to validate. - - The document is invalid with respect to the loaded schemas. - - The loaded instance. - - - Performs validation of the document using the received reader. - Where the actual work takes place - The reader pointing to the document to validate. - - The document is invalid with respect to the loaded schemas. - - The loaded instance. - - - - - - - - - - - - - - - - Exposes the schematron schemas to use for validation. - - - - - Exposes the XML schemas to use for validation. - - - - - Strategy class for matching and keeping references to nodes in an . - - - - - When an is created from an , - it implements the interface, which is used to gain - access to the underlying node position. - - - - - The table contains an item for each line, and the item value - is an instance of our class for - optimized value types storage. - - - - Initializes an instance of the class. - - - See . - - - See . - - - See . - - - diff --git a/src/Schematron/SchematronXsltContext.cs b/src/Schematron/SchematronXsltContext.cs index e2fbce6..9da300b 100644 --- a/src/Schematron/SchematronXsltContext.cs +++ b/src/Schematron/SchematronXsltContext.cs @@ -10,8 +10,8 @@ namespace Schematron; /// sealed class SchematronXsltContext : XsltContext { - readonly Dictionary _variables; - readonly XPathNavigator? _contextNode; + readonly Dictionary variables; + readonly XPathNavigator? contextNode; /// /// Thread-local ambient context for the current Schematron evaluation. Used by @@ -23,11 +23,11 @@ sealed class SchematronXsltContext : XsltContext /// /// Creates a load-time (stub) context for schema loading. Variables return empty strings. - /// Useful for passing to when the + /// Useful for passing to when the /// expression contains variable references but actual values are not yet available. /// public static SchematronXsltContext ForLoading(XmlNamespaceManager nsManager) - => new SchematronXsltContext(new Dictionary(), null, nsManager); + => new([], null, nsManager); /// /// Initialises a new instance from accumulated <let> declarations, @@ -39,8 +39,8 @@ public SchematronXsltContext( XmlNamespaceManager nsManager) : base(new System.Xml.NameTable()) { - _variables = variables; - _contextNode = contextNode; + this.variables = variables; + this.contextNode = contextNode; // Copy prefix→namespace mappings so the context can resolve namespace prefixes used // in variable-value XPath expressions. @@ -49,7 +49,7 @@ public SchematronXsltContext( if (string.IsNullOrEmpty(prefix)) continue; // "xml" and "xmlns" are reserved and cannot be re-added. if (prefix == "xml" || prefix == "xmlns") continue; - string ns = nsManager.LookupNamespace(prefix) ?? String.Empty; + var ns = nsManager.LookupNamespace(prefix) ?? string.Empty; if (ns.Length > 0) { try { AddNamespace(prefix, ns); } @@ -74,12 +74,12 @@ public override IXsltContextFunction ResolveFunction(string prefix, string name, /// public override IXsltContextVariable ResolveVariable(string prefix, string name) { - if (_variables.TryGetValue(name, out string? expr)) - return new LetVariable(name, expr, _contextNode); + if (variables.TryGetValue(name, out var expr)) + return new LetVariable(name, expr, contextNode); // Return an empty-string variable for undefined references rather than throwing, // so that schemas with forward-declared variables do not crash during evaluation. - return new LetVariable(name, "", _contextNode); + return new LetVariable(name, "", contextNode); } // ------------------------------------------------------------------------- @@ -88,44 +88,30 @@ public override IXsltContextVariable ResolveVariable(string prefix, string name) /// Returns the raw string value for a variable by name, or if not found. /// public string? GetVariableValue(string name) - { - if (_variables.TryGetValue(name, out var val)) return val; - return null; - } + => variables.TryGetValue(name, out var val) ? val : null; // ------------------------------------------------------------------------- - sealed class LetVariable : IXsltContextVariable + sealed class LetVariable(string name, string expr, XPathNavigator? context) : IXsltContextVariable { - readonly string _name; - readonly string _expr; - readonly XPathNavigator? _context; - - public LetVariable(string name, string expr, XPathNavigator? context) - { - _name = name; - _expr = expr; - _context = context; - } - public bool IsLocal => true; public bool IsParam => false; public XPathResultType VariableType => XPathResultType.Any; public object Evaluate(XsltContext xsltContext) { - if (string.IsNullOrEmpty(_expr) || _context is null) - return String.Empty; + if (string.IsNullOrEmpty(expr) || context is null) + return string.Empty; try { - XPathExpression compiled = _context.Compile(_expr); + var compiled = context.Compile(expr); compiled.SetContext(xsltContext); - return _context.Evaluate(compiled); + return context.Evaluate(compiled); } catch { - return String.Empty; + return string.Empty; } } } diff --git a/src/Schematron/SyncEvaluationContext.cs b/src/Schematron/SyncEvaluationContext.cs index 9fe3256..3f1d067 100644 --- a/src/Schematron/SyncEvaluationContext.cs +++ b/src/Schematron/SyncEvaluationContext.cs @@ -15,11 +15,6 @@ namespace Schematron; /// class SyncEvaluationContext : EvaluationContextBase { - /// Creates the evaluation context - public SyncEvaluationContext() - { - } - /// /// Starts the evaluation process. /// @@ -29,21 +24,18 @@ public SyncEvaluationContext() /// public override void Start() { - this.Reset(); + Reset(); // Is there something to evaluate at all? - if (Schema.Patterns.Count == 0) return; + if (Schema is null || Schema.Patterns.Count == 0) + return; // If no phase was received, try the default phase defined for the schema. // If no default phase is defined, all patterns will be tested. if (Phase == string.Empty) - { - Phase = !string.IsNullOrEmpty(Schema.DefaultPhase) - ? Schema.DefaultPhase - : Schematron.Phase.All; - } + Phase = Schema.DefaultPhase is { Length: > 0 } phase ? phase : Schematron.Phase.All; - if (Schema.Phases[Phase] == null) + if (Phase != Schematron.Phase.All && Schema.Phases[Phase] == null) throw new ArgumentException("The specified Phase isn't defined for the current schema."); if (Evaluate(Schema.Phases[Phase], Messages)) @@ -65,7 +57,7 @@ public override void Start() /// A boolean indicating the presence of errors (true). bool Evaluate(Phase phase, StringBuilder output) { - bool failed = false; + var failed = false; Source.MoveToRoot(); var sb = new StringBuilder(); @@ -76,8 +68,8 @@ bool Evaluate(Phase phase, StringBuilder output) { var whenExpr = Source.Compile(phase.When); whenExpr.SetContext(SchematronXsltContext.ForLoading(Schema.NsManager)); - object whenResult = Source.Evaluate(whenExpr); - bool whenBool = whenResult switch + var whenResult = Source.Evaluate(whenExpr); + var whenBool = whenResult switch { bool b => b, string s => !string.IsNullOrEmpty(s), @@ -88,7 +80,7 @@ bool Evaluate(Phase phase, StringBuilder output) catch { /* if @when evaluation fails, proceed */ } } - foreach (Pattern pt in phase.Patterns) + foreach (var pt in phase.Patterns) { if (Evaluate(pt, sb)) failed = true; } @@ -119,16 +111,16 @@ bool Evaluate(Phase phase, StringBuilder output) /// A boolean indicating if a new message was added. bool Evaluate(Pattern pattern, StringBuilder output) { - bool failed = false; + var failed = false; Source.MoveToRoot(); var sb = new StringBuilder(); // Reset matched nodes, as across patters, nodes can be // evaluated more than once. Matched.Clear(); - bool isGroup = pattern is Group; + var isGroup = pattern is Group; - foreach (Rule rule in pattern.Rules) + foreach (var rule in pattern.Rules) { // For groups (ISO Schematron 2025), each rule evaluates independently. if (isGroup) Matched.Clear(); @@ -171,6 +163,7 @@ bool Evaluate(Pattern pattern, StringBuilder output) /// /// The to evaluate. /// Contains the builder to accumulate messages in. + /// A dictionary of pattern-level variable bindings (Let expressions) that will be merged with schema-level and rule-level variables during expression evaluation. Can be null if no pattern-level variables are defined. /// A boolean indicating if a new message was added. /// /// The rule to evaluate is abstract (see ). @@ -180,10 +173,10 @@ bool Evaluate(Rule rule, StringBuilder output, Dictionary? patte if (rule.IsAbstract) throw new InvalidOperationException("The Rule is abstract, so it can't be evaluated."); - bool failed = false; + var failed = false; var sb = new StringBuilder(); Source.MoveToRoot(); - XPathNodeIterator nodes = Source.Select(rule.CompiledExpression); + var nodes = Source.Select(rule.CompiledExpression); var evaluables = new ArrayList(nodes.Count); // The navigator doesn't contain line info @@ -193,7 +186,7 @@ bool Evaluate(Rule rule, StringBuilder output, Dictionary? patte { // Add the navigator to the list to be evaluated and to // the list of pattern-level nodes matched so far. - XPathNavigator curr = nodes.Current.Clone(); + var curr = nodes.Current.Clone(); evaluables.Add(curr); Matched.AddMatched(curr); } @@ -209,7 +202,7 @@ bool Evaluate(Rule rule, StringBuilder output, Dictionary? patte var expanded = new ArrayList(); foreach (XPathNavigator contextNode in evaluables) { - XPathNodeIterator visitNodes = contextNode.Select(visitExpr); + var visitNodes = contextNode.Select(visitExpr); while (visitNodes.MoveNext()) expanded.Add(visitNodes.Current.Clone()); } @@ -254,13 +247,15 @@ bool Evaluate(Rule rule, StringBuilder output, Dictionary? patte /// The to evaluate. /// The context node for the execution. /// Contains the builder to accumulate messages in. + /// A dictionary of pattern-level variable bindings (Let expressions) that will be merged with schema-level and rule-level variables during expression evaluation. Can be null if no pattern-level variables are defined. + /// A collection of rule-level variable bindings (Let expressions) that will be merged with schema-level and pattern-level variables during expression evaluation. Can be null if no rule-level variables are defined. /// A boolean indicating if a new message was added. bool EvaluateAssert(Assert assert, XPathNavigator context, StringBuilder output, Dictionary? patternLets = null, LetCollection? ruleLets = null) { var expr = PrepareExpression(assert.CompiledExpression, context, patternLets, ruleLets, out var xsltCtx); - object eval = context.Evaluate(expr); - bool result = true; + var eval = context.Evaluate(expr); + var result = true; if (assert.ReturnType == XPathResultType.Boolean) { @@ -292,13 +287,15 @@ bool EvaluateAssert(Assert assert, XPathNavigator context, StringBuilder output, /// The to evaluate. /// The context node for the execution. /// Contains the builder to accumulate messages in. + /// A dictionary of pattern-level variable bindings (Let expressions) that will be merged with schema-level and rule-level variables during expression evaluation. Can be null if no pattern-level variables are defined. + /// A collection of rule-level variable bindings (Let expressions) that will be merged with schema-level and pattern-level variables during expression evaluation. Can be null if no rule-level variables are defined. /// A boolean indicating if a new message was added. bool EvaluateReport(Report report, XPathNavigator context, StringBuilder output, Dictionary? patternLets = null, LetCollection? ruleLets = null) { var expr = PrepareExpression(report.CompiledExpression, context, patternLets, ruleLets, out var xsltCtx); - object eval = context.Evaluate(expr); - bool result = false; + var eval = context.Evaluate(expr); + var result = false; if (report.ReturnType == XPathResultType.Boolean) { @@ -326,10 +323,10 @@ bool EvaluateReport(Report report, XPathNavigator context, StringBuilder output, Dictionary BuildLets(LetCollection? extraLets = null) { var d = new Dictionary(StringComparer.Ordinal); - foreach (Let let in Schema.Lets) + foreach (var let in Schema.Lets) if (let.Value is not null) d[let.Name] = let.Value; if (extraLets != null) - foreach (Let let in extraLets) + foreach (var let in extraLets) if (let.Value is not null) d[let.Name] = let.Value; return d; } @@ -343,16 +340,16 @@ XPathExpression PrepareExpression( LetCollection? ruleLets, out SchematronXsltContext? xsltCtx) { - bool hasVars = (Schema.Lets.Count + (patternLets?.Count ?? 0) + (ruleLets?.Count ?? 0)) > 0; + var hasVars = (Schema.Lets.Count + (patternLets?.Count ?? 0) + (ruleLets?.Count ?? 0)) > 0; if (!hasVars) { xsltCtx = null; return expr; } var vars = new Dictionary(StringComparer.Ordinal); - foreach (Let let in Schema.Lets) + foreach (var let in Schema.Lets) if (let.Value is not null) vars[let.Name] = let.Value; if (patternLets != null) foreach (var kv in patternLets) vars[kv.Key] = kv.Value; if (ruleLets != null) - foreach (Let let in ruleLets) + foreach (var let in ruleLets) if (let.Value is not null) vars[let.Name] = let.Value; xsltCtx = new SchematronXsltContext(vars, context, Schema.NsManager); diff --git a/src/Schematron/TagExpressions.cs b/src/Schematron/TagExpressions.cs index 83c7a8a..d586a26 100644 --- a/src/Schematron/TagExpressions.cs +++ b/src/Schematron/TagExpressions.cs @@ -32,7 +32,7 @@ static TagExpressions() Any = new Regex(@"<[^\s]*[^>]*>", RegexOptions.Compiled); // Closing elements don't have an expanded xmlns so they will be matched too. // TODO: improve this to avoid removing non-schematron closing elements. - string nsPattern = "(?:" + Regex.Escape(Schema.LegacyNamespace) + "|" + Regex.Escape(Schema.IsoNamespace) + ")"; + var nsPattern = "(?:" + Regex.Escape(Schema.LegacyNamespace) + "|" + Regex.Escape(Schema.IsoNamespace) + ")"; AllSchematron = new Regex(@"<.*\bxmlns\b[^\s]*" + nsPattern + "[^>]*>|]*>", RegexOptions.Compiled); } diff --git a/src/Schematron/Test.cs b/src/Schematron/Test.cs index d1b7fda..f7b9997 100644 --- a/src/Schematron/Test.cs +++ b/src/Schematron/Test.cs @@ -1,4 +1,4 @@ -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using System.Xml.XPath; namespace Schematron; @@ -8,18 +8,6 @@ namespace Schematron; /// public abstract class Test : EvaluableExpression { - /// - protected string _msg = string.Empty; - - /// - protected MatchCollection _name_valueofs; - - /// - protected XPathExpression?[] _paths; - - /// - protected XPathExpression?[] _selects; - /// /// /// @@ -34,83 +22,70 @@ public Test(string test, string message) : base(test) // Save and tags in the message and their paths / selects in their compiled form. // TODO: see if we can work with the XML in the message, instead of using RE. // TODO: Check the correct usage of path and select attributes. - _name_valueofs = TagExpressions.NameValueOf.Matches(Message); - int nc = _name_valueofs.Count; - _paths = new XPathExpression[nc]; - _selects = new XPathExpression[nc]; + NameValueOfExpressions = TagExpressions.NameValueOf.Matches(Message); + var nc = NameValueOfExpressions.Count; + NamePaths = new XPathExpression[nc]; + ValueOfSelects = new XPathExpression[nc]; - for (int i = 0; i < nc; i++) + for (var i = 0; i < nc; i++) { - Match name_valueof = _name_valueofs[i]; - int start = name_valueof.Value.IndexOf("path="); + var name_valueof = NameValueOfExpressions[i]; + var start = name_valueof.Value.IndexOf("path="); if (start > 0) { start += 6; - int end = name_valueof.Value.LastIndexOf("xmlns") - 2; + var end = name_valueof.Value.LastIndexOf("xmlns") - 2; if (end < 0) end = name_valueof.Value.LastIndexOf('"'); - string xpath = name_valueof.Value.Substring(start, end - start); - _paths[i] = Config.DefaultNavigator.Compile(xpath); - _selects[i] = null; + var xpath = name_valueof.Value[start..end]; + NamePaths[i] = Config.DefaultNavigator.Compile(xpath); + ValueOfSelects[i] = null; } else if ((start = name_valueof.Value.IndexOf("select=")) > 0) { start += 8; - int end = name_valueof.Value.LastIndexOf("xmlns") - 2; + var end = name_valueof.Value.LastIndexOf("xmlns") - 2; if (end < 0) end = name_valueof.Value.LastIndexOf('"'); - string xpath = name_valueof.Value.Substring(start, end - start); - _selects[i] = Config.DefaultNavigator.Compile(xpath); - _paths[i] = null; + var xpath = name_valueof.Value[start..end]; + ValueOfSelects[i] = Config.DefaultNavigator.Compile(xpath); + NamePaths[i] = null; } else { - _paths[i] = null; - _selects[i] = null; + NamePaths[i] = null; + ValueOfSelects[i] = null; } } } /// - public string Message - { - get { return _msg; } - set { _msg = value; } - } + public string Message { get; set; } /// Gets or sets the optional identifier for this test (@id attribute). - public string Id { get; set; } = String.Empty; + public string Id { get; set; } = string.Empty; /// Gets or sets the role of this test (@role attribute). - public string Role { get; set; } = String.Empty; + public string Role { get; set; } = string.Empty; /// /// Gets or sets the flag tokens for this test (@flag attribute). /// In ISO Schematron 2025 @flag is a whitespace-separated list of tokens. /// - public IReadOnlyList Flag { get; set; } = Array.Empty(); + public IReadOnlyList Flag { get; set; } = []; /// Gets or sets the severity of this test (@severity attribute). - public string Severity { get; set; } = String.Empty; + public string Severity { get; set; } = string.Empty; /// Gets or sets the diagnostic IDs referenced by this test (@diagnostics attribute). - public IReadOnlyList DiagnosticRefs { get; set; } = Array.Empty(); + public IReadOnlyList DiagnosticRefs { get; set; } = []; /// - public MatchCollection NameValueOfExpressions - { - get { return _name_valueofs; } - } + public MatchCollection NameValueOfExpressions { get; protected set; } /// - public XPathExpression?[] NamePaths - { - get { return _paths; } - } + public XPathExpression?[] NamePaths { get; protected set; } = []; /// - public XPathExpression?[] ValueOfSelects - { - get { return _selects; } - } + public XPathExpression?[] ValueOfSelects { get; protected set; } = []; } \ No newline at end of file diff --git a/src/Schematron/TestCollection.cs b/src/Schematron/TestCollection.cs index 83e9fe5..596cf2c 100644 --- a/src/Schematron/TestCollection.cs +++ b/src/Schematron/TestCollection.cs @@ -1,76 +1,4 @@ -using System.Collections; - namespace Schematron; /// A collection of Test elements. -/// -/// -public class TestCollection : CollectionBase -{ - /// - public TestCollection() - { - } - - /// Returns the Test element at the specified position. - public Test this[int index] - { - get { return (Test)InnerList[index]; } - set { InnerList[index] = value; } - } - - /// - public int Add(Test value) - { - return InnerList.Add(value); - } - - /// - public void AddRange(Test[] values) - { - foreach (Test elem in values) - Add(elem); - } - - /// - public void AddRange(TestCollection values) - { - foreach (Test elem in values) - Add(elem); - } - - /// - public bool Contains(Test value) - { - return InnerList.Contains(value); - } - - /// - public void CopyTo(Test[] array, int index) - { - InnerList.CopyTo(array, index); - } - - /// - public int IndexOf(Test value) - { - return InnerList.IndexOf(value); - } - - /// - public void Insert(int index, Test value) - { - InnerList.Insert(index, value); - } - - /// - public void Remove(Test value) - { - int index = IndexOf(value); - if (index < 0) - throw (new ArgumentException("The specified object is not found in the collection")); - - RemoveAt(index); - } -} - +public class TestCollection : List; \ No newline at end of file diff --git a/src/Schematron/ValidationException.cs b/src/Schematron/ValidationException.cs index 6a38bdb..295c3d0 100644 --- a/src/Schematron/ValidationException.cs +++ b/src/Schematron/ValidationException.cs @@ -13,36 +13,26 @@ public class ValidationException : ApplicationException /// /// Initializes a new instance of the class. /// - public ValidationException() - { - } + public ValidationException() { } /// /// Initializes a new instance of the class. /// /// The error message that explains the reason for the exception. - public ValidationException(string message) : base(message) - { - } + public ValidationException(string message) : base(message) { } /// /// Initializes a new instance of the class. /// /// Info /// Context - protected ValidationException(SerializationInfo info, StreamingContext context) : - base(info, context) - { - } + protected ValidationException(SerializationInfo info, StreamingContext context) : base(info, context) { } /// /// Initializes a new instance of the class. /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception. - public ValidationException(string message, Exception innerException) : - base(message, innerException) - { - } + public ValidationException(string message, Exception innerException) : base(message, innerException) { } } diff --git a/src/Schematron/Validator.cs b/src/Schematron/Validator.cs index 1e3e243..cc5b5bd 100644 --- a/src/Schematron/Validator.cs +++ b/src/Schematron/Validator.cs @@ -16,54 +16,40 @@ namespace Schematron; /// public class Validator { - #region Fields & Ctors + readonly XmlSchemaSet xmlschemas = new(); + readonly SchemaCollection schematrons = []; + NavigableType navtype = NavigableType.XPathDocument; - XmlSchemaSet _xmlschemas = new XmlSchemaSet(); - SchemaCollection _schematrons = new SchemaCollection(); - EvaluationContextBase _evaluationctx; - NavigableType _navtype = NavigableType.XPathDocument; - - StringBuilder? _errors; - bool _haserrors; + StringBuilder? errors; + bool haserrors; /// /// Initializes a new instance of the class. /// public Validator() { - _evaluationctx = CreateContext(); - _evaluationctx.Formatter = Config.DefaultFormatter; + Context = CreateContext(); + Context.Formatter = Config.DefaultFormatter; } /// /// Initializes a new instance of the class, using the specified output format for error messages. /// /// Output format of error messages. - public Validator(OutputFormatting format) : this() - { - InitValidator(format, NavigableType.Default); - } + public Validator(OutputFormatting format) : this() => InitValidator(format, NavigableType.Default); /// /// Initializes a new instance of the class, using the specified return type. /// /// The type to use for validation and return type. - public Validator(NavigableType type) : this() - { - InitValidator(OutputFormatting.Default, type); - } + public Validator(NavigableType type) : this() => InitValidator(OutputFormatting.Default, type); /// /// Initializes a new instance of the class, using the specified options. /// /// Output format of error messages. /// The type to use for validation and return type. - public Validator(OutputFormatting format, NavigableType type) : this() - { - InitValidator(format, type); - } - - #endregion Fields & Ctors + public Validator(OutputFormatting format, NavigableType type) : this() => InitValidator(format, type); /// /// Initializes the validator with the options received from the constructor overloads. @@ -78,17 +64,17 @@ void InitValidator(OutputFormatting format, NavigableType type) switch (format) { case OutputFormatting.Boolean: - _evaluationctx.Formatter = new BooleanFormatter(); + Context.Formatter = new BooleanFormatter(); break; case OutputFormatting.Log: case OutputFormatting.Default: - _evaluationctx.Formatter = new LogFormatter(); + Context.Formatter = new LogFormatter(); break; case OutputFormatting.Simple: - _evaluationctx.Formatter = new SimpleFormatter(); + Context.Formatter = new SimpleFormatter(); break; case OutputFormatting.XML: - _evaluationctx.Formatter = new XmlFormatter(); + Context.Formatter = new XmlFormatter(); break; } @@ -96,133 +82,94 @@ void InitValidator(OutputFormatting format, NavigableType type) throw new ArgumentException("Invalid type.", "type"); // If type is Default, set it to XPathDocument. - _navtype = (type != NavigableType.Default) ? type : NavigableType.XPathDocument; + navtype = (type != NavigableType.Default) ? type : NavigableType.XPathDocument; } - #region Overridable Factory Methods /// Creates the evaluation context to use. /// /// Inheritors can override this method should they want to /// use a different strategy for node traversal and evaluation /// against the source file. /// - protected virtual EvaluationContextBase CreateContext() - { - return new SyncEvaluationContext(); - } - #endregion + protected virtual EvaluationContextBase CreateContext() => new SyncEvaluationContext(); - #region Properties /// - public EvaluationContextBase Context - { - get { return _evaluationctx; } - set { _evaluationctx = value; } - } + public EvaluationContextBase Context { get; set; } /// public IFormatter Formatter { - get { return _evaluationctx.Formatter; } - set { _evaluationctx.Formatter = value; } + get => Context.Formatter; + set => Context.Formatter = value; } /// public NavigableType ReturnType { - get { return _navtype; } + get { return navtype; } set { if (!Enum.IsDefined(typeof(NavigableType), value)) throw new ArgumentException("NavigableType value is not defined."); - _navtype = value; + navtype = value; } } /// public string Phase { - get { return _evaluationctx.Phase; } - set { _evaluationctx.Phase = value; } + get => Context.Phase; + set => Context.Phase = value; } /// /// Exposes the schematron schemas to use for validation. /// - public SchemaCollection Schemas - { - get { return _schematrons; } - } + public SchemaCollection Schemas => schematrons; /// /// Exposes the XML schemas to use for validation. /// - public XmlSchemaSet XmlSchemas - { - get { return _xmlschemas; } - } + public XmlSchemaSet XmlSchemas => xmlschemas; - #endregion - - #region AddSchema overloads /// /// Adds an XML Schema to the collection to use for validation. /// - public void AddSchema(XmlSchema schema) - { - _xmlschemas.Add(schema); - } + public void AddSchema(XmlSchema schema) => xmlschemas.Add(schema); /// /// Adds a Schematron schema to the collection to use for validation. /// - public void AddSchema(Schema schema) - { - _schematrons.Add(schema); - } + public void AddSchema(Schema schema) => schematrons.Add(schema); /// /// Adds a set of XML Schemas to the collection to use for validation. /// - public void AddSchemas(XmlSchemaSet schemas) - { - _xmlschemas.Add(schemas); - } + public void AddSchemas(XmlSchemaSet schemas) => xmlschemas.Add(schemas); /// /// Adds a set of Schematron schemas to the collection to use for validation. /// - public void AddSchemas(SchemaCollection schemas) - { - _schematrons.AddRange(schemas); - } + public void AddSchemas(SchemaCollection schemas) => schematrons.AddRange(schemas); /// /// Adds a schema to the collection to use for validation from the specified URL. /// public void AddSchema(string uri) { - using (var fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - AddSchema(new XmlTextReader(fs)); - } + using var fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read); + AddSchema(new XmlTextReader(fs)); } /// /// Adds a schema to the collection to use for validation. /// - public void AddSchema(TextReader reader) - { - AddSchema(new XmlTextReader(reader)); - } + public void AddSchema(TextReader reader) => AddSchema(new XmlTextReader(reader)); /// /// Adds a schema to the collection to use for validation. /// - public void AddSchema(Stream input) - { - AddSchema(new XmlTextReader(input)); - } + public void AddSchema(Stream input) => AddSchema(new XmlTextReader(input)); /// /// Adds a schema to the collection to use for validation. @@ -230,20 +177,21 @@ public void AddSchema(Stream input) /// Processing takes place here. public void AddSchema(XmlReader reader) { - if (reader.MoveToContent() == XmlNodeType.None) throw new BadSchemaException("No information found to read"); + if (reader.MoveToContent() == XmlNodeType.None) + throw new BadSchemaException("No information found to read"); // Determine type of schema received. - bool standalone = Schema.IsSchematronNamespace(reader.NamespaceURI); - bool wxs = (reader.NamespaceURI == XmlSchema.Namespace); + var standalone = Schema.IsSchematronNamespace(reader.NamespaceURI); + var wxs = (reader.NamespaceURI == XmlSchema.Namespace); // The whole schema must be read first to preserve the state for later. - string state = reader.ReadOuterXml(); + var state = reader.ReadOuterXml(); var r = new StringReader(state); if (wxs) { - _haserrors = false; - _errors = new StringBuilder(); + haserrors = false; + errors = new StringBuilder(); var xs = XmlSchema.Read(new XmlTextReader(r, reader.NameTable), new ValidationEventHandler(OnValidation)); @@ -255,9 +203,9 @@ public void AddSchema(XmlReader reader) set.Compile(); } - if (_haserrors) throw new BadSchemaException(_errors.ToString()); + if (haserrors) throw new BadSchemaException(errors.ToString()); - _xmlschemas.Add(xs); + xmlschemas.Add(xs); } //Schemas wouldn't be too big, so they are loaded in an XmlDocument for Schematron validation, so that @@ -266,21 +214,21 @@ public void AddSchema(XmlReader reader) //XPathNavigator nav = new XPathDocument(new XmlTextReader(r, reader.NameTable)).CreateNavigator(); var doc = new XmlDocument(reader.NameTable); doc.LoadXml(state); - XPathNavigator nav = doc.CreateNavigator(); - _evaluationctx.Source = nav; + var nav = doc.CreateNavigator(); + Context.Source = nav; if (standalone) PerformValidation(Config.FullSchematron); else PerformValidation(Config.EmbeddedSchematron); - if (_evaluationctx.HasErrors) - throw new BadSchemaException(_evaluationctx.Messages.ToString()); + if (Context.HasErrors) + throw new BadSchemaException(Context.Messages.ToString()); var sch = new Schema(); sch.Load(nav); - _schematrons.Add(sch); - _errors = null; + schematrons.Add(sch); + errors = null; } @@ -299,35 +247,32 @@ bool TryAddXmlSchema( nameTable = null; namespaceUri = null; - using (var reader = XmlReader.Create(schemaUri)) - { - if (reader.MoveToContent() == XmlNodeType.None) - { - throw new BadSchemaException("No information found to read"); - } + using var reader = XmlReader.Create(schemaUri); + if (reader.MoveToContent() == XmlNodeType.None) + throw new BadSchemaException("No information found to read"); - nameTable = reader.NameTable; - namespaceUri = reader.NamespaceURI; - xmlContent = reader.ReadOuterXml(); + nameTable = reader.NameTable; + namespaceUri = reader.NamespaceURI; + xmlContent = reader.ReadOuterXml(); - if (!IsStandardSchema(namespaceUri)) - return false; + if (!IsStandardSchema(namespaceUri)) + return false; - if (_errors == null) - _errors = new StringBuilder(); + errors ??= new StringBuilder(); - var set = new XmlSchemaSet(); - set.XmlResolver = new XmlUrlResolver(); - set.Add(targetNamespace, new Uri(Path.GetFullPath(schemaUri)).AbsoluteUri); + var set = new XmlSchemaSet + { + XmlResolver = new XmlUrlResolver() + }; + set.Add(targetNamespace, new Uri(Path.GetFullPath(schemaUri)).AbsoluteUri); - if (!set.IsCompiled) - set.Compile(); + if (!set.IsCompiled) + set.Compile(); - if (_haserrors) throw new BadSchemaException(_errors.ToString()); + if (haserrors) throw new BadSchemaException(errors.ToString()); - foreach (XmlSchema s in set.Schemas()) - schemaSet.Add(s); - } + foreach (XmlSchema s in set.Schemas()) + schemaSet.Add(s); return true; } @@ -342,35 +287,32 @@ bool TryAddXmlSchema( /// Validation takes place here. public void AddSchema(string targetNamespace, string schemaUri) { - TryAddXmlSchema(targetNamespace, schemaUri, _xmlschemas, OnValidation, out var xmlContent, out var nameTable, out var namespaceUri); + TryAddXmlSchema(targetNamespace, schemaUri, xmlschemas, OnValidation, out var xmlContent, out var nameTable, out var namespaceUri); var doc = new XmlDocument(nameTable!); doc.LoadXml(xmlContent!); - XPathNavigator nav = doc.CreateNavigator(); - _evaluationctx.Source = nav; + var nav = doc.CreateNavigator(); + Context.Source = nav; if (IsStandaloneSchematron(namespaceUri)) PerformValidation(Config.FullSchematron); else PerformValidation(Config.EmbeddedSchematron); - if (_evaluationctx.HasErrors) - throw new BadSchemaException(_evaluationctx.Messages.ToString()); + if (Context.HasErrors) + throw new BadSchemaException(Context.Messages.ToString()); var sch = new Schema(); sch.Load(nav); - _schematrons.Add(sch); - _errors = null; + schematrons.Add(sch); + errors = null; } #endregion - #endregion - - #region Validation Methods /// /// Performs Schematron-only validation. /// @@ -382,10 +324,7 @@ public void AddSchema(string targetNamespace, string schemaUri) /// /// The document is invalid with respect to the loaded schemas. /// - public void ValidateSchematron(IXPathNavigable source) - { - ValidateSchematron(source.CreateNavigator()); - } + public void ValidateSchematron(IXPathNavigable source) => ValidateSchematron(source.CreateNavigator()); /// /// Performs Schematron-only validation. @@ -395,26 +334,26 @@ public void ValidateSchematron(IXPathNavigable source) /// public void ValidateSchematron(XPathNavigator file) { - _errors = new StringBuilder(); - _evaluationctx.Source = file; + errors = new StringBuilder(); + Context.Source = file; - foreach (Schema sch in _schematrons) + foreach (var sch in schematrons) { PerformValidation(sch); - if (_evaluationctx.HasErrors) + if (Context.HasErrors) { - _haserrors = true; - _errors.Append(_evaluationctx.Messages.ToString()); + haserrors = true; + errors.Append(Context.Messages.ToString()); } } - if (_haserrors) + if (haserrors) { - _evaluationctx.Formatter.Format(_errors); - throw new ValidationException(_errors.ToString()); + Context.Formatter.Format(errors); + throw new ValidationException(errors.ToString()); } - if (_haserrors) throw new ValidationException(_errors.ToString()); + if (haserrors) throw new ValidationException(errors.ToString()); } /// Performs validation of the document at the specified URI. @@ -425,10 +364,8 @@ public void ValidateSchematron(XPathNavigator file) /// The loaded instance. public IXPathNavigable Validate(string uri) { - using (var fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - return Validate(new XmlTextReader(fs)); - } + using var fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read); + return Validate(new XmlTextReader(fs)); } /// Performs validation of the document using the specified reader. @@ -438,10 +375,7 @@ public IXPathNavigable Validate(string uri) /// The document is invalid with respect to the loaded schemas. /// /// The loaded instance. - public IXPathNavigable Validate(TextReader reader) - { - return Validate(new XmlTextReader(reader)); - } + public IXPathNavigable Validate(TextReader reader) => Validate(new XmlTextReader(reader)); /// Performs validation of the document using the specified stream. /// The stream with the document to validate. @@ -449,10 +383,7 @@ public IXPathNavigable Validate(TextReader reader) /// The document is invalid with respect to the loaded schemas. /// /// The loaded instance. - public IXPathNavigable Validate(Stream input) - { - return Validate(new XmlTextReader(input)); - } + public IXPathNavigable Validate(Stream input) => Validate(new XmlTextReader(input)); /// Performs validation of the document using the received reader. /// Where the actual work takes place @@ -463,11 +394,11 @@ public IXPathNavigable Validate(Stream input) /// The loaded instance. public IXPathNavigable Validate(XmlReader reader) { - _errors = new StringBuilder(); + errors = new StringBuilder(); - bool hasxml = false; + var hasxml = false; StringBuilder? xmlerrors = null; - bool hassch = false; + var hassch = false; StringBuilder? scherrors = null; var settings = new XmlReaderSettings @@ -476,7 +407,7 @@ public IXPathNavigable Validate(XmlReader reader) }; settings.ValidationEventHandler += OnValidation; - foreach (XmlSchema xsd in _xmlschemas.Schemas()) + foreach (XmlSchema xsd in xmlschemas.Schemas()) settings.Schemas.Add(xsd); var r = XmlReader.Create(reader, settings); @@ -486,7 +417,7 @@ public IXPathNavigable Validate(XmlReader reader) try { - if (_navtype == NavigableType.XmlDocument) + if (navtype == NavigableType.XmlDocument) { navdoc = new XmlDocument(r.NameTable); ((XmlDocument)navdoc).Load(r); @@ -503,61 +434,60 @@ public IXPathNavigable Validate(XmlReader reader) nav = navdoc.CreateNavigator(); - if (_haserrors) + if (haserrors) { - _evaluationctx.Formatter.Format(r.Settings.Schemas, _errors); - _evaluationctx.Formatter.Format(r, _errors); + Context.Formatter.Format(r.Settings.Schemas, errors); + Context.Formatter.Format(r, errors); hasxml = true; - xmlerrors = _errors; + xmlerrors = errors; } - _evaluationctx.Source = nav; + Context.Source = nav; // Reset shared variables - _haserrors = false; - _errors = new StringBuilder(); + haserrors = false; + errors = new StringBuilder(); - foreach (Schema sch in _schematrons) + foreach (var sch in schematrons) { PerformValidation(sch); - if (_evaluationctx.HasErrors) + if (Context.HasErrors) { - _haserrors = true; - _errors.Append(_evaluationctx.Messages.ToString()); + haserrors = true; + errors.Append(Context.Messages.ToString()); } } - if (_haserrors) + if (haserrors) { - _evaluationctx.Formatter.Format(_schematrons, _errors); + Context.Formatter.Format(schematrons, errors); hassch = true; - scherrors = _errors; + scherrors = errors; } - _errors = new StringBuilder(); - if (hasxml) _errors.Append(xmlerrors!.ToString()); - if (hassch) _errors.Append(scherrors!.ToString()); + errors = new StringBuilder(); + if (hasxml) errors.Append(xmlerrors!.ToString()); + if (hassch) errors.Append(scherrors!.ToString()); if (hasxml || hassch) { - _evaluationctx.Formatter.Format(_errors); - throw new ValidationException(_errors.ToString()); + Context.Formatter.Format(errors); + throw new ValidationException(errors.ToString()); } return navdoc; } - #endregion void PerformValidation(Schema schema) { - _evaluationctx.Schema = schema; - _evaluationctx.Start(); + Context.Schema = schema; + Context.Start(); } void OnValidation(object sender, ValidationEventArgs e) { - _haserrors = true; - _evaluationctx.Formatter.Format(e, _errors!); + haserrors = true; + Context.Formatter.Format(e, errors!); } } diff --git a/src/Schematron/XPathMatchedNodes.cs b/src/Schematron/XPathMatchedNodes.cs index 5996a3d..e3ca24d 100644 --- a/src/Schematron/XPathMatchedNodes.cs +++ b/src/Schematron/XPathMatchedNodes.cs @@ -16,14 +16,14 @@ namespace Schematron; class XPathMatchedNodes : IMatchedNodes { // The dictionary maps each line number to a list of column positions. - Dictionary> _matched = new Dictionary>(); + readonly Dictionary> matched = []; /// See . public bool IsMatched(XPathNavigator node) { var info = (IXmlLineInfo)node; - if (!_matched.TryGetValue(info.LineNumber, out List pos)) + if (!matched.TryGetValue(info.LineNumber, out var pos)) return false; return pos.Contains(info.LinePosition); @@ -34,16 +34,16 @@ public void AddMatched(XPathNavigator node) { var info = (IXmlLineInfo)node; - if (!_matched.TryGetValue(info.LineNumber, out var pos)) + if (!matched.TryGetValue(info.LineNumber, out var pos)) { pos = []; - _matched.Add(info.LineNumber, pos); + matched.Add(info.LineNumber, pos); } pos.Add(info.LinePosition); } /// See . - public void Clear() => _matched.Clear(); + public void Clear() => matched.Clear(); }