/// <summary> /// Evaluates the selected <see cref="Rule"/>. /// </summary> /// <remarks> /// <see cref="Rule.Asserts"/> and <see cref="Rule.Reports"/> are iterated /// and each <see cref="Assert"/> and <see cref="Report"/> is executed against /// the context selected by the <see cref="Rule.Context"/>. /// <para> /// Nodes matched are added to the <see cref="EvaluationContextBase.Matched"/> list of /// nodes to skip in the next rule, using the <see cref="IMatchedNodes.AddMatched"/> method. /// This object is a strategy object which implements different algorithms for matching and /// saving node references, as the actual <see cref="XPathNavigator"/> implementation provides /// different methods for accessing the underlying source. /// <para> /// This makes the implementation both performant and compliant with /// the restriction about node mathing (see <linkref id="schematron" />) in the spec. /// </para> /// <para> /// <seealso cref="DomMatchedNodes"/> /// <seealso cref="XPathMatchedNodes"/> /// <seealso cref="GenericMatchedNodes"/> /// </para> /// As most of the other evaluation methods, it repositions the /// <see cref="EvaluationContextBase.Source"/> navigator on the root node. /// </para> /// </remarks> /// <param name="rule">The <see cref="Rule"/> to evaluate.</param> /// <param name="output">Contains the builder to accumulate messages in.</param> /// <returns>A boolean indicating if a new message was added.</returns> /// <exception cref="InvalidOperationException"> /// The rule to evaluate is abstract (see <see cref="Rule.IsAbstract"/>). /// </exception> private bool Evaluate(Rule rule, StringBuilder output) { if (rule.IsAbstract) { throw new InvalidOperationException("The Rule is abstract, so it can't be evaluated."); } bool failed = false; StringBuilder sb = new StringBuilder(); Source.MoveToRoot(); XPathNodeIterator nodes = Source.Select(rule.CompiledExpression); ArrayList evaluables = new ArrayList(nodes.Count); // The navigator doesn't contain line info while (nodes.MoveNext()) { if (!Matched.IsMatched(nodes.Current)) { // 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(); evaluables.Add(curr); Matched.AddMatched(curr); } } foreach (Assert asr in rule.Asserts) { foreach (XPathNavigator node in evaluables) { if (EvaluateAssert(asr, node.Clone(), sb)) { failed = true; } } } foreach (Report rpt in rule.Reports) { foreach (XPathNavigator node in evaluables) { if (EvaluateReport(rpt, node.Clone(), sb)) { failed = true; } } } if (failed) { Formatter.Format(rule, Source, sb); output.Append(sb.ToString()); } return(failed); }
/// <summary> /// Evaluates the selected <see cref="Rule"/>. /// </summary> /// <remarks> /// Here is where asynchronous becomes. <see cref="Assert"/> and /// <see cref="Report"/> are queued using the <see cref="ThreadPool"/> class. /// <para> /// Nodes matched by this <see cref="Rule"/> are added to the <see cref="EvaluationContextBase.Matched"/> list of /// nodes to skip in the next rule, using the <see cref="IMatchedNodes.AddMatched"/> method. /// This object is a strategy object which implements different algorithms for matching and /// saving node references, as the actual <see cref="XPathNavigator"/> implementation provides /// different methods for accessing the underlying source. /// <para> /// This makes the implementation both performant and compliant with /// the restriction about node mathing (see <link ref="schematron" />) in the spec. /// </para> /// <para> /// <seealso cref="DomMatchedNodes"/> /// <seealso cref="XPathMatchedNodes"/> /// <seealso cref="GenericMatchedNodes"/> /// </para> /// As most of the other evaluation methods, it repositions the /// <see cref="EvaluationContextBase.Source"/> navigator on the root node. /// </para> /// <para>Here is where the multithreading problems arise, which are not /// due to the schema design itself, but this specific evaluation process. /// The intent it to evaluate asserts and reports in parallel, to get the /// most out of the CPU. /// </para> /// </remarks> /// <param name="rule">The <see cref="Rule"/> to evaluate.</param> /// <returns>The messages accumulated by the evaluation of all the child /// <see cref="Assert"/> and <see cref="Report"/>.</returns> /// <exception cref="InvalidOperationException"> /// The rule to evaluate is abstract (see <see cref="Rule.IsAbstract"/>). /// </exception> private string Evaluate(Rule rule) { if (rule.IsAbstract) { throw new InvalidOperationException("The Rule is abstract, so it can't be evaluated."); } StringBuilder sb = new StringBuilder(); Source.MoveToRoot(); XPathNodeIterator nodes = Source.Select(rule.CompiledExpression); ArrayList evaluables = new ArrayList(nodes.Count); while (nodes.MoveNext()) { if (!Matched.IsMatched(nodes.Current)) { // 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(); evaluables.Add(curr); Matched.AddMatched(curr); } } ArrayList handles = new ArrayList(rule.Asserts.Count + rule.Reports.Count); foreach (Assert asr in rule.Asserts) { //AsyncAssertEvaluate eval = new AsyncAssertEvaluate(EvaluateAssert); foreach (XPathNavigator node in evaluables) { XPathNavigator ctx = node.Clone(); WaitHandle wh = new AutoResetEvent(true); WaitOrTimerCallback cb = new WaitOrTimerCallback(OnAssertEvaluate); ThreadPool.RegisterWaitForSingleObject(wh, cb, new AsyncAssertState(asr, sb, ctx), Timeout.Infinite, true); handles.Add(wh); //handles.Add(eval.BeginInvoke( // asr, ctx, new AsyncCallback(OnAssertCompleted), // new AsyncAssertState(asr, sb, ctx)).AsyncWaitHandle); //Synchronous evaluation //string str = EvaluateAssert(asr, node.Clone()); //if (str != String.Empty) // sb.Append(str).Append(System.Environment.NewLine); } } foreach (Report rpt in rule.Reports) { //AsyncReportEvaluate eval = new AsyncReportEvaluate(EvaluateReport); foreach (XPathNavigator node in evaluables) { XPathNavigator ctx = node.Clone(); WaitHandle wh = new AutoResetEvent(true); WaitOrTimerCallback cb = new WaitOrTimerCallback(OnReportEvaluate); ThreadPool.RegisterWaitForSingleObject(wh, cb, new AsyncReportState(rpt, sb, ctx), Timeout.Infinite, true); handles.Add(wh); //handles.Add(eval.BeginInvoke( // rpt, ctx, new AsyncCallback(OnReportCompleted), // new AsyncReportState(rpt, sb, ctx)).AsyncWaitHandle); //Synchronous evaluation //string str = EvaluateReport(rpt, node.Clone()); //if (str != String.Empty) // sb.Append(str).Append(System.Environment.NewLine); } } try { //TODO: I think we are getting contention here. Anyone can help? WaitHandle[] waithandles = new WaitHandle[handles.Count]; handles.CopyTo(waithandles); WaitHandle.WaitAll(waithandles); } catch (Exception ex) { System.Diagnostics.Debug.Fail(ex.ToString()); } string res = sb.ToString(); return(Formatter.Format(rule, res, Source)); }