This is the fourth part of the SQLxD series. For your convenience you can find other parts in the table of contents in Part 1 – XML Transformation

Now we can implement classes representing multi-part identifiers. We start with the following:

using System.Collections.Generic;
using Model;

namespace QueryLogic.Selectors
{
    public interface ISelector
    {
        IEnumerable Select(Node parent);
    }
}

This selector providers us an abstraction of selecting nodes from root. We start with top level selector which extracts element only if its name is as specified:

using System.Collections.Generic;
using Model;

namespace QueryLogic.Selectors
{
    public class TopLevelSelector : ISelector
    {
        public TopLevelSelector(string name)
        {
            Name = name;
        }

        public string Name { get; private set; }

        public IEnumerable Select(Node parent)
        {
            if (parent.Name == Name)
            {
                yield return parent;
            }
        }

        protected bool Equals(TopLevelSelector other)
        {
            return string.Equals(Name, other.Name);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != GetType()) return false;
            return Equals((TopLevelSelector)obj);
        }

        public override int GetHashCode()
        {
            return (Name != null ? Name.GetHashCode() : 0);
        }
    }
}

Next, we can go one level deeper:

using System.Collections.Generic;
using Model;

namespace QueryLogic.Selectors
{
    public class LevelSelector : ISelector
    {
        public IEnumerable Select(Node parent)
        {
            return parent.InnerNodes;
        }

        protected bool Equals(LevelSelector other)
        {
            return true;
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != GetType()) return false;
            return Equals((LevelSelector)obj);
        }

        public override int GetHashCode()
        {
            return 0;
        }
    }
}

We can also extract any children with specified name:

using System.Collections.Generic;
using System.Linq;
using Model;

namespace QueryLogic.Selectors
{
    public class NodeSelector : ISelector
    {
        public NodeSelector(string name)
        {
            Name = name;
        }

        public string Name { get; private set; }

        public IEnumerable Select(Node parent)
        {
            return parent.InnerNodes.Where(node => node.Type == NodeType.Element && node.Name == Name);
        }

        protected bool Equals(NodeSelector other)
        {
            return string.Equals(Name, other.Name);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != GetType()) return false;
            return Equals((NodeSelector)obj);
        }

        public override int GetHashCode()
        {
            return (Name != null ? Name.GetHashCode() : 0);
        }
    }
}

Finally, we can recursively use selector for all children:

using System.Collections.Generic;
using System.Linq;
using Model;

namespace QueryLogic.Selectors
{
    public class AnySelector : ISelector
    {
        public IEnumerable Select(Node parent)
        {
            return parent.InnerNodes.Concat(parent.InnerNodes.SelectMany(Select));
        }

        protected bool Equals(AnySelector other)
        {
            return true;
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != GetType()) return false;
            return Equals((AnySelector)obj);
        }

        public override int GetHashCode()
        {
            return 0;
        }
    }
}

This is great, we also need chain of selectors:

using System.Collections.Generic;
using System.Linq;
using Model;

namespace QueryLogic.Selectors
{
    public class ChainedSelector : ISelector
    {
        public ChainedSelector(ISelector first, ISelector second)
        {
            First = first;
            Second = second;
        }

        public ISelector First { get; private set; }
        public ISelector Second { get; private set; }

        public IEnumerable Select(Node parent)
        {
            IEnumerable result = First.Select(parent);
            IList listResult = result as IList ?? result.ToList();
            return listResult.Any() ? listResult.SelectMany(node => Second.Select(node)) : listResult;
        }

        protected bool Equals(ChainedSelector other)
        {
            return Equals(First, other.First) && Equals(Second, other.Second);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != GetType()) return false;
            return Equals((ChainedSelector)obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return ((First != null ? First.GetHashCode() : 0) * 397) ^ (Second != null ? Second.GetHashCode() : 0);
            }
        }
    }
}

And we are done. Now if we want to extract SELECT * FROM table we use the new TopLevelSelector("table"); selector, if we want to represent t.table2, we use new ChainedSelector(new TopLevelSelector("table"), new NodeSelector("table2"));.