// // ConfigurationBase.cs // // Author: // Carsten Sonne Larsen // // Copyright (c) 2013-2016 Carsten Sonne Larsen // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Text; namespace Ntp.Config { public abstract class ConfigurationBase : IEnumerable { private readonly string configName; private readonly int indentLevel; protected ConfigurationBase(string configName, int indentLevel) { this.configName = configName; this.indentLevel = indentLevel; } /// /// Gets the name of this configuration config. /// /// The name of the configuration. public string ConfigName { get { return configName; } } protected int IndentLevel { get { return indentLevel; } } #region IEnumerable protected abstract IEnumerable Items { get; } IEnumerator IEnumerable.GetEnumerator() { return Items.GetEnumerator(); } #endregion #region Error Handling private static StringBuilder errors = new StringBuilder(); protected int IndentChars = 4; public static string Errors { get { return errors.ToString(); } } internal static void ClearErrors() { errors = new StringBuilder(); } protected string ConfigText(string[] settings, IEnumerable values) { var valueList = new List(values); string indentHeader = String.Empty.PadLeft(IndentLevel*IndentChars, ' '); string indentContent = String.Empty.PadLeft((IndentLevel + 1)*IndentChars, ' '); var text = new StringBuilder(); text.AppendLine(); text.Append(indentHeader); text.AppendFormat("{0} {1}", settings[0], ConfigName); text.Append(ConfigName != String.Empty ? " {" : "{"); text.AppendLine(); for (int i = 1; i < settings.Length; i++) { if (valueList[i] == null || valueList[i].ToString() == String.Empty) continue; if (valueList[i] is IList) { var collection = valueList[i] as IEnumerable; foreach (object item in collection) { text.Append(item); } } else if (valueList[i] is ConfigurationBase) { text.Append(valueList[i]); } else { object value = valueList [i]; if (value is bool) { value = (int)((bool)value ? (int)1 : (int)0); } text.Append(indentContent); text.AppendFormat("{0} {1}", settings[i], value); text.AppendLine(); } } text.Append(indentHeader); text.AppendLine("}"); return text.ToString(); } protected static void ConfigError(string errorText) { errors.AppendLine(errorText); } #endregion protected static ConfigurationBlock FindSingleBlock(string name, ConfigurationBlock block) { return FindSingleBlock(name, block.Lines, block.IndentLevel + 1); } protected static ConfigurationBlock FindSingleBlock(string name, string[] lines, int indentLevel, int start = 0) { bool found = false; while (!found) { start = FindLine(start, lines, '{'); if (start == -1) return null; found = lines[start].Trim().StartsWith(name + " "); } int end = FindLine(start, lines, '}', '{'); // No ending symbol if (end == -1) { ConfigError("End of block not found in " + name); return null; } var block = new List(end - start + 1); for (int c = start; c < end + 1; c++) block.Add(lines[c]); return new ConfigurationBlock(start, block.ToArray(), indentLevel); } protected static ConfigurationBlock[] FindBlocks(string name, ConfigurationBlock block) { return FindBlocks(name, block.Lines, block.IndentLevel); } protected static ConfigurationBlock[] FindBlocks(string name, string[] lines, int indentLevel) { int start = 0; var blocks = new List(); ConfigurationBlock block; do { block = FindSingleBlock(name, lines, indentLevel + 1, start); if (block != null) { blocks.Add(block); start = block.Offset; } } while (block != null); return blocks.ToArray(); } private static int FindLine(int startLine, string[] lines, char searchChar, char indentChar = (char) 0) { if (startLine >= lines.Length - 1) return -1; bool found = false; int i = startLine; int indent = 1; do { i++; if (indentChar != (char) 0 && lines[i].IndexOf(indentChar) != -1) indent++; else if (lines[i].IndexOf(searchChar) != -1) { indent--; found = true; } } while ((!found || indent != 0) && i < lines.Length - 1); if (i == lines.Length - 1 && (!found || indent != 0)) return -1; return i; } protected static OptionSet GetOptions(IEnumerable options, IEnumerable lines) { if (options == null || lines == null) return null; OptionSet result = new OptionSet(); foreach (string option in options) result.Add(option, String.Empty); foreach (string optionLine in lines) { string line = optionLine.Trim(); if (line.StartsWith("#")) continue; int pos = line.IndexOf(' '); if (pos == -1) continue; string value = line.Substring(pos, line.Length - pos).Trim(); foreach (string option in options) { if (line.StartsWith(option + " ")) { result[option] = value; break; } } } return result; } protected static IEnumerable GetMultiOption(string option, ConfigurationBlock block) { return GetMultiOption(option, block.Lines); } protected static List GetMultiOption(string option, string[] lines) { var result = new List(); foreach (string line in lines) { string currentLine = line.Trim (); if (!currentLine.StartsWith(option)) continue; int pos = currentLine.IndexOf(' '); if (pos == -1) continue; if (currentLine.Substring(0, pos).Trim() != option) continue; string value = currentLine.Substring(pos, currentLine.Length - pos).Trim(); result.Add(value); } return result; } protected static string GetOptionConfigName( Dictionary options, string option, string configPath, bool mandatory, string defaultValue = null) { string value = GetOptionString(options, option, configPath, mandatory, "DEFAULT CONFIG"); return value.Replace("{", String.Empty).Trim(); } protected static string GetOptionString( Dictionary options, string option, string configPath, bool mandatory, string defaultValue = null) { if (options.ContainsKey (option) && options [option] != String.Empty) { return options [option]; } if (mandatory) { ConfigError (String.Format ( "No value for setting {0} in section {1} was specified.", option, configPath)); } return defaultValue ?? String.Empty; } protected static Int32 GetOptionInteger( Dictionary options, string option, string configPath, bool mandatory, int defaultValue = 0) { if (!options.ContainsKey (option) || options [option].Trim () == String.Empty) { if (mandatory) { ConfigError (String.Format ( "No value for setting {0} in section {1} was specified.", option, configPath)); } return defaultValue; } int ivalue; if (!Int32.TryParse (options [option].Trim (), out ivalue)) { ivalue = defaultValue; ConfigError (String.Format ( "Invalid integer value for setting {0} in section {1}.", option, configPath)); } return ivalue; } protected static Boolean GetOptionBoolean( Dictionary options, string option, string configPath, bool mandatory, bool defaultValue = false) { if (!options.ContainsKey (option) || options [option].Trim () == String.Empty) { if (mandatory) { ConfigError (String.Format ( "No value for setting {0} in section {1} was specified.", option, configPath)); } return defaultValue; } string value = options [option].Trim (); if (value.ToLower() == "true" || value == "1") return true; if (value.ToLower() == "false" || value == "0") return false; ConfigError (String.Format ( "Invalid boolean value for setting {0} in section {1}.", option, configPath)); return false; } protected static Double GetOptionDouble( Dictionary options, string option, string configPath, bool mandatory, double defaultValue = 0.0) { if (!options.ContainsKey (option) || options [option].Trim () == String.Empty) { if (mandatory) { ConfigError (String.Format ( "No value for setting {0} in section {1} was specified.", option, configPath)); } return defaultValue; } double dvalue; if (!Double.TryParse (options [option].Trim (), out dvalue)) { dvalue = defaultValue; ConfigError (String.Format ( "Invalid numeric value for setting {0} in section {1}.", option, configPath)); } return dvalue; } } }