Example #1
0
        /// <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));
        }