public static CWNode SearchNodes(this IEnumerable <CWNode> nodes, NodeSearchCriteria nodeSearchCriteria)
        {
            foreach (var cwNode in nodes)
            {
                var searchNodeResult = SearchNodes(cwNode, nodeSearchCriteria);
                if (searchNodeResult != null)
                {
                    return(searchNodeResult);
                }
            }

            return(null);
        }
        /// <summary>
        /// Finds a node in this CWNode and all of its child CWNodes that matches the specified <see cref="NodeSearchCriteria"/>.  It will return the first Node that matches, and what consitutes "first" is undetermined.
        /// </summary>
        /// <param name="nodeSearchCriteria">The criteria.  Null fields are ignored in searching.  If multiple criteria are specified then the match returns the first node that matches any of them</param>
        /// <returns>The found node, or <c>null</c> if none could be found</returns>
        public static CWNode SearchNodes(this CWNode node, NodeSearchCriteria nodeSearchCriteria)
        {
            // test if our node matches the criteria
            if (nodeSearchCriteria.NodeKey != null)
            {
                if (node.Key.Equals(nodeSearchCriteria.NodeKey, StringComparison.InvariantCultureIgnoreCase))
                {
                    return(node);
                }
            }

            if (nodeSearchCriteria.Value != null)
            {
                if (node.Values.Contains(nodeSearchCriteria.Value, StringComparer.InvariantCultureIgnoreCase))
                {
                    return(node);
                }
            }

            if (nodeSearchCriteria.KeyValue != null)
            {
                var criteriaKeyValue = nodeSearchCriteria.KeyValue.Value;
                if (criteriaKeyValue.Key == null)
                {
                    if (nodeSearchCriteria.SearchForKVPAgainstRawValues)
                    {
                        if (node.RawKeyValues.Any(kvp => kvp.Value.Equals(criteriaKeyValue.Value, StringComparison.InvariantCultureIgnoreCase)))
                        {
                            return(node);
                        }
                    }

                    if (nodeSearchCriteria.SearchForKVPAgainstSubstitutedValues)
                    {
                        if (node.KeyValues.Any(kvp => kvp.Value.Equals(criteriaKeyValue.Value, StringComparison.InvariantCultureIgnoreCase)))
                        {
                            return(node);
                        }
                    }
                }
                else if (criteriaKeyValue.Value == null)
                {
                    if (nodeSearchCriteria.SearchForKVPAgainstRawValues)
                    {
                        if (node.RawKeyValues.Any(kvp => kvp.Key.Equals(criteriaKeyValue.Key, StringComparison.InvariantCultureIgnoreCase)))
                        {
                            return(node);
                        }
                    }

                    if (nodeSearchCriteria.SearchForKVPAgainstSubstitutedValues)
                    {
                        if (node.KeyValues.Any(kvp => kvp.Key.Equals(criteriaKeyValue.Key, StringComparison.InvariantCultureIgnoreCase)))
                        {
                            return(node);
                        }
                    }
                }
                else
                {
                    if (nodeSearchCriteria.SearchForKVPAgainstRawValues)
                    {
                        if (node.RawKeyValues.Any(kvp => kvp.Equals(criteriaKeyValue, StringComparison.InvariantCultureIgnoreCase)))
                        {
                            return(node);
                        }
                    }

                    if (nodeSearchCriteria.SearchForKVPAgainstSubstitutedValues)
                    {
                        if (node.KeyValues.Cast <CWNodeContextedKeyValue>().Any(kvp => kvp.Equals(criteriaKeyValue, StringComparison.InvariantCultureIgnoreCase)))
                        {
                            return(node);
                        }
                    }
                }
            }

            // otherwise DFS down the children
            return(node.Nodes.Select(cwNode => cwNode.SearchNodes(nodeSearchCriteria)).FirstOrDefault(childResult => childResult != null));
        }