Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Parser Schemas– Easy and Powerful parsing of XML-based languages

By , 18 Oct 2005
/***********************************************************************\
 * Comnicate.CodeDom.Xml.ParserSchemas                                 *
 * Parses xml-based languages according to a user defined schema.      *
 * Copyright � 2005 Tomas Deml (as Comnicate!)                         *
 *                  tomasdeml@msn.com                                  *
 *                                                                     *
 * This library is free software; you can redistribute it and/or       *
 * modify it under the terms of the GNU Lesser General Public          *
 * License as published by the Free Software Foundation; either        *
 * version 2.1 of the License, or (at your option) any later version.  *
 *                                                                     *
 * This library is distributed in the hope that it will be useful,     *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of      *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   *
 * Lesser General Public License for more details.                     *
\***********************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.XPath;
using Comnicate.CodeDom.Xml.ParserSchemas.Rules.Evaluated;
using Comnicate.CodeDom.Xml.ParserSchemas.Rules.Evaluation;

namespace Comnicate.CodeDom.Xml.ParserSchemas.Rules
{
    /// <summary>
    /// Indicates to or not to consider the value of a node.
    /// </summary>
    [Flags]    
    public enum NodeValueOptions : int
    {
        /// <summary>
        /// Node value will be ignored. Recommended for parental rules.
        /// </summary>
        IgnoreValue = 1,

        /// <summary>
        /// Node value will be captured. Recommended for non-parental rules.
        /// </summary>
        GetValue = 2,

        /// <summary>
        /// Node value will be required. If the value is empty, an exception will be thrown. 
        /// </summary>
        RequireValue = 4
    }

    /// <summary>
    /// Indicates to or not to require at least one optional entry (child) of a parental rule.
    /// </summary>
    public enum OptionalEntriesMatchOption : int
    {
        /// <summary>
        /// Do not require any optional entries.
        /// </summary>
        DoNotRequireAnyOptionalEntries,

        /// <summary>
        /// Require at least one optional entry.
        /// </summary>
        RequireAtLeastOneOptionalEntry
    }

    /// <summary>
    /// Represents a schema rule.
    /// </summary>
    public abstract class Rule
    {
        #region Fields

        // Node name
        private string nodeName;

        // Node uri
        private Uri namespaceUri;

        // Node value options
        private NodeValueOptions nodeValueOptions;

        // Rule ID
        internal int ruleCookie;

        // Rnd number generator (generates the Rule ID)
        private static readonly Random RandomGenerator = new Random();
        
        #endregion

        #region .ctors
        // All these .ctors must be internal to avoid unwanted inheritance. Only children 'ParentalRule' and 'NonParentalRule' are intended for inheritance.

        internal Rule(string nodeName) 
            : this(nodeName, null, NodeValueOptions.IgnoreValue) { }

        internal Rule(string nodeName, Uri namespaceUri)
            : this(nodeName, namespaceUri, NodeValueOptions.IgnoreValue) { }

        internal Rule(string nodeName, NodeValueOptions options)
            : this(nodeName, null, options)
        { }

        internal Rule(string nodeName, Uri namespaceUri, NodeValueOptions options)
        {
            // Init fields
            this.nodeName = nodeName;
            this.namespaceUri = namespaceUri;
            this.nodeValueOptions = options;
            this.ruleCookie = RandomGenerator.Next();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets (+ protected set) name of the node to be matched by this rule.
        /// </summary>
        public string NodeName
        {
            get
            {
                return this.nodeName;
            }
            protected set
            {
                this.nodeName = value;
            }
        }

        /// <summary>
        /// Gets (+ protected set) namespace of the node to be matched by this rule.
        /// </summary>
        public Uri NamespaceUri
        {
            get
            {
                return this.namespaceUri;
            }
            protected set
            {
                this.namespaceUri = value;
            }
        }        

        /// <summary>
        /// Gets or sets the node value handling options.
        /// </summary>
        public NodeValueOptions NodeValueOptions
        {
            get
            {
                return this.nodeValueOptions;
            }
            set
            {
                this.nodeValueOptions = value;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Informs about the equality of two rules.
        /// </summary>
        /// <param name="obj">Another rule.</param>
        /// <returns>True if the rules are equal; False if they are different.</returns>
        public override bool Equals(object obj)
        {
            // Cast
            Rule other = obj as Rule;

            // Null?
            if (object.ReferenceEquals(other, null)) return false;

            // Check properties
            if (other.nodeName != this.nodeName) return false;
            if (other.namespaceUri != this.namespaceUri) return false;
            if (other.nodeValueOptions != this.nodeValueOptions) return false;

            // All passed
            return true;
        }

        /// <summary>
        /// Return unique code representing this rule.
        /// </summary>
        /// <returns>Rule hash code.</returns>
        public override int GetHashCode()
        {
            return (this.nodeName.GetHashCode() & this.namespaceUri.GetHashCode() & this.nodeValueOptions.GetHashCode());
        }        

        /// <summary>
        /// == operator overload.
        /// </summary>
        /// <param name="first">First rule.</param>
        /// <param name="second">Second rule.</param>
        /// <returns>True if the rules are equal; False if they are different.</returns>
        public static bool operator ==(Rule first, Rule second)
        {
            return object.Equals(first, second);
        }

        /// <summary>
        /// != operator overload.
        /// </summary>
        /// <param name="first">First rule.</param>
        /// <param name="second">Second rule.</param>
        /// <returns>True if the rules are not equal; False if they are equal.</returns>
        public static bool operator !=(Rule first, Rule second)
        {
            return !object.Equals(first, second);
        }

        /// <summary>
        /// Ignores or stores the node value depending on <see cref="NodeValueOptions"/>.
        /// </summary>
        /// <param name="nodeNavigator">XPathNavigator pointed to the node whose value should be resolved.</param>
        /// <param name="evaluatedRule">An instance of the <see cref="EvaluatedRule"/> class which will hold the node value.</param>
        protected void ResolveValueOptions(XPathNavigator nodeNavigator, EvaluatedRule evaluatedRule)
        {
            // Null?
            if (nodeNavigator == null || evaluatedRule == null) throw new ArgumentNullException(Resources.ExceptionMsg_NullArgumentsPassed);

            // Ignore?
            if ((this.nodeValueOptions & NodeValueOptions.IgnoreValue) == NodeValueOptions.IgnoreValue)
                return;

            // PublishEvaluatedRule?
            if ((this.nodeValueOptions & NodeValueOptions.GetValue) == NodeValueOptions.GetValue)
                evaluatedRule.Value = nodeNavigator.Value;

            // Require?
            if ((this.nodeValueOptions & NodeValueOptions.RequireValue) == NodeValueOptions.RequireValue)
                if (String.IsNullOrEmpty(nodeNavigator.Value))
                    throw new MissingNodeValueException(this, nodeNavigator);
        }
        
        /// <summary>
        /// Inspects the node the XPathNavigator instance is pointed to and say if this rule can match the node.
        /// </summary>
        /// <param name="nodeNavigator">XPathNavigator pointing to the node.</param>
        /// <returns>True if the node can be parsed by this rule; False if it cannot be parsed with this rule.</returns>
        public virtual bool IsMatch(XPathNavigator nodeNavigator)
        {
            // Check for null...
            if (nodeNavigator == null) return false;
  
            // Are names equal?
            bool res1 = (nodeNavigator.LocalName == this.nodeName);
            if (!res1) return false;

            // Namespace equality
            bool res2;

            // The rule does not specifies the namespace...
            if (this.namespaceUri == null)
                res2 = String.IsNullOrEmpty(nodeNavigator.NamespaceURI);
            // The node is not namespaced but this rule specifies the namespace...
            else if (String.IsNullOrEmpty(nodeNavigator.NamespaceURI))
                res2 = false;
            // Check namespaces equality...
            else
                res2 = (this.namespaceUri.ToString() == nodeNavigator.NamespaceURI);
                
            // Return namespace equality check eventData
            return res2;
        }

        /// <summary>
        /// Returns text representation of the rule.
        /// </summary>
        /// <returns>Text representation of the rule.</returns>
        public override string ToString()
        {
            // No namespace specified...
            if (this.namespaceUri == null)
                return this.NodeName;
            else
                return String.Format("{0}:{1}", this.namespaceUri, this.nodeName);
        }
        
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of use and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Tomas Deml

Czech Republic Czech Republic
I'm a student of the Low-voltage Electrical Engineering specialized on Computing from the Czech republic.
 
I'm a C# kind of guy, fan of .NET.
 
I've formed a programming group called 'Comnicate!'. Currently the only member of the group is myself. Wink | ;-)

| Advertise | Privacy | Mobile
Web03 | 2.8.140415.2 | Last Updated 18 Oct 2005
Article Copyright 2005 by Tomas Deml
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid