Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.IO;
- using System.Reflection;
- using System.Runtime.Serialization;
- using System.Collections;
- using System.Text;
- using System.Collections.Generic;
- namespace HumbleAPI
- {
- /// <summary>
- /// (De-)serializes objects into ini files
- /// </summary>
- public class IniFormatter : IFormatter
- {
- /// <summary>
- /// An entry in an array.
- /// Required to sort array entries if somebody shuffles them in the ini file
- /// </summary>
- private class ArrayEntry
- {
- /// <summary>
- /// Array index
- /// </summary>
- public int Index
- { get; private set; }
- /// <summary>
- /// Array value
- /// </summary>
- public string Value
- { get; private set; }
- /// <summary>
- /// Array name
- /// </summary>
- public string Name
- { get; private set; }
- /// <summary>
- /// Creates a new array entry
- /// </summary>
- /// <param name="Line">INI entry</param>
- public ArrayEntry(string Line)
- {
- string[] Parts = new string[]
- {
- Line.Substring(0,Line.IndexOf('=')),
- Line.Substring(Line.IndexOf('=')+1)
- };
- if (Parts.Length < 2)
- {
- throw new ArgumentException("Invalid INI file line");
- }
- Value = Parts[1];
- Name = Parts[0].Substring(0, Parts[0].IndexOf('['));
- //extracts the index
- Index = int.Parse(Parts[0].Substring(Parts[0].IndexOf('[') + 1,
- Parts[0].Length - Parts[0].IndexOf('[') - 2));
- }
- /// <summary>
- /// Converts the entry back to its ini line
- /// </summary>
- /// <returns>string</returns>
- public override string ToString()
- {
- return string.Format("{0}[{1}]={2}", Name, Index, Value);
- }
- }
- /// <summary>
- /// Possible types of lines in an ini file
- /// </summary>
- private enum LineType
- {
- /// <summary>
- /// A [Section]
- /// </summary>
- Section,
- /// <summary>
- /// A Property=Value
- /// </summary>
- Property,
- /// <summary>
- /// A #comment
- /// </summary>
- Comment,
- /// <summary>
- /// an empty line
- /// </summary>
- Empty,
- /// <summary>
- /// An invalid line
- /// </summary>
- Invalid
- }
- /// <summary>
- /// null reference string (not empty string)
- /// </summary>
- public const string NULL = "[null]";
- /// <summary>
- /// base64 encoded string
- /// </summary>
- public const string B64 = "B64:";
- /// <summary>
- /// array of values
- /// </summary>
- public const string ARRAY = "ARRAY:";
- public ISurrogateSelector SurrogateSelector
- { get; set; }
- public SerializationBinder Binder
- { get; set; }
- public StreamingContext Context
- { get;set; }
- /// <summary>
- /// chars that cause a string to get Base64 encoded
- /// </summary>
- private string[] replaceables = new string[]
- {
- @"\",
- "\0",
- "\r",
- "\n",
- "\t",
- };
- /// <summary>
- /// Creates a new IniFormatter
- /// </summary>
- public IniFormatter()
- {
- Context = new StreamingContext(StreamingContextStates.All);
- }
- /// <summary>
- /// Deserializes an object from an ini file
- /// </summary>
- /// <param name="serializationStream">Stream to ini data</param>
- /// <returns>deserialized object</returns>
- public object Deserialize(Stream serializationStream)
- {
- string[] Content;
- using (StreamReader SR = new StreamReader(serializationStream))
- {
- Content = SR.ReadToEnd().Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
- }
- return AssembleSection(Content[0].Substring(1, Content[0].Length - 2), Content);
- }
- /// <summary>
- /// Assembles a section into an object
- /// </summary>
- /// <param name="Section">Section name</param>
- /// <param name="Content">INI lines</param>
- /// <returns>assembled object</returns>
- private object AssembleSection(string Section, string[] Content)
- {
- string TypeName = extractType(Section);
- //get all values from that object
- string[] Values = GetSection(Section, Content);
- //get the type of the current object
- Type T = Type.GetType(TypeName);
- object obj = FormatterServices.GetUninitializedObject(T);
- MemberInfo[] MI = FormatterServices.GetSerializableMembers(T);
- object[] data = new object[MI.Length];
- Dictionary<string, string> sdict = new Dictionary<string, string>();
- //get all properties
- foreach (string Line in Values)
- {
- string[] Parts = new string[]
- {
- Line.Substring(0,Line.IndexOf('=')),
- Line.Substring(Line.IndexOf('=')+1)
- };
- //line is an array
- if (Parts[0].Contains("["))
- {
- //only process, if first array entry
- if (Parts[0].EndsWith("[0]"))
- {
- string Name = new ArrayEntry(Line).Name;
- sdict.Add(Name, ARRAY + string.Join("|", GetArrayLine(Name, Values)));
- }
- }
- else
- {
- sdict.Add(Parts[0], Parts[1]);
- }
- }
- for(int i=0;i<MI.Length;i++)
- {
- FieldInfo FI = (FieldInfo)MI[i];
- if (!sdict.ContainsKey(FI.Name))
- {
- throw new SerializationException("Missing field value: " + FI.Name);
- }
- if (FI.FieldType.IsArray)
- {
- object[] oArr=(object[])FromString(sdict[FI.Name], Content);
- if (oArr != null)
- {
- Array A = Array.CreateInstance(oArr[0].GetType(), oArr.Length);
- Array.Copy(oArr, A, A.Length);
- data[i] = A;
- }
- else
- {
- data[i] = null;
- }
- }
- else
- {
- data[i] = System.Convert.ChangeType(FromString(sdict[FI.Name], Content), FI.FieldType);
- }
- }
- return FormatterServices.PopulateObjectMembers(obj, MI, data);
- }
- /// <summary>
- /// Serializes data into an ini file
- /// </summary>
- /// <param name="serializationStream">INI output stream</param>
- /// <param name="graph">Object</param>
- public void Serialize(Stream serializationStream, object graph)
- {
- using (StreamWriter sw = new StreamWriter(serializationStream))
- {
- string s = Serialize(graph.GetType().FullName, graph);
- sw.WriteLine(s);
- sw.Flush();
- }
- }
- /// <summary>
- /// Serializes an object and its members into a section
- /// </summary>
- /// <param name="current">Current Section</param>
- /// <param name="graph">Object</param>
- /// <returns>string with content</returns>
- private string Serialize(string current, object graph)
- {
- StringBuilder Current = new StringBuilder();
- StringBuilder Postcontent = new StringBuilder();
- MemberInfo[] members = FormatterServices.GetSerializableMembers(graph.GetType(), Context);
- object[] objs = FormatterServices.GetObjectData(graph, members);
- Current.AppendFormat("[{0}]\r\n", current);
- for (int i = 0; i < objs.Length; ++i)
- {
- if (objs[i] == null)
- {
- Current.AppendFormat("{0}={1}\r\n", members[i].Name, NULL);
- }
- else
- {
- FieldInfo FI = (FieldInfo)members[i];
- if (!FI.FieldType.IsPrimitive && FI.FieldType.FullName != string.Empty.GetType().FullName)
- {
- //not a primitive type (and no string)
- //check if it is enumerable
- if (objs[i] is IEnumerable)
- {
- int j = 0;
- foreach (object o in ((IEnumerable)objs[i]))
- {
- if (o.GetType().IsPrimitive || FI.FieldType.FullName == string.Empty.GetType().FullName)
- {
- Current.AppendFormat("{0}[{1}]={2}\r\n", members[i].Name, j, o == null ? NULL : o.ToString());
- }
- else
- {
- Current.AppendFormat("{0}[{1}]=[{2}]\r\n", members[i].Name, j, string.Format("{0},{1}[{2}]", current, FI.FieldType.FullName, j));
- Postcontent.Append(Serialize(string.Format("{0},{1}[{2}]", current, FI.FieldType.FullName, j), o));
- }
- j++;
- }
- if (j == 0)
- {
- Current.AppendFormat("{0}={1}\r\n", members[i].Name, NULL);
- }
- }
- else
- {
- Current.AppendFormat("{0}=[{1},{2}]\r\n", members[i].Name, current, FI.FieldType.FullName);
- Postcontent.Append(Serialize(string.Format("{0},{1}", current, FI.FieldType.FullName), objs[i]));
- }
- }
- else
- {
- Current.AppendFormat("{0}={1}\r\n", members[i].Name, objs[i] == null ? NULL : objs[i].ToString());
- }
- }
- }
- return Current.ToString() + Postcontent.ToString();
- }
- /// <summary>
- /// Gets a section with all its values
- /// </summary>
- /// <param name="Section">Section</param>
- /// <param name="INI">INI lines</param>
- /// <returns>Lines of specified section</returns>
- private string[] GetSection(string Section, string[] INI)
- {
- //0=Not yet in section, 1=in section, 2=section ended
- int sectionState = 0;
- List<string> Lines=new List<string>();
- for (int i = 0; i < INI.Length && sectionState < 2; i++)
- {
- if (sectionState == 1)
- {
- switch (GetType(INI[i]))
- {
- case LineType.Property:
- Lines.Add(INI[i]);
- break;
- case LineType.Section:
- sectionState = 2;
- break;
- }
- }
- else
- {
- sectionState = (INI[i] == string.Format("[{0}]", Section)) ? 1 : 0;
- }
- }
- return Lines.ToArray();
- }
- /// <summary>
- /// Converts multiple ini lines into the array it comes from
- /// </summary>
- /// <param name="Property">property to search for</param>
- /// <param name="Lines">Lines to search</param>
- /// <returns>string array of values</returns>
- private string[] GetArrayLine(string Property, string[] Lines)
- {
- List<ArrayEntry> Props = new List<ArrayEntry>();
- //get rid of array indexes in property name
- if (Property.Contains("["))
- {
- Property = Property.Substring(0, Property.IndexOf('['));
- }
- foreach (string Line in Lines)
- {
- if (GetType(Line) == LineType.Property && Line.StartsWith(Property+"["))
- {
- Props.Add(new ArrayEntry(Line));
- }
- }
- Props.Sort(Sorter);
- return Props.ConvertAll<string>(Extractor).ToArray();
- }
- /// <summary>
- /// gets the values from ArrayEntry
- /// </summary>
- /// <param name="E"></param>
- /// <returns></returns>
- private string Extractor(ArrayEntry E)
- {
- return E.Value;
- }
- /// <summary>
- /// Sorts array entries
- /// </summary>
- /// <param name="Low">Lower array entry</param>
- /// <param name="High">Upper array entry</param>
- /// <returns>sorting value</returns>
- private int Sorter(ArrayEntry Low,ArrayEntry High)
- {
- return Low.Index - High.Index;
- }
- /// <summary>
- /// Splits a line into Name and value
- /// </summary>
- /// <param name="Line">INI Line</param>
- /// <returns>Array with 2 values: Name and value</returns>
- private string[] SplitLine(string Line)
- {
- return new string[]
- {
- Line.Substring(0,Line.IndexOf('=')),
- Line.Substring(Line.IndexOf('=')+1)
- };
- }
- /// <summary>
- /// Extracts the current object type of a name chain
- /// </summary>
- /// <param name="Namechain">Name chain</param>
- /// <returns>object type as string</returns>
- private string extractType(string Namechain)
- {
- string Last = Namechain.Split(',')[Namechain.Split(',').Length - 1];
- if (Last.Contains("["))
- {
- return Last.Substring(0, Last.IndexOf('['));
- }
- return Last;
- }
- /// <summary>
- /// Gets the type of the current line
- /// </summary>
- /// <param name="Line">Line</param>
- /// <returns>Type of line</returns>
- private LineType GetType(string Line)
- {
- if (Line.StartsWith("[") && Line.EndsWith("]"))
- {
- return LineType.Section;
- }
- if (Line.StartsWith(";") || Line.StartsWith("#"))
- {
- return LineType.Comment;
- }
- if (Line.Contains("="))
- {
- return LineType.Property;
- }
- if (string.IsNullOrEmpty(Line.Trim()))
- {
- return LineType.Empty;
- }
- return LineType.Invalid;
- }
- /// <summary>
- /// Checks if a line is of a given type
- /// </summary>
- /// <param name="Line">Line to check</param>
- /// <param name="Type">Type to check against</param>
- /// <returns>true, if types match</returns>
- private bool IsType(string Line, LineType Type)
- {
- return GetType(Line) == Type;
- }
- /// <summary>
- /// converts a safe string to a regular string
- /// </summary>
- /// <param name="s">safe string</param>
- /// <param name="INI">INI file for further parsing</param>
- /// <returns>object value</returns>
- private object FromString(string s, string[] INI)
- {
- if (s == NULL)
- {
- return null;
- }
- if (s.StartsWith(ARRAY))
- {
- string[] parts = s.Substring(ARRAY.Length).Split('|');
- object[] obj = new object[parts.Length];
- for (int i = 0; i < parts.Length; i++)
- {
- obj[i] = FromString(parts[i], INI);
- }
- return obj;
- }
- if (s.StartsWith("[") && s.EndsWith("]"))
- {
- return AssembleSection(s.Substring(1, s.Length - 2), INI);
- }
- if (s.StartsWith(B64))
- {
- return Encoding.UTF8.GetString(Convert.FromBase64String(s.Substring(B64.Length)));
- }
- return s == NULL ? null : s;
- }
- /// <summary>
- /// converts an object to a safe string
- /// </summary>
- /// <param name="o">object (primitive type only)</param>
- /// <returns>safe string</returns>
- private string ToString(object o)
- {
- if(o.GetType().FullName!=string.Empty.GetType().FullName && !o.GetType().IsPrimitive)
- {
- throw new ArgumentException("tried to convert non-primitive type to string");
- }
- if (o == null)
- {
- return NULL;
- }
- bool offend = false;
- string s = o.ToString();
- offend = (s.StartsWith("[") && s.EndsWith("]")) || s.StartsWith(B64) || s == NULL || s.StartsWith(ARRAY);
- for (int i = 0; i < replaceables.Length && !offend; i++)
- {
- offend = offend || s.Contains(replaceables[i]);
- }
- //convert to base64 if offending content
- if (offend)
- {
- s = B64 + Convert.ToBase64String(Encoding.UTF8.GetBytes(s), Base64FormattingOptions.None).Replace('=', '_');
- }
- return s;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement