/// <summary>
        /// Reads the configuration.
        /// </summary>
        /// <param name="dependenciesFile">The configuration file.</param>
        /// <param name="pathMapper">The path mapper.</param>
        /// <param name="settings">The settings.</param>
        /// <param name="context">The context.</param>
        /// <returns>
        /// Returns the dependencies read from the file.
        /// </returns>
        /// <exception cref="NAMEException">An unhandled exception happened.</exception>
        /// <exception cref="System.IO.FileNotFoundException">The configuration file was not found.</exception>
        public static ParsedDependencies ReadDependencies(string dependenciesFile, IFilePathMapper pathMapper, NAMESettings settings, NAMEContext context)
        {
            Guard.NotNull(settings, nameof(settings));
            if (context == null)
            {
                context = new NAMEContext();
            }

            string jsonContents = ReadJsonContents(pathMapper.MapPath(dependenciesFile));

            return(ParseDependenciesFromString(jsonContents, pathMapper, settings, context));
        }
        /// <summary>
        /// Reads the NAME settings overrides.
        /// </summary>
        /// <param name="settingsFile">The settings file.</param>
        /// <param name="pathMapper">The path mapper.</param>
        /// <returns>
        /// Returns the <see cref="NAMESettings" />.
        /// </returns>
        public static NAMESettings ReadNAMESettingsOverrides(string settingsFile, IFilePathMapper pathMapper)
        {
            Guard.NotNull(settingsFile, nameof(settingsFile));

            var jsonContents = ReadJsonContents(pathMapper.MapPath(settingsFile));

            JsonNode rootNode     = Json.Json.Parse(jsonContents);
            var      overrideNode = rootNode["Overrides"];

            var settings = new NAMESettings();

            if (overrideNode == null)
            {
                return(settings);
            }

            var registryEndpoints = overrideNode[nameof(settings.RegistryEndpoints)]?.AsArray;

            if (registryEndpoints != null)
            {
                settings.RegistryEndpoints = new string[registryEndpoints.Count];
                for (int i = 0; i < registryEndpoints.Count; i++)
                {
                    settings.RegistryEndpoints[i] = registryEndpoints[i].Value;
                }
            }

            var selfhostPortRangeFirst = overrideNode[nameof(settings.SelfHostPortRangeFirst)]?.AsInt;

            if (selfhostPortRangeFirst != null)
            {
                settings.SelfHostPortRangeFirst = selfhostPortRangeFirst.Value;
            }

            var selfhostPortRangeLast = overrideNode[nameof(settings.SelfHostPortRangeLast)]?.AsInt;

            if (selfhostPortRangeLast != null)
            {
                settings.SelfHostPortRangeLast = selfhostPortRangeLast.Value;
            }

            var serviceDependencyMaxHops = overrideNode[nameof(settings.ServiceDependencyMaxHops)]?.AsInt;

            if (serviceDependencyMaxHops != null)
            {
                settings.ServiceDependencyMaxHops = serviceDependencyMaxHops.Value;
            }

            var serviceDependencyShowConnectionString = overrideNode[nameof(settings.ConnectedDependencyShowConnectionString)]?.AsBool;

            if (serviceDependencyShowConnectionString != null)
            {
                settings.ConnectedDependencyShowConnectionString = serviceDependencyShowConnectionString.Value;
            }

            var dependencyConnectTimeout = overrideNode[nameof(settings.DependencyConnectTimeout)]?.AsInt;

            if (dependencyConnectTimeout != null)
            {
                settings.DependencyConnectTimeout = dependencyConnectTimeout.Value;
            }

            var dependencyReadWriteTimeout = overrideNode[nameof(settings.DependencyReadWriteTimeout)]?.AsInt;

            if (dependencyReadWriteTimeout != null)
            {
                settings.DependencyReadWriteTimeout = dependencyReadWriteTimeout.Value;
            }

            var registryReAnnounceFrequency = overrideNode[nameof(settings.RegistryReAnnounceFrequency)];

            if (registryReAnnounceFrequency != null && TimeSpan.TryParse(registryReAnnounceFrequency, out TimeSpan parsedAnnounceFreq))
            {
                settings.RegistryReAnnounceFrequency = parsedAnnounceFreq;
            }

            var registryPingFrequency = overrideNode[nameof(settings.RegistryPingFrequency)];

            if (registryPingFrequency != null && TimeSpan.TryParse(registryPingFrequency, out TimeSpan parsedPingFreq))
            {
                settings.RegistryPingFrequency = parsedPingFreq;
            }

            var runningMode = overrideNode[nameof(settings.RunningMode)];

            if (runningMode != null && Enum.TryParse <SupportedNAMEBehaviours>(runningMode.Value.ToString(), false, out var behaviour))
            {
                settings.RunningMode = behaviour;
            }

            var registryBootstrapRetryFrequency = overrideNode[nameof(settings.RegistryBootstrapRetryFrequency)];

            if (registryBootstrapRetryFrequency != null && TimeSpan.TryParse(registryBootstrapRetryFrequency, out TimeSpan bootstrapRetryFreq))
            {
                settings.RegistryBootstrapRetryFrequency = bootstrapRetryFreq;
            }

            var registryBootstrapConnectTimeout = overrideNode[nameof(settings.RegistryBootstrapTimeout)];

            if (registryBootstrapConnectTimeout != null && TimeSpan.TryParse(registryBootstrapConnectTimeout, out TimeSpan bootstrapConnectTimeout))
            {
                settings.RegistryBootstrapTimeout = bootstrapConnectTimeout;
            }

            return(settings);
        }
        private static IConnectionStringProvider ParseConnectionStringProvider(JsonNode node, IFilePathMapper pathMapper)
        {
            if (node.AsObject == null)
            {
                return(new StaticConnectionStringProvider(node.Value));
            }

            JsonClass objClass = node.AsObject;
            SupportedConnectionStringLocators locator;

            if (!Enum.TryParse(node["locator"]?.Value, out locator))
            {
                throw new NAMEException($"The locator {node["locator"]?.Value} is not supported.");
            }
            IConnectionStringProvider provider = null;
            string key;

            switch (locator)
            {
#if NET45
            case SupportedConnectionStringLocators.ConnectionStrings:
                key      = node["key"]?.Value;
                provider = new ConnectionStringsConnectionStringProvider(key);
                break;

            case SupportedConnectionStringLocators.AppSettings:
                key      = node["key"]?.Value;
                provider = new AppSettingsConnectionStringProvider(key);
                break;

            case SupportedConnectionStringLocators.VSSettingsFile:
                key = node["key"]?.Value;
                string section = node["section"]?.Value;
                if (string.IsNullOrEmpty(section))
                {
                    throw new ArgumentNullException("section", "The section must be specified.");
                }
                provider = new VisualStudioSetingsFileConnectionStringProvider(section, key);
                break;
#endif
            case SupportedConnectionStringLocators.JSONPath:
            {
                key = node["expression"]?.Value;
                string file = node["file"]?.Value;
                if (string.IsNullOrEmpty(file))
                {
                    throw new ArgumentNullException("file", "The file must be specified.");
                }
                provider = new JsonPathConnectionStringProvider(pathMapper.MapPath(file), key);
            }
            break;

            case SupportedConnectionStringLocators.XPath:
            {
                key = node["expression"]?.Value;
                string file = node["file"]?.Value;
                if (string.IsNullOrEmpty(file))
                {
                    throw new ArgumentNullException("file", "The file must be specified.");
                }
                provider = new XpathConnectionStringProvider(pathMapper.MapPath(file), key);
            }
            break;

            default:
                throw new NAMEException($"The locator {locator.ToString()} is not supported..");
            }

            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException($"The connection string key/expression must be specified.");
            }

            return(provider);
        }