Skip to content

Instantly share code, notes, and snippets.

@tfwio
Last active July 28, 2017 15:22
Show Gist options
  • Save tfwio/0ebc2277a4b5118bf2a4d3f959ade730 to your computer and use it in GitHub Desktop.
Save tfwio/0ebc2277a4b5118bf2a4d3f959ade730 to your computer and use it in GitHub Desktop.
INI file serializer/deserializer
/*
* User: xo
* Date: 6/17/2017
* Time: 11:33 PM
*/
using System;
using System.Linq;
using w32;
using w32.shell;
using System.IO;
namespace System
{
static class ShellExtensions
{
static public int Get_SHICON(this string path, SHGFI flags)
{
var shinfo = new CsShellFileInfo();
Shell32.SHGetFileInfo( path, 0, ref shinfo, CsShellFileInfo.SIZE, flags );
return shinfo.IconIndex;
}
static public CsShellFileInfo Get_SHFILE(this DirectoryInfo directory, SHGFI flags)
{
return directory.FullName.Get_SHFILE(flags);
}
/// See SHFileInfo.Create(...)
static public CsShellFileInfo Get_SHFILE(this string path, SHGFI flags)
{
var shinfo = new CsShellFileInfo();
Shell32.SHGetFileInfo( path, 0, ref shinfo, CsShellFileInfo.SIZE, flags );
return shinfo;
}
static public IntPtr Get_Shell_ImageListPointer(this string path, SHGFI flags)
{
var shinfo = new CsShellFileInfo();
return Shell32.SHGetFileInfo( path, 0, ref shinfo, CsShellFileInfo.SIZE, flags );
}
}
static class FileIsLockedExtension
{
// https://stackoverflow.com/questions/876473/is-there-a-way-to-check-if-a-file-is-in-use
static public bool IsFileLocked(this FileInfo file)
{
FileStream stream = null;
try { stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None); }
catch (IOException) { return true; }
finally { if (stream != null) stream.Close(); }
return false;
}
// https://stackoverflow.com/questions/876473/is-there-a-way-to-check-if-a-file-is-in-use
static public bool IsFileStringLocked(this string filePath)
{
if (!File.Exists(filePath)) throw new System.IO.FileNotFoundException(filePath);
var file = new FileInfo(filePath);
return file.IsFileLocked();
}
}
static class FileSystemExtensions
{
static public FileAttributes GetFileAttributes(this string path) { return File.GetAttributes(path); }
static public bool IsDirectory(this string path) { return path.GetFileAttributes().HasFlag(System.IO.FileAttributes.Directory); }
/// <summary>returns null or the existing FileInfo by default.</summary><seealso cref="EnsureDirectory"/>
static public FileInfo GetFileInfo(this string path, bool nullIfNotExist=true) { return nullIfNotExist ? (File.Exists(path) ? new FileInfo(path) : null) : new FileInfo(path); }
/// <summary>returns null or the existing DirectoryInfo by default.</summary><seealso cref="EnsureDirectory"/>
static public DirectoryInfo GetDirectoryInfo(this string path, bool nullIfNotExist=true) { return nullIfNotExist ? (Directory.Exists(path) ? new DirectoryInfo(path) : null) : new DirectoryInfo(path); }
// if the file or directory exists, returns File or DirectoryInfo.
static public FileSystemInfo GetFileSystemInfo(this string path) {
if (path.IsDirectory() && Directory.Exists(path)) return new DirectoryInfo(path);
else if (File.Exists(path)) return new FileInfo(path);
return null;
}
/// <summary>
/// If input path is a file, we return the Directory containing it, otherwise we simply make sure that we're returning a directory.
/// </summary>
static public string EnsureDirectory(this string path) { return path.IsDirectory() ? new DirectoryInfo(path).FullName : new FileInfo(path).Directory.FullName; }
}
}
// - 20170728 added another DIC and serializer method.
// - copyright 20160119-20170728 tfwroble [gmail]
namespace System.IO
{
using System;
using System.Linq;
using System.Collections.Generic;
/// <summary>
/// Ignore IniSerialization on a given Property.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class IgnoreAttribute : Attribute
{
}
/// <summary>
/// The Serialization semantic works for string values only as only string values
/// are stored to ini files, not to mention simplicity.
///
///
/// if you would like to implement other types of values, you would be responsible
/// for parsing complex values and the various number types, etc.
/// </summary>
public class IniCollection : Dictionary<string,Dictionary<string,string>>
{
public IniCollection(params string[] groups) : base() { if (groups.Length > 0) InitializeGroups(groups); }
public IniCollection(IniCollection collection) : base(collection) { }
public IniCollection(FileInfo file) : this(StaticLoad(file.FullName)) {}
public IniCollection(object type_instance) : this() { FromInstance(type_instance); }
internal void FromInstance(object input_o)
{
var input_t = input_o.GetType();
var WritableProperties = input_t.GetProperties().ToList();
foreach (var i in WritableProperties)
{
string category = null;
var ign_attr = i.GetCustomAttributes(typeof(System.IO.IgnoreAttribute), false);
var cat_attr = i.GetCustomAttributes(typeof(System.ComponentModel.CategoryAttribute), false);
if (cat_attr.Length==1) {
var ca = cat_attr[0] as System.ComponentModel.CategoryAttribute;
category = ca.Category;
}
else throw new Exception("Ini-file contained a 'headless' key/value (no group).");
if (!ContainsKey(category)) InitializeGroups(category.Trim());
if (ign_attr.Length == 0) {
var val = input_o.GetStringValue(i.Name, "");
this[category].Add(i.Name,val);
}
}
// Console.ReadKey();
}
internal void ToInstance(object class_instance)
{
var class_type = class_instance.GetType();
var WritableProperties = class_type.GetProperties().ToList();
foreach (var i in WritableProperties)
{
string category = null;
var ign_attr = i.GetCustomAttributes(typeof(System.IO.IgnoreAttribute), false);
var cat_attr = i.GetCustomAttributes(typeof(System.ComponentModel.CategoryAttribute), false);
if (ign_attr.Length == 1) continue;
if (cat_attr.Length==1) { var ca = cat_attr[0] as System.ComponentModel.CategoryAttribute; category = ca.Category; }
else throw new Exception("Ini-file contained a 'headless' key/value (no group).");
var has = HasGroupAndKey(category,i.Name);
if (has)
{
var val = this[category,i.Name];
if (!class_instance.SetInstanceValue(i.Name, val))
throw new Exception("Error writing instance value");
}
}
}
internal void InitializeGroups(params string[] groups) { foreach (var g in groups) this.Add(g, new Dictionary<string,string>()); }
public void Write(string iniFile)
{
Write(iniFile.GetFileInfo(false));
}
public void Write(FileInfo file)
{
if (file.Exists) file.Delete();
using (var writer = new StreamWriter(file.FullName, false, System.Text.Encoding.UTF8))
{
foreach (var k in this.Keys)
{
writer.WriteLine("[{0}]",k);
writer.WriteLine("");
foreach (var kv in this[k])
{
writer.WriteLine("{0} = {1}", kv.Key, kv.Value);
writer.WriteLine("");
}
}
}
}
bool HasGroupAndKey(string pGroup, string pKey) { return this[pGroup].ContainsKey(pKey); }
public string this[string kGroup, string kKey]
{
get { return HasGroupAndKey(kGroup,kKey) ? this[kGroup][kKey] : null; }
set { if (ContainsKey(kGroup)) this[kGroup].Add(kKey,value); }
}
static public IniCollection StaticLoad(string file)
{
var Dic = new IniCollection();
if (!File.Exists(file)) return null;
var data = File.ReadAllLines(file, Text.Encoding.UTF8);
string k1 = "", k2 = "";
foreach (var line in data)
{
int i = -1;
var start = line.Trim(' ');
if (string.IsNullOrEmpty(start)) continue;
if (start[0]==';') continue;
if (start[0]=='#') continue;
if (start[0]=='[')
{
k1 = start.Trim('[',']');
if (!Dic.ContainsKey(k1)) Dic.InitializeGroups(k1);
continue;
}
else if ((i=start.IndexOf('=')) >= 0)
{
k2 = start.Substring(0,i);
i++;
var v = start.Substring(i,start.Length-i);
Dic[k1].Add(k2.Trim(),v.Trim());
}
}
data = null;
return Dic;
}
}
/// <summary>
/// this class is generally obsolete.
///
/// Its put to use in <see cref="IniReader"/> and the dictionary implementation using it.
/// </summary>
public class IniSetting
{
public string ItemKey { get; set; }
public string ItemValue { get; set; }
public IniSetting() { }
public IniSetting(string pItemKey, string pItemValue) { ItemKey=pItemKey; ItemValue=pItemValue; }
}
/// <summary>
/// it could get a bit confusing that I have two dictionaries for the same purpose.
/// both appear to work, one seems to be simpler than the other (dic&lt;string,dic&lt;str,str>>)
/// but in reality they both do the same thing.
///
/// There was a little parser hiccup which forced me to rewrite anew which told me that
/// I wasn't trimming the key and that I didn't have to re-write anything...
///
/// and now I have two frigging collection cases both of which happen to be used.
///
///
/// this collection is used in <see cref="IniReader"/> and that is all.
///
/// IniReader also happens to be obsolete.
/// </summary>
public class IniDic : Dictionary<string,List<IniSetting>> {
public IniDic(params string[] groupNames) : base() { InitializeGroupNames(groupNames); }
public void InitializeGroupNames(params string[] groupNames)
{
foreach (var g in groupNames) this.Add(g, new List<IniSetting>());
}
public string this[string kGroup, string kKey]
{
get { return ContainsKey(kGroup) ? this[kGroup].First(f=>f.ItemKey==kKey).ItemValue : null; }
set { if (ContainsKey(kGroup)) { this[kGroup].Add(new IniSetting(kKey,value)); } }
}
}
/// <summary>
/// <c>Dictionary&lt;string,List&lt;Pair>>;</c> would be our primary container for all settings where the Key
/// defines our group and each value is a (custom) keyvalue pair type of thing.
/// </summary>
/// <seealso cref="Pair"/>
class IniReader : IDisposable
{
#region IDisposable implementation
public void Dispose()
{
Dic.Clear();
}
#endregion
public bool ContainsKey(string Key)
{
return Dic.ContainsKey(Key);
}
readonly IniDic Dic = new IniDic();
/// <summary>
/// Returns a formatted 'ini' Key/Value string such as
/// "Key=Value"
/// </summary>
/// <returns>The string.</returns>
/// <param name="Name">Name.</param>
/// <param name="Value">Value.</param>
static string KeyStr(string Name, object Value)
{
return string.Format("{0}={1}",Name,Value);
}
//
// Self Referencing Dictionary Lookups
// ===========================================================
[IgnoreAttribute]
public string this[string k1, string k2] {
get { return Dic == null ? null : this[k1]?.Find(p => p.ItemKey == k2)?.ItemValue; }
}
[IgnoreAttribute]
public List<IniSetting> this[string k1] {
get { return Dic.ContainsKey(k1) ? Dic[k1] : null; }
}
//
// Get Value Methods
// ===========================================================
// getInt
public int GetInt32(string k1, string k2, int defaultValue = 0)
{
var v = this[k1,k2];
int result = defaultValue;
return v == null ? defaultValue : int.TryParse(v, out result) ? result : defaultValue;
}
// getFloat
public float GetFloat(string k1, string k2, float nullValue = 0.0f)
{
var v = this[k1,k2];
float result = nullValue;
return (v == null) ? nullValue : float.TryParse(v, out result) ? result : nullValue;
}
// getString
public string GetChars(string k1, string k2, string nullValue=null)
{
return this[k1,k2] ?? nullValue;
}
public string GetChars(IniSetting Key, string nullValue=null)
{
return this[Key.ItemKey,Key.ItemValue] ?? nullValue;
}
public void Load(string file)
{
if (!File.Exists(file)) return;
Dic.Clear();
var data = File.ReadAllLines(file, Text.Encoding.UTF8);
string k1 = "", k2 = "";
foreach (var line in data)
{
int i = -1;
var start = line.Trim(' ');
if (string.IsNullOrEmpty(start)) continue;
if (start[0]==';') continue;
if (start[0]=='#') continue;
if (start[0]=='[')
{
k1 = start.Trim('[',']');
Dic.Add(k1,new List<IniSetting>());
//Debug.Print("Key: {0} — Added",k1);
continue;
}
else if ((i=start.IndexOf('=')) >= 0)
{
k2 = start.Substring(0,i);
i++;
var v = start.Substring(i,start.Length-i);
Dic[k1].Add(new IniSetting(k2,v));
//Debug.Print("Sub: {0} — {1}",k2, v);
}
}
data = null;
}
}
/// <summary>
/// this is a very general INI WRITER used for manually wrting values (IE: writing your own code)
/// to an INI file.
///
///
/// - <c><see cref="Write">Write()</see></c> appends a blank line.
///
/// - <c><see cref="Write(string)">Write(string)</see></c> Append a group.
///
/// - <c><see cref="Write(string,object)">Write(string)</see></c> Append a Key and Value to the last written group.
/// In this case, the object is automatically converted using ToString() via a call to <c>String.Format(...)</c>
/// as defined in <see cref="KeyStr(string, object)"/>.
/// </summary>
class IniWriter : IDisposable
{
Stream Stream { get; set; }
StreamWriter Writer { get; set; }
#if __WIN
public string NewLine { get; set; } = "\r\n";
#else
public string NewLine { get; set; } = "\n";
#endif
#region .ctor
public IniWriter(string outputFile)
{
if (File.Exists (outputFile)) File.Delete (outputFile);
Stream = new FileStream(outputFile, FileMode.OpenOrCreate);
Writer = new StreamWriter(Stream);
Writer.NewLine = NewLine;
IsDisposed = false;
}
public IniWriter() : this(new MemoryStream())
{
}
public IniWriter(Stream stream)
{
Stream = stream;
Writer = new StreamWriter(Stream);
IsDisposed = false;
}
~IniWriter()
{
if (!IsDisposed)
Dispose ();
}
#endregion
/// <summary>
/// Write a Key. If the key isn't surrounded in square-braces,
/// it will be [wrapped] in them.
/// </summary>
/// <param name="Key">Key.</param>
public void Write(string Key)
{
Writer.WriteLine(string.Format(Key[0]=='[' ? "{0}" : "[{0}]",Key));
}
/// <summary>
/// Writes such as "Key=Value".
/// </summary>
/// <param name="Key">Key.</param>
/// <param name="Value">Value.</param>
public void Write(string Key, object Value)
{
Writer.WriteLine (KeyStr (Key,Value));
}
/// <summary>
/// Writes a line-break using
/// </summary>
public void Write()
{
Writer.WriteLine ();
}
string KeyStr(string Name, object Value)
{
return string.Format("{0}={1}",Name,Value);
}
#region IDisposable implementation
bool IsDisposed = false;
public void Dispose ()
{
if (Writer!=null) {
try {
Writer.Close();
} catch {
}
try {
Writer.Dispose();
} catch {
} finally {
Writer = null;
}
}
if (Stream!=null) {
try {
Stream.Close();
} catch {
}
try {
Stream.Dispose();
} catch {
} finally {
Writer = null;
}
}
IsDisposed = true;
}
#endregion
}
}
/* tfwxo * 1/19/2016 * 2:10 AM */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
namespace System
{
static class PropertyInfoHelper
{
static public string GetStringValue(this object TargetClass, string PropertyName, string nullValue="")
{
Type t = null;
PropertyInfo i = null;
object val = null;
try {
t = TargetClass.GetType();
i = t.GetProperty(PropertyName);
val = i.GetValue(TargetClass,null);
} catch {
Console.WriteLine("Couldn't get a valid value for '{0}'", i.Name);
}
return string.Format("{0}", val ?? nullValue);
}
static public bool SetInstanceValue(this object TargetClass, string PropertyName, object value)
{
var type = TargetClass.GetType();
var prop = type.GetProperty(PropertyName);
var props = type.GetProperties();
try { prop.SetValue( TargetClass, value, null); }
catch { return false; }
return true;
}
static public string GetFormatStringValue(this object TargetClass, string PropertyName, string format="{0}", string nullValue="")
{
var t = TargetClass.GetType();
var i = t.GetProperty(PropertyName);
var v = i.GetValue(TargetClass,null);
return string.Format("{0}", v ?? nullValue);
}
static public string GetCategory(this PropertyInfo p, string defaultCategory="global")
{
var l = p.GetCustomAttributes(false).ToList();
foreach (var item in l)
{
var categoryAttribute = item as CategoryAttribute;
if (categoryAttribute != null)
{
var cat = categoryAttribute;
return cat.Category;
}
}
return defaultCategory;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment