private void CheckNeedsRecursive(NodeStack nodeStack, UrlDir.UrlConfig urlConfig)
        {
            ConfigNode original = nodeStack.value;

            for (int i = 0; i < original.values.Count; ++i)
            {
                ConfigNode.Value val     = original.values[i];
                string           valname = val.name;
                try
                {
                    if (CheckNeedsName(ref valname))
                    {
                        val.name = valname;
                    }
                    else
                    {
                        original.values.Remove(val);
                        i--;
                        progress.NeedsUnsatisfiedValue(urlConfig, nodeStack.GetPath() + '/' + val.name);
                    }
                }
                catch (ArgumentOutOfRangeException e)
                {
                    progress.Exception("ArgumentOutOfRangeException in CheckNeeds for value \"" + val.name + "\"", e);
                    throw;
                }
                catch (Exception e)
                {
                    progress.Exception("General Exception in CheckNeeds for value \"" + val.name + "\"", e);
                    throw;
                }
            }

            for (int i = 0; i < original.nodes.Count; ++i)
            {
                ConfigNode node     = original.nodes[i];
                string     nodeName = node.name;

                if (nodeName == null)
                {
                    progress.Error(urlConfig, "Error - Node in file " + urlConfig.SafeUrl() + " subnode: " + nodeStack.GetPath() + " has config.name == null");
                }

                try
                {
                    if (CheckNeedsName(ref nodeName))
                    {
                        node.name = nodeName;
                        CheckNeedsRecursive(nodeStack.Push(node), urlConfig);
                    }
                    else
                    {
                        original.nodes.Remove(node);
                        i--;
                        progress.NeedsUnsatisfiedNode(urlConfig, nodeStack.GetPath() + '/' + node.name);
                    }
                }
                catch (ArgumentOutOfRangeException e)
                {
                    progress.Exception("ArgumentOutOfRangeException in CheckNeeds for node \"" + node.name + "\"", e);
                    throw;
                }
                catch (Exception e)
                {
                    progress.Exception("General Exception " + e.GetType().Name + " for node \"" + node.name + "\"", e);
                    throw;
                }
            }
        }
        public void TestCheckNeedsRecursive()
        {
            ConfigNode node = new TestConfigNode("SOME_NODE")
            {
                { "aa", "00" },
                { "bb:NEEDS[mod1]", "01" },
                { "cc:NEEDS[mod3]", "02" },
                new TestConfigNode("INNER_NODE_1")
                {
                    { "dd", "03" },
                    { "ee", "04" },
                    new TestConfigNode("INNER_INNER_NODE_1")
                    {
                        { "ff", "05" },
                    },
                },
                new TestConfigNode("INNER_NODE_2")
                {
                    { "gg:NEEDS[mod1]", "06" },
                    { "hh:NEEDS[mod3]", "07" },
                    { "ii", "08" },
                    new TestConfigNode("INNER_INNER_NODE_11")
                    {
                        { "jj", "09" },
                    },
                    new TestConfigNode("INNER_INNER_NODE_12:NEEDS[mod2]")
                    {
                        { "kk", "10" },
                    },
                    new TestConfigNode("INNER_INNER_NODE_12:NEEDS[mod3]")
                    {
                        { "ll", "11" },
                    },
                },
                new TestConfigNode("INNER_NODE_3:NEEDS[mod1]")
                {
                    { "mm:NEEDS[mod1]", "12" },
                    { "nn:NEEDS[mod3]", "13" },
                    { "oo", "14" },
                    new TestConfigNode("INNER_INNER_NODE_21")
                    {
                        { "pp", "15" },
                    },
                    new TestConfigNode("INNER_INNER_NODE_22:NEEDS[mod2]")
                    {
                        { "qq", "16" },
                    },
                    new TestConfigNode("INNER_INNER_NODE_22:NEEDS[mod3]")
                    {
                        { "rr", "17" },
                    },
                },
                new TestConfigNode("INNER_NODE_4:NEEDS[mod3]")
                {
                    { "ss:NEEDS[mod1]", "18" },
                },
            };

            UrlDir.UrlConfig urlConfig = UrlBuilder.CreateConfig("abc/def", node);

            needsChecker.CheckNeedsRecursive(node, urlConfig);

            progress.DidNotReceiveWithAnyArgs().Warning(null, null);
            progress.DidNotReceiveWithAnyArgs().Error(null, null);
            progress.DidNotReceiveWithAnyArgs().Exception(null, null);
            progress.DidNotReceiveWithAnyArgs().Exception(null, null, null);

            Received.InOrder(delegate
            {
                progress.NeedsUnsatisfiedValue(urlConfig, "SOME_NODE/cc:NEEDS[mod3]");
                progress.NeedsUnsatisfiedValue(urlConfig, "SOME_NODE/INNER_NODE_2/hh:NEEDS[mod3]");
                progress.NeedsUnsatisfiedNode(urlConfig, "SOME_NODE/INNER_NODE_2/INNER_INNER_NODE_12:NEEDS[mod3]");
                progress.NeedsUnsatisfiedValue(urlConfig, "SOME_NODE/INNER_NODE_3/nn:NEEDS[mod3]");
                progress.NeedsUnsatisfiedNode(urlConfig, "SOME_NODE/INNER_NODE_3/INNER_INNER_NODE_22:NEEDS[mod3]");
                progress.NeedsUnsatisfiedNode(urlConfig, "SOME_NODE/INNER_NODE_4:NEEDS[mod3]");
            });

            Assert.Equal(2, node.values.Count);
            Assert.Equal(3, node.nodes.Count);

            Assert.Equal("aa", node.values[0].name);
            Assert.Equal("00", node.values[0].value);

            Assert.Equal("bb", node.values[1].name);
            Assert.Equal("01", node.values[1].value);

            Assert.Same(node.nodes[0], node.nodes[0]);
            Assert.Equal("INNER_NODE_1", node.nodes[0].name);

            Assert.Equal(2, node.nodes[0].values.Count);
            Assert.Equal(1, node.nodes[0].nodes.Count);

            Assert.Equal("dd", node.nodes[0].values[0].name);
            Assert.Equal("03", node.nodes[0].values[0].value);

            Assert.Equal("ee", node.nodes[0].values[1].name);
            Assert.Equal("04", node.nodes[0].values[1].value);

            Assert.Equal("INNER_INNER_NODE_1", node.nodes[0].nodes[0].name);

            Assert.Equal(1, node.nodes[0].nodes[0].values.Count);
            Assert.Equal(0, node.nodes[0].nodes[0].nodes.Count);

            Assert.Equal("ff", node.nodes[0].nodes[0].values[0].name);
            Assert.Equal("05", node.nodes[0].nodes[0].values[0].value);

            // Assert.NotSame(node.nodes[1], newNode.nodes[1]);
            Assert.Equal("INNER_NODE_2", node.nodes[1].name);

            Assert.Equal(2, node.nodes[1].values.Count);
            Assert.Equal(2, node.nodes[1].nodes.Count);

            Assert.Equal("gg", node.nodes[1].values[0].name);
            Assert.Equal("06", node.nodes[1].values[0].value);

            Assert.Equal("ii", node.nodes[1].values[1].name);
            Assert.Equal("08", node.nodes[1].values[1].value);

            Assert.Equal("INNER_INNER_NODE_11", node.nodes[1].nodes[0].name);

            Assert.Equal("jj", node.nodes[1].nodes[0].values[0].name);
            Assert.Equal("09", node.nodes[1].nodes[0].values[0].value);

            Assert.Equal("INNER_INNER_NODE_12", node.nodes[1].nodes[1].name);

            Assert.Equal("kk", node.nodes[1].nodes[1].values[0].name);
            Assert.Equal("10", node.nodes[1].nodes[1].values[0].value);

            // Assert.NotSame(node.nodes[1], newNode.nodes[1]);
            Assert.Equal("INNER_NODE_3", node.nodes[2].name);

            Assert.Equal(2, node.nodes[2].values.Count);
            Assert.Equal(2, node.nodes[2].nodes.Count);

            Assert.Equal("mm", node.nodes[2].values[0].name);
            Assert.Equal("12", node.nodes[2].values[0].value);

            Assert.Equal("oo", node.nodes[2].values[1].name);
            Assert.Equal("14", node.nodes[2].values[1].value);

            Assert.Equal("INNER_INNER_NODE_21", node.nodes[2].nodes[0].name);

            Assert.Equal("pp", node.nodes[2].nodes[0].values[0].name);
            Assert.Equal("15", node.nodes[2].nodes[0].values[0].value);

            Assert.Equal("INNER_INNER_NODE_22", node.nodes[2].nodes[1].name);

            Assert.Equal("qq", node.nodes[2].nodes[1].values[0].name);
            Assert.Equal("16", node.nodes[2].nodes[1].values[0].value);
        }