Last active
July 28, 2017 15:22
-
-
Save tfwio/0ebc2277a4b5118bf2a4d3f959ade730 to your computer and use it in GitHub Desktop.
INI file serializer/deserializer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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; } | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// - 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<string,dic<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<string,List<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 | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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