/// <summary> /// Register a custom option parser. /// </summary> /// <param name="parser">Registers the parser, if the associated type does not already have a parser registered.</param> /// <param name="replace">If set to true, the parser will be registered in place of an existing parser for its type.</param> public static void RegisterParser(ParserBase parser, bool replace = false) { if (replace || !Parsers.ContainsKey(parser.Type)) { Parsers[parser.Type] = parser; } }
/// <summary> /// Converts a config file text to the a config object of the specified type. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="lines">The lines of the config file to be converted</param> /// <returns>The converted config object</returns> public static T Parse <T>(IEnumerable <string> lines) where T : new() { T config = (T)Activator.CreateInstance(typeof(T)); foreach (string line in lines) { Match match = Regex.Match(line, $"^<{_allowedCharacters}>\\[{_allowedCharacters}\\]:"); if (match.Success) { string optionString = match.Value; string valueString = line.Substring(optionString.Length); string typeTag = Regex.Match(optionString, $"<{_allowedCharacters}>").Value; typeTag = typeTag.Substring(1, typeTag.Length - 2); string optionName = Regex.Match(optionString, $"\\[{_allowedCharacters}\\]").Value; optionName = optionName.Substring(1, optionName.Length - 2); bool successflag = false; foreach (PropertyInfo property in typeof(T).GetProperties()) { ParserBase parser = null; foreach (ParserBase p in Parsers.Values) { if (p.Tag.Equals(typeTag)) { parser = p; break; } } if (parser == null) { throw new ConfigException($"No parser found with tag '{typeTag}'"); } object[] attr = property.GetCustomAttributes(typeof(Option), true); Option optionAttribute = attr.Length > 0 ? (Option)attr[0] : null; //if ((optionAttribute != null && ((optionAttribute.Name != null && optionName.Equals(optionAttribute.Name)) || )) || (configAttribute.UseAllProperties && property.Name.Equals(optionName))) if (optionAttribute != null) { if (!string.IsNullOrEmpty(optionAttribute.Name)) { if (optionAttribute.Name.Equals(optionName)) { property.SetValue(config, parser.Decode(valueString.Trim())); successflag = true; break; } } else { if (property.Name.Equals(optionName)) { property.SetValue(config, parser.Decode(valueString.Trim())); successflag = true; break; } } } else { if (property.Name.Equals(optionName)) { property.SetValue(config, parser.Decode(valueString.Trim())); successflag = true; break; } } } if (!successflag) { throw new ConfigException($"Failed to map option {optionName} to a property of {typeof(T)}"); } } } return(config); }
/// <summary> /// Serializes a config /// </summary> /// <typeparam name="T"></typeparam> /// <param name="config">The config object to serialize</param> /// <returns>An array of lines that make up the config file text</returns> public static string[] Serialize <T>(T config) { Config configAttribute = GetAttribute <T>(); //Get options List <string> regions = new List <string> { "" }; List <KeyValuePair <PropertyInfo, Option> > options = new List <KeyValuePair <PropertyInfo, Option> >(); foreach (PropertyInfo member in typeof(T).GetProperties()) { object[] attr = member.GetCustomAttributes(typeof(Option), true); Option optionAttribute = attr.Length > 0 ? (Option)attr[0] : null; if (optionAttribute != null || configAttribute.UseAllProperties) { options.Add(new KeyValuePair <PropertyInfo, Option>(member, (optionAttribute == null ? new Option() : optionAttribute))); if (optionAttribute != null && !regions.Contains(optionAttribute.Region)) { regions.Add(optionAttribute.Region); } } } options.Sort((a, b) => { if (a.Value.Region.CompareTo(b.Value.Region) != 0) { return(a.Value.Region.CompareTo(b.Value.Region)); } else { string aName = string.IsNullOrEmpty(a.Key.Name) ? a.Key.Name : a.Key.Name; string bName = string.IsNullOrEmpty(b.Key.Name) ? b.Key.Name : b.Key.Name; return(aName.CompareTo(bName)); } }); //Get comments List <KeyValuePair <string, string> > comments = new List <KeyValuePair <string, string> >(); foreach (object attr in typeof(T).GetCustomAttributes(typeof(Comment), true)) { Comment comment = (Comment)attr; comments.Add(new KeyValuePair <string, string>(comment.Region, comment.Text)); if (!regions.Contains(comment.Region)) { regions.Contains(comment.Region); } } comments.Sort((a, b) => { return(a.Key.CompareTo(b.Key)); }); regions.Sort(); //Write content List <string> lines = new List <string>(); if (!string.IsNullOrEmpty(configAttribute.Header)) { Writer.Header(lines, configAttribute.Header); } foreach (string r in regions) { if (!string.IsNullOrEmpty(r)) { Writer.Region(lines, r); } foreach (var comment in comments.Where(c => c.Key.Equals(r))) { Writer.Comment(lines, comment.Value); } foreach (var option in options.Where(o => o.Value.Region.Equals(r))) { if (!string.IsNullOrEmpty(option.Value.Comment)) { Writer.Comment(lines, option.Value.Comment); } Type type = option.Key.PropertyType; string name = string.IsNullOrEmpty(option.Value.Name) ? option.Key.Name : option.Value.Name; if (!Parsers.ContainsKey(type)) { throw new ConfigException($"No parser registered for type {type}!"); } ParserBase parser = Parsers[type]; if (!Regex.IsMatch(name, $"^{_allowedCharacters}$")) { throw new ConfigException($"The specified option name ({name}) is invalid."); } if (!Regex.IsMatch(parser.Tag, $"^{_allowedCharacters}$")) { throw new ConfigException($"The supplied parser for type {type} has an invalid tag: '{parser.Tag}' contains illegal characters."); } string value = parser.Encode(option.Key.GetValue(config)); lines.Add($"<{parser.Tag}>[{name}]: {value}"); } } return(lines.ToArray()); }