diff --git a/src/AutoMapper/AutoMapper.csproj b/src/AutoMapper/AutoMapper.csproj
new file mode 100644
index 0000000..3653d84
--- /dev/null
+++ b/src/AutoMapper/AutoMapper.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netstandard2.0
+ hydrogen.AutoMapper
+ hydrogen.AutoMapper
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AutoMapper/AutoMapperConfigAttribute.cs b/src/AutoMapper/AutoMapperConfigAttribute.cs
new file mode 100644
index 0000000..aacca65
--- /dev/null
+++ b/src/AutoMapper/AutoMapperConfigAttribute.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace hydrogen.AutoMapper
+{
+ ///
+ /// Specifies that a type has AutoMapper configuration
+ ///
+ ///
+ /// Any type that is decorated by this attribute, should have a public, static method called "ConfigureAutoMapper"
+ /// that doesn't take any arguments, and should return void. This method will be called during AutoMapper configuration,
+ /// by AutoMapperConfigurator, prior to validation of mappings.
+ ///
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
+ public class AutoMapperConfigAttribute : Attribute
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/AutoMapper/AutoMapperConfigurationExtensions.cs b/src/AutoMapper/AutoMapperConfigurationExtensions.cs
new file mode 100644
index 0000000..c9eaeef
--- /dev/null
+++ b/src/AutoMapper/AutoMapperConfigurationExtensions.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Linq.Expressions;
+using AutoMapper;
+
+namespace hydrogen.AutoMapper
+{
+ public static class AutoMapperConfigurationExtensions
+ {
+ public static IMappingExpression IgnoreAll(this IMappingExpression expression)
+ {
+ expression.ForAllMembers(opt => opt.Ignore());
+ return expression;
+ }
+
+ public static IMappingExpression IgnoreUnmappedProperties(this IMappingExpression expression)
+ {
+ var typeMap = Mapper.Configuration.FindTypeMapFor();
+ if (typeMap != null)
+ {
+ foreach (var unmappedPropertyName in typeMap.GetUnmappedPropertyNames())
+ {
+ expression.ForMember(unmappedPropertyName, opt => opt.Ignore());
+ }
+ }
+
+ return expression;
+ }
+
+ public static IMappingExpression Ignore(this IMappingExpression expression, Expression> destinationMember)
+ {
+ return expression.ForMember(destinationMember, opt => opt.Ignore());
+ }
+
+ public static IMappingExpression IgnoreSource(this IMappingExpression expression, Expression> sourceMember)
+ {
+ return expression.ForSourceMember(sourceMember, opt => opt.Ignore());
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/AutoMapper/AutoMapperConfigurator.cs b/src/AutoMapper/AutoMapperConfigurator.cs
new file mode 100644
index 0000000..8d22040
--- /dev/null
+++ b/src/AutoMapper/AutoMapperConfigurator.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using hydrogen.General.Collections;
+
+namespace hydrogen.AutoMapper
+{
+ public static class AutoMapperConfigurator
+ {
+ public static void Scan(Assembly assembly)
+ {
+ assembly.GetTypes()
+ .Where(t => t.GetCustomAttributes(typeof(AutoMapperConfigAttribute)).Any())
+ .OrderBy(GetDistanceFromObject)
+ .ForEach(type =>
+ {
+ var method = type.GetMethod("ConfigureAutoMapper", new Type[0]);
+ if (method == null || !method.IsStatic || method.ReturnType != typeof(void) || method.GetParameters().Any())
+ throw new InvalidOperationException(
+ "Type " + type.FullName + " is decorated with [AutoMapperConfigAttribute] but does not contain a public static method with the signature of 'void ConfigureAutoMapper()'");
+
+ method.Invoke(null, null);
+ });
+ }
+
+ private static int GetDistanceFromObject(Type t)
+ {
+ var result = 0;
+ while (!(typeof(object) == t))
+ {
+ if (t == null)
+ return -1;
+
+ result++;
+ t = t.BaseType;
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Collections/CollectionExtensions.cs b/src/General/Collections/CollectionExtensions.cs
new file mode 100644
index 0000000..77bc53a
--- /dev/null
+++ b/src/General/Collections/CollectionExtensions.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+
+namespace hydrogen.General.Collections
+{
+ public static class CollectionExtensions
+ {
+ public static void AddAll(this ICollection target, IEnumerable items)
+ {
+ foreach (var item in items)
+ {
+ target.Add(item);
+ }
+ }
+
+ public static void AddAll(this ISet target, IEnumerable items)
+ {
+ foreach (var item in items)
+ {
+ target.Add(item);
+ }
+ }
+
+ public static void AddIfNotContains(this ICollection target, T item)
+ {
+ if (!target.Contains(item))
+ target.Add(item);
+ }
+
+ public static void SetItemExistance(this ICollection target, T item, bool existance)
+ {
+ if (existance && !target.Contains(item))
+ target.Add(item);
+ else
+ target.Remove(item);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Collections/LinkedDictionary.cs b/src/General/Collections/LinkedDictionary.cs
new file mode 100644
index 0000000..d56849e
--- /dev/null
+++ b/src/General/Collections/LinkedDictionary.cs
@@ -0,0 +1,615 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace hydrogen.General.Collections
+{
+ ///
+ /// Linked-list backend IDictionary implementation.
+ /// Code copied and changed from REVISION 679 of following SVN repository:
+ /// https://slog.dk/svn/home/jensen/source/linked_dictionary/trunk/
+ ///
+ ///
+ ///
+ public abstract class AbstractLinkedDictionary : IDictionary
+ {
+ #region Fields
+
+ protected readonly IDictionary Backend;
+ protected volatile uint Updates;
+ protected LinkedKeyValuePair First;
+ protected LinkedKeyValuePair Last;
+
+ #endregion
+
+ #region Initialization
+
+ protected AbstractLinkedDictionary(IDictionary backend)
+ {
+ Backend = backend;
+ }
+
+ #endregion
+
+ #region Abstracts
+
+ protected abstract bool RemoveItem(TKey key);
+ protected abstract void AddItem(TKey key, TValue value);
+ protected abstract void SetItem(TKey key, TValue value);
+ protected abstract LinkedKeyValuePair GetItem(TKey key);
+
+ #endregion
+
+ #region Explicit directional iteration
+
+ public IEnumerator> GetEnumeratorForward()
+ {
+ return new LinkedDictionaryEnumerator(this, true);
+ }
+
+ public IEnumerator> GetEnumeratorBackward()
+ {
+ return new LinkedDictionaryEnumerator(this, false);
+ }
+
+ public ICollection ValuesForward => new DictionaryValues(this, true);
+
+ public ICollection ValuesBackward => new DictionaryValues(this, false);
+
+ public ICollection KeysForward => new DictionaryKeys(this, true);
+
+ public ICollection KeysBackward => new DictionaryKeys(this, false);
+
+ #endregion
+
+ #region Implementation of IEnumerable
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+
+ #region Implementation of IEnumerable>
+
+ public virtual IEnumerator> GetEnumerator()
+ {
+ return GetEnumeratorForward();
+ }
+
+ #endregion
+
+ #region Implementation of ICollection>
+
+ public void Add(KeyValuePair item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ public void Clear()
+ {
+ Updates++;
+ Backend.Clear();
+ First = null;
+ Last = null;
+ }
+
+ public bool Contains(KeyValuePair item)
+ {
+ return Backend.ContainsKey(item.Key) && Backend[item.Key].Value.Equals(item.Value);
+ }
+
+ public void CopyTo(KeyValuePair[] array, int index)
+ {
+ foreach (KeyValuePair e in this)
+ array.SetValue(e, index++);
+ }
+
+ public bool Remove(KeyValuePair item)
+ {
+ if (Contains(item))
+ return Remove(item.Key);
+
+ return false;
+ }
+
+ public int Count => Backend.Count;
+
+ public bool IsReadOnly => Backend.IsReadOnly;
+
+ #endregion
+
+ #region Implementation of IDictionary
+
+ public bool ContainsKey(TKey key)
+ {
+ return Backend.ContainsKey(key);
+ }
+
+ public void Add(TKey key, TValue value)
+ {
+ Updates++;
+ AddItem(key, value);
+ }
+
+ public bool Remove(TKey key)
+ {
+ Updates++;
+ return RemoveItem(key);
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ var item = GetItem(key);
+ if (item != null)
+ {
+ value = item.Value;
+ return true;
+ }
+
+ value = default(TValue);
+ return false;
+ }
+
+ public TValue this[TKey key]
+ {
+ get
+ {
+ return GetItem(key).Value;
+ }
+ set
+ {
+ Updates++;
+ SetItem(key, value);
+ }
+ }
+
+ public virtual ICollection Keys => KeysForward;
+
+ public virtual ICollection Values => ValuesForward;
+
+ #endregion
+
+ #region Inner types
+
+ public class LinkedKeyValuePair
+ {
+ public LinkedKeyValuePair Previous { get; set; }
+ public KeyValuePair KeyValuePair { get; }
+ public LinkedKeyValuePair Next { get; set; }
+
+ public TKey Key => KeyValuePair.Key;
+
+ public TValue Value => KeyValuePair.Value;
+
+ public LinkedKeyValuePair(TKey key, TValue value, LinkedKeyValuePair prev, LinkedKeyValuePair next)
+ {
+ KeyValuePair = new KeyValuePair(key, value);
+ Previous = prev;
+ Next = next;
+ }
+ }
+
+ private class LinkedDictionaryEnumerator : IEnumerator>
+ {
+ private readonly AbstractLinkedDictionary _parent;
+ private readonly bool _forward;
+ private readonly uint _updates;
+ private LinkedKeyValuePair _current;
+
+ public LinkedDictionaryEnumerator(AbstractLinkedDictionary parent, bool forward)
+ {
+ _parent = parent;
+ _forward = forward;
+ _current = null;
+ _updates = parent.Updates;
+ }
+
+ #region IEnumerator Members
+
+ public void Reset()
+ {
+ _current = null;
+ }
+
+ public KeyValuePair Current => new KeyValuePair(_current.Key, _current.Value);
+
+ object IEnumerator.Current => Current;
+
+ public bool MoveNext()
+ {
+ if (_parent.Updates != _updates)
+ throw new InvalidOperationException("Collection was modified after the enumerator was created");
+ if (_current == null)
+ _current = _forward ? _parent.First : _parent.Last;
+ else
+ _current = _forward ? _current.Next : _current.Previous;
+ return _current != null;
+ }
+
+ public void Dispose()
+ {
+ }
+
+ #endregion
+
+ #region Key and Value direct access members
+
+ public TKey Key
+ {
+ get
+ {
+ if (_current == null)
+ throw new IndexOutOfRangeException();
+
+ return _current.Key;
+ }
+ }
+ public TValue Value
+ {
+ get
+ {
+ if (_current == null)
+ throw new IndexOutOfRangeException();
+
+ return _current.Value;
+ }
+ }
+
+ #endregion
+ }
+
+ private struct DictionaryKeys : ICollection
+ {
+ private readonly AbstractLinkedDictionary _parent;
+ private readonly bool _forward;
+
+ public DictionaryKeys(AbstractLinkedDictionary parent, bool forward)
+ {
+ _parent = parent;
+ _forward = forward;
+ }
+
+ #region Implementation of IEnumerable
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+
+ #region Implementation of IEnumerable
+
+ public IEnumerator GetEnumerator()
+ {
+ return new KeysEnumerator(new LinkedDictionaryEnumerator(_parent, _forward));
+ }
+
+ #endregion
+
+ #region Implementation of ICollection
+
+ public void Add(TKey item)
+ {
+ throw new NotSupportedException();
+ }
+
+ public void Clear()
+ {
+ throw new NotSupportedException();
+ }
+
+ public bool Contains(TKey item)
+ {
+ return _parent.Backend.ContainsKey(item);
+ }
+
+ public void CopyTo(TKey[] array, int index)
+ {
+ foreach (object o in this)
+ array.SetValue(o, index++);
+ }
+
+ public bool Remove(TKey item)
+ {
+ throw new NotSupportedException();
+ }
+
+ public int Count => _parent.Backend.Count;
+
+ public bool IsReadOnly => true;
+
+ #endregion
+
+ #region Inner types
+
+ private struct KeysEnumerator : IEnumerator
+ {
+ private readonly LinkedDictionaryEnumerator _enumerator;
+
+ public KeysEnumerator(LinkedDictionaryEnumerator enumerator)
+ {
+ _enumerator = enumerator;
+ }
+
+ #region IEnumerator Members
+
+ public void Reset()
+ {
+ _enumerator.Reset();
+ }
+
+ public TKey Current => _enumerator.Key;
+
+ public bool MoveNext()
+ {
+ return _enumerator.MoveNext();
+ }
+
+ public void Dispose()
+ {
+ }
+
+ object IEnumerator.Current => Current;
+
+ #endregion
+ }
+
+ #endregion
+ }
+
+ public struct DictionaryValues : ICollection
+ {
+ private readonly AbstractLinkedDictionary _parent;
+ private readonly bool _forward;
+
+ public DictionaryValues(AbstractLinkedDictionary parent, bool forward)
+ {
+ _parent = parent;
+ _forward = forward;
+ }
+
+ #region Implementation of IEnumerable
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+
+ #region Implementation of IEnumerable
+
+ public IEnumerator GetEnumerator()
+ {
+ return new ValuesEnumerator(new LinkedDictionaryEnumerator(_parent, _forward));
+ }
+
+ #endregion
+
+ #region Implementation of ICollection
+
+ public void Add(TValue item)
+ {
+ throw new NotSupportedException();
+ }
+
+ public void Clear()
+ {
+ throw new NotSupportedException();
+ }
+
+ public bool Contains(TValue item)
+ {
+ return _parent.Backend.Values.Any(l => l.Value.Equals(item));
+ }
+
+ public void CopyTo(TValue[] array, int index)
+ {
+ foreach (object o in this)
+ array.SetValue(o, index++);
+ }
+
+ public bool Remove(TValue item)
+ {
+ throw new NotSupportedException();
+ }
+
+ public int Count => _parent.Count;
+
+ public bool IsReadOnly => true;
+
+ #endregion
+
+ #region Inner types
+
+ struct ValuesEnumerator : IEnumerator
+ {
+ private readonly LinkedDictionaryEnumerator _enumerator;
+
+ public ValuesEnumerator(LinkedDictionaryEnumerator enumerator)
+ {
+ _enumerator = enumerator;
+ }
+
+ #region IEnumerator Members
+
+ public void Reset()
+ {
+ _enumerator.Reset();
+ }
+
+ object IEnumerator.Current => Current;
+
+ public bool MoveNext()
+ {
+ return _enumerator.MoveNext();
+ }
+
+ public TValue Current => _enumerator.Value;
+
+ public void Dispose()
+ {
+ }
+
+ #endregion
+ }
+
+ #endregion
+ }
+
+ #endregion
+
+ #region Debug Helpers
+
+#if DEBUG
+ // ReSharper disable UnusedMember.Local
+ static object[] Array(ICollection c) { var a = new object[c.Count]; c.CopyTo(a, 0); return a; }
+ // ReSharper restore UnusedMember.Local
+#endif
+
+ #endregion
+ }
+
+ ///
+ /// Provides an IDictionary which is iterated in the inverse order of update
+ ///
+ public class UpdateLinkedDictionary : AbstractLinkedDictionary
+ {
+ public UpdateLinkedDictionary()
+ : this(new Dictionary())
+ {
+ }
+
+ public UpdateLinkedDictionary(IDictionary backend)
+ : base(backend)
+ {
+ }
+
+ protected override void AddItem(TKey key, TValue value)
+ {
+ if (Backend.ContainsKey(key))
+ throw new ArgumentException($"Key \"{key}\" already present in dictionary");
+
+ var l = new LinkedKeyValuePair(key, value, Last, null);
+ if (Last != null)
+ Last.Next = l;
+
+ Last = l;
+ if (First == null)
+ First = l;
+
+ Backend.Add(key, l);
+ }
+
+ protected override LinkedKeyValuePair GetItem(TKey key)
+ {
+ LinkedKeyValuePair result;
+ return Backend.TryGetValue(key, out result) ? result : null;
+ }
+
+ protected override bool RemoveItem(TKey key)
+ {
+ LinkedKeyValuePair l;
+ if (Backend.TryGetValue(key, out l))
+ {
+ if (l != null)
+ {
+ LinkedKeyValuePair pre = l.Previous;
+ LinkedKeyValuePair nxt = l.Next;
+
+ if (pre != null)
+ pre.Next = nxt;
+ else
+ First = nxt;
+ if (nxt != null)
+ nxt.Previous = pre;
+ else
+ Last = pre;
+
+ }
+
+ return Backend.Remove(key);
+ }
+
+ return false;
+ }
+
+ protected override void SetItem(TKey key, TValue value)
+ {
+ LinkedKeyValuePair l = GetItem(key);
+ if (l != null)
+ RemoveItem(key);
+
+ AddItem(key, value);
+ }
+ }
+
+ public class LruDictionary : UpdateLinkedDictionary
+ {
+ public LruDictionary()
+ : this(new Dictionary())
+ {
+ }
+
+ public LruDictionary(IDictionary backend)
+ : base(backend)
+ {
+ }
+
+ protected override LinkedKeyValuePair GetItem(TKey key)
+ {
+ LinkedKeyValuePair l;
+ if (!Backend.TryGetValue(key, out l))
+ return null;
+
+ if (l == null)
+ return null;
+
+ LinkedKeyValuePair nxt = l.Next;
+ if (nxt != null) // last => no-change
+ {
+ Updates++; // looking is updating
+ // note, atleast 2 items in chain now, since l != last
+ LinkedKeyValuePair pre = l.Previous;
+ if (pre == null)
+ First = nxt;
+ else
+ pre.Next = l.Next;
+
+ nxt.Previous = pre; // nxt != null since l != last
+ Last.Next = l;
+ l.Next = null;
+ l.Previous = Last;
+ Last = l;
+ }
+
+ return l;
+ }
+ }
+
+ public class MruDictionary : LruDictionary
+ {
+ public MruDictionary()
+ : this(new Dictionary())
+ {
+ }
+
+ public MruDictionary(IDictionary backend)
+ : base(backend)
+ {
+ }
+
+ public override IEnumerator> GetEnumerator()
+ {
+ return GetEnumeratorForward();
+ }
+
+ public override ICollection Keys => KeysBackward;
+
+ public override ICollection Values => ValuesBackward;
+ }
+}
\ No newline at end of file
diff --git a/src/General/Collections/PagedList.cs b/src/General/Collections/PagedList.cs
new file mode 100644
index 0000000..d69d18f
--- /dev/null
+++ b/src/General/Collections/PagedList.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace hydrogen.General.Collections
+{
+ public class PagedList : IEnumerable
+ {
+ private PaginationStats _paginationStats;
+
+ public PaginationStats Stats => _paginationStats;
+
+ public int PageSize => _paginationStats.PageSize;
+
+ public int PageNumber => _paginationStats.PageNumber;
+
+ public int TotalNumberOfPages => _paginationStats.TotalNumberOfPages;
+
+ public int TotalNumberOfItems => _paginationStats.TotalNumberOfItems;
+
+ public int FirstItemIndex => _paginationStats.FirstItemIndex;
+
+ public int LastItemIndex => _paginationStats.FirstItemIndex + (PageItems?.Count ?? 0) - 1;
+
+ public List PageItems { get; set; }
+
+ #region Initialization
+
+ [Obsolete("Use static builder methods instead")]
+ public PagedList(int totalNumberOfItems, int pageSize, int pageNumber)
+ {
+ _paginationStats = PaginationStats.FromPageNumber(totalNumberOfItems, pageSize, pageNumber);
+ }
+
+ [Obsolete("Use static builder methods instead")]
+ public PagedList(List allItems)
+ {
+ PageItems = allItems;
+ _paginationStats = PaginationStats.SinglePage(allItems.Count);
+ }
+
+ private PagedList()
+ {
+ }
+
+ public static PagedList BuildFromCompleteList(List allItems)
+ {
+ var result = new PagedList
+ {
+ PageItems = allItems,
+ _paginationStats = PaginationStats.SinglePage(allItems.Count)
+ };
+
+ return result;
+ }
+
+ public static PagedList BuildUsingPageNumber(int totalNumberOfItems, int pageSize, int pageNumber)
+ {
+ var result = new PagedList
+ {
+ _paginationStats = PaginationStats.FromPageNumber(totalNumberOfItems, pageSize, pageNumber)
+ };
+
+ return result;
+ }
+
+ public static PagedList BuildUsingStartIndex(int totalNumberOfItems, int pageSize, int startIndex)
+ {
+ var result = new PagedList
+ {
+ _paginationStats = PaginationStats.FromStartIndex(totalNumberOfItems, pageSize, startIndex)
+ };
+
+ return result;
+ }
+
+ public static PagedList BuildUsingPagedList(PagedList source)
+ {
+ var result = new PagedList
+ {
+ _paginationStats = PaginationStats.FromPaginationStats(source._paginationStats)
+ };
+
+ return result;
+ }
+
+ public static PagedList BuildUsingPagedList(PagedList source, Func mapping)
+ {
+ var result = BuildUsingPagedList(source);
+ result.PageItems = source.PageItems.Select(mapping).ToList();
+
+ return result;
+ }
+
+ #endregion
+
+ #region Implementation of IEnumerable
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ if (PageItems != null)
+ return PageItems.GetEnumerator();
+
+ throw new InvalidOperationException("List does not have a value.");
+ }
+
+ #endregion
+
+ #region Implementation of IEnumerable
+
+ public IEnumerator GetEnumerator()
+ {
+ if (PageItems != null)
+ return PageItems.GetEnumerator();
+
+ throw new InvalidOperationException("List does not have a value.");
+ }
+
+ #endregion
+
+ #region Public methods
+
+ public void FillFrom(IQueryable query)
+ {
+ PageItems = query.Skip(FirstItemIndex - 1).Take(PageSize).ToList();
+ }
+
+ public void FillFrom(IEnumerable items)
+ {
+ PageItems = items.Skip(FirstItemIndex - 1).Take(PageSize).ToList();
+ }
+
+ public void FillFromNoSkip(IEnumerable items)
+ {
+ PageItems = items.Take(PageSize).ToList();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/General/Collections/PaginationStats.cs b/src/General/Collections/PaginationStats.cs
new file mode 100644
index 0000000..bfd34be
--- /dev/null
+++ b/src/General/Collections/PaginationStats.cs
@@ -0,0 +1,84 @@
+using System;
+
+namespace hydrogen.General.Collections
+{
+ public class PaginationStats
+ {
+ private int _pageSize;
+ private int _pageNumber;
+ private int _totalNumberOfItems;
+ private int _totalNumberOfPages;
+ private int _firstItemIndex;
+
+ public int PageSize => _pageSize;
+
+ public int PageNumber => _pageNumber;
+
+ public int TotalNumberOfPages => _totalNumberOfPages;
+
+ public int TotalNumberOfItems => _totalNumberOfItems;
+
+ public int FirstItemIndex => _firstItemIndex;
+
+ #region Initialization
+
+ private PaginationStats()
+ {
+ }
+
+ public static PaginationStats SinglePage(int count)
+ {
+ var result = new PaginationStats();
+
+ result._totalNumberOfItems = result._pageSize = count;
+ result._pageNumber = result._totalNumberOfPages = result._firstItemIndex = 1;
+
+ return result;
+ }
+
+ public static PaginationStats FromPageNumber(int totalNumberOfItems, int pageSize, int pageNumber)
+ {
+ var result = new PaginationStats
+ {
+ _totalNumberOfItems = totalNumberOfItems,
+ _pageSize = pageSize,
+ _totalNumberOfPages = (totalNumberOfItems + pageSize - 1)/pageSize
+ };
+
+ result._pageNumber = Math.Max(Math.Min(pageNumber, result._totalNumberOfPages), 1);
+ result._firstItemIndex = (result._pageNumber - 1) * result._pageSize + 1;
+
+ return result;
+ }
+
+ public static PaginationStats FromStartIndex(int totalNumberOfItems, int pageSize, int startIndex)
+ {
+ var result = new PaginationStats
+ {
+ _totalNumberOfItems = totalNumberOfItems,
+ _pageSize = pageSize,
+ _totalNumberOfPages = (totalNumberOfItems + pageSize - 1)/pageSize,
+ _firstItemIndex = Math.Max(Math.Min(startIndex, totalNumberOfItems), 1),
+ _pageNumber = (startIndex + pageSize - 2)/pageSize + 1
+ };
+
+ return result;
+ }
+
+ public static PaginationStats FromPaginationStats(PaginationStats source)
+ {
+ var result = new PaginationStats
+ {
+ _totalNumberOfItems = source._totalNumberOfItems,
+ _pageSize = source._pageSize,
+ _totalNumberOfPages = source._totalNumberOfPages,
+ _firstItemIndex = source._firstItemIndex,
+ _pageNumber = source._pageNumber
+ };
+
+ return result;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/General/Collections/ReadOnlyDictionary.cs b/src/General/Collections/ReadOnlyDictionary.cs
new file mode 100644
index 0000000..00e6529
--- /dev/null
+++ b/src/General/Collections/ReadOnlyDictionary.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+
+namespace hydrogen.General.Collections
+{
+ public class ReadOnlyDictionary : IDictionary
+ {
+ private readonly IDictionary _dict;
+
+ public ReadOnlyDictionary(IDictionary backingDict)
+ {
+ _dict = backingDict;
+ }
+
+ public void Add(TKey key, TValue value)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return _dict.ContainsKey(key);
+ }
+
+ public ICollection Keys => _dict.Keys;
+
+ public bool Remove(TKey key)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ return _dict.TryGetValue(key, out value);
+ }
+
+ public ICollection Values => _dict.Values;
+
+ public TValue this[TKey key]
+ {
+ get { return _dict[key]; }
+ set { throw new InvalidOperationException(); }
+ }
+
+ public void Add(KeyValuePair item)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public void Clear()
+ {
+ throw new InvalidOperationException();
+ }
+
+ public bool Contains(KeyValuePair item)
+ {
+ return _dict.Contains(item);
+ }
+
+ public void CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ _dict.CopyTo(array, arrayIndex);
+ }
+
+ public int Count => _dict.Count;
+
+ public bool IsReadOnly => true;
+
+ public bool Remove(KeyValuePair item)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ return _dict.GetEnumerator();
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return ((System.Collections.IEnumerable)_dict).GetEnumerator();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Collections/StackExtensions.cs b/src/General/Collections/StackExtensions.cs
new file mode 100644
index 0000000..b96dfc7
--- /dev/null
+++ b/src/General/Collections/StackExtensions.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+
+namespace hydrogen.General.Collections
+{
+ public static class StackExtensions
+ {
+ public static void PushAll(this Stack stack, IEnumerable elements)
+ {
+ if (elements == null)
+ return;
+
+ foreach (var element in elements)
+ {
+ stack.Push(element);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/General.csproj b/src/General/General.csproj
index 55b7ed9..a2b4a2b 100644
--- a/src/General/General.csproj
+++ b/src/General/General.csproj
@@ -8,7 +8,7 @@
clay-one
Hydrogen.General
hydrogen libraries for .NET Standard is a set of utilities and extensions that are used frequently in different projects.
- Copyright appson.tech 2017
+ Copyright clay-one 2017
https://github.com/clay-one/hydrogen.git
hydrogen dotnet
clay-one
@@ -18,4 +18,42 @@
+
+
+
+
+
+
+
+ True
+ True
+ TimeSpanLocalizationResources.resx
+
+
+ True
+ True
+ NumericStringUtilsResources.resx
+
+
+ True
+ True
+ UtilResources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ TimeSpanLocalizationResources.Designer.cs
+
+
+ ResXFileCodeGenerator
+ NumericStringUtilsResources.Designer.cs
+
+
+ ResXFileCodeGenerator
+ UtilResources.Designer.cs
+
+
+
diff --git a/src/General/Geo/GoogleMapsUtil.cs b/src/General/Geo/GoogleMapsUtil.cs
new file mode 100644
index 0000000..fcdebbd
--- /dev/null
+++ b/src/General/Geo/GoogleMapsUtil.cs
@@ -0,0 +1,38 @@
+using System;
+
+namespace hydrogen.General.Geo
+{
+ public static class GoogleMapsUtil
+ {
+ public const int DefaultWorldPx = 256;
+
+ public static int GetBoundsZoomLevel(LatLngBounds bounds, int widthPx, int heightPx)
+ {
+ var latFraction = (LatRad(bounds.NorthLat) - LatRad(bounds.SouthLat)) / Math.PI;
+
+ var lngDiff = bounds.EastLng - bounds.WestLng;
+ var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;
+
+ var latZoom = GetFractionZoomLevel(heightPx, DefaultWorldPx, latFraction);
+ var lngZoom = GetFractionZoomLevel(widthPx, DefaultWorldPx, lngFraction);
+
+ return Math.Min(latZoom, Math.Min(lngZoom, 21));
+ }
+
+ public static int GetFractionZoomLevel(int mapPx, int worldPx, double fraction)
+ {
+ return (int)Math.Floor(Math.Log((double)mapPx / worldPx / fraction) / Math.Log(2));
+ }
+
+ #region Private helpers
+
+ private static double LatRad(double lat)
+ {
+ var sin = Math.Sin(lat*Math.PI/180);
+ var radX2 = Math.Log((1 + sin)/(1 - sin))/2;
+ return Math.Max(Math.Min(radX2, Math.PI), -Math.PI)/2;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/General/Geo/LatLng.cs b/src/General/Geo/LatLng.cs
new file mode 100644
index 0000000..85143dc
--- /dev/null
+++ b/src/General/Geo/LatLng.cs
@@ -0,0 +1,21 @@
+
+namespace hydrogen.General.Geo
+{
+ public class LatLng
+ {
+ public static readonly LatLng Zero = new LatLng {Lat = 0, Lng = 0};
+
+ public double Lat { get; set; }
+ public double Lng { get; set; }
+
+ public string ToGoogleApi()
+ {
+ return $"new google.maps.LatLng({Lat}, {Lng})";
+ }
+
+ public string ToWkt()
+ {
+ return $"POINT({Lng} {Lat})";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Geo/LatLngBounds.cs b/src/General/Geo/LatLngBounds.cs
new file mode 100644
index 0000000..ccef9bd
--- /dev/null
+++ b/src/General/Geo/LatLngBounds.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Collections.Generic;
+
+namespace hydrogen.General.Geo
+{
+ public class LatLngBounds
+ {
+ public LatLng NorthEast { get; set; }
+ public LatLng SouthWest { get; set; }
+
+ public double NorthLat
+ {
+ get { return NorthEast.Lat; }
+ set { NorthEast.Lat = value; }
+ }
+
+ public double SouthLat
+ {
+ get { return SouthWest.Lat; }
+ set { SouthWest.Lat = value; }
+ }
+
+ public double EastLng
+ {
+ get { return NorthEast.Lng; }
+ set { NorthEast.Lng = value; }
+ }
+
+ public double WestLng
+ {
+ get { return SouthWest.Lng; }
+ set { SouthWest.Lng = value; }
+ }
+
+ public LatLngBounds()
+ {
+ NorthEast = new LatLng();
+ SouthWest = new LatLng();
+ }
+
+ public void ExtentToContain(LatLng point)
+ {
+ if (point.Lat > NorthLat)
+ NorthLat = point.Lat;
+
+ if (point.Lat < SouthLat)
+ SouthLat = point.Lat;
+
+ if (point.Lng > EastLng)
+ EastLng = point.Lng;
+
+ if (point.Lng < WestLng)
+ WestLng = point.Lng;
+ }
+
+ public LatLng GetCenter()
+ {
+ return new LatLng
+ {
+ Lat = (NorthLat + SouthLat)/2,
+ Lng = (EastLng + WestLng)/2
+ };
+ }
+
+ public LatLngBounds GetSmallestContainingRect()
+ {
+ var center = GetCenter();
+ var latSpan = Math.Abs(NorthLat - SouthLat);
+ var lngSpan = Math.Abs(EastLng - WestLng);
+ var largerSpan = Math.Max(latSpan, lngSpan);
+
+ return new LatLngBounds
+ {
+ NorthEast = new LatLng { Lat = center.Lat + largerSpan/2, Lng = center.Lng + largerSpan/2 },
+ SouthWest = new LatLng { Lat = center.Lat - largerSpan/2, Lng = center.Lng - largerSpan/2 }
+ };
+ }
+
+ public LatLngBounds GetLargestContainedRect()
+ {
+ var center = GetCenter();
+ var latSpan = Math.Abs(NorthLat - SouthLat);
+ var lngSpan = Math.Abs(EastLng - WestLng);
+ var smallerSpan = Math.Min(latSpan, lngSpan);
+
+ return new LatLngBounds
+ {
+ NorthEast = new LatLng { Lat = center.Lat + smallerSpan/2, Lng = center.Lng + smallerSpan/2 },
+ SouthWest = new LatLng { Lat = center.Lat - smallerSpan/2, Lng = center.Lng - smallerSpan/2 }
+ };
+ }
+
+ public string ToGoogleApi()
+ {
+ return $"new google.maps.LatLngBounds({SouthWest.ToGoogleApi()}, {NorthEast.ToGoogleApi()})";
+ }
+
+ public static LatLngBounds BoundsOf(LatLng point)
+ {
+ return new LatLngBounds {NorthEast = point, SouthWest = point};
+ }
+
+ public static LatLngBounds BoundsOf(IEnumerable points)
+ {
+ LatLngBounds result = null;
+
+ foreach (var point in points)
+ {
+ if (point == null)
+ continue;
+
+ if (result == null)
+ result = BoundsOf(point);
+ else
+ result.ExtentToContain(point);
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Geo/LatLngPath.cs b/src/General/Geo/LatLngPath.cs
new file mode 100644
index 0000000..ddb0f01
--- /dev/null
+++ b/src/General/Geo/LatLngPath.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace hydrogen.General.Geo
+{
+ public class LatLngPath
+ {
+ public LatLngPath()
+ {
+ Points = new List();
+ }
+
+ public List Points { get; set; }
+ public string Title { get; set; }
+
+ public LatLngPath SetTitle(string title)
+ {
+ Title = title;
+ return this;
+ }
+
+ public string ToGoogleApi()
+ {
+ return "[" + string.Join(", ", Points.Select(ll => ll.ToGoogleApi())) + "]";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Localization/DateTimeLocalizationExtensions.cs b/src/General/Localization/DateTimeLocalizationExtensions.cs
new file mode 100644
index 0000000..ef1c18d
--- /dev/null
+++ b/src/General/Localization/DateTimeLocalizationExtensions.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace hydrogen.General.Localization
+{
+ public static class DateTimeLocalizationExtensions
+ {
+ public static string ToLocalizedDateString(this DateTime dateTime)
+ {
+ return DateTimeLocalizationUtils.ToLocalizedDateString(dateTime);
+ }
+
+ public static string ToLocalizedDateString(this DateTime? dateTime)
+ {
+ return DateTimeLocalizationUtils.ToLocalizedDateString(dateTime);
+ }
+
+ public static string ToLocalizedTimeString(this DateTime dateTime)
+ {
+ return DateTimeLocalizationUtils.ToLocalizedTimeString(dateTime);
+ }
+
+ public static string ToLocalizedTimeString(this DateTime? dateTime)
+ {
+ return DateTimeLocalizationUtils.ToLocalizedTimeString(dateTime);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Localization/DateTimeLocalizationUtils.cs b/src/General/Localization/DateTimeLocalizationUtils.cs
new file mode 100644
index 0000000..caf6595
--- /dev/null
+++ b/src/General/Localization/DateTimeLocalizationUtils.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Globalization;
+using System.Text.RegularExpressions;
+
+namespace hydrogen.General.Localization
+{
+ public class DateTimeLocalizationUtils
+ {
+ private static readonly PersianCalendar PersianCalendar = new PersianCalendar();
+ private static readonly Regex ShamsiDateRegex = new Regex(@"^(1[34][0-9][0-9])/(0?[1-9]|10|11|12)/(0?[1-9]|[12][0-9]|30|31)$");
+
+ #region Public methods
+
+ public static string ToLocalizedDateString(DateTime dateTime)
+ {
+ // TODO: Make the conversion locale-sensitive
+ return PersianCalendar.GetYear(dateTime).ToString("0000") + "/" +
+ PersianCalendar.GetMonth(dateTime).ToString("00") + "/" +
+ PersianCalendar.GetDayOfMonth(dateTime).ToString("00");
+ }
+
+ public static string ToLocalizedTimeString(DateTime dateTime)
+ {
+ return dateTime.Hour.ToString("00") + ":" + dateTime.Minute.ToString("00");
+ }
+
+ public static string ToLocalizedDateString(DateTime? dateTime)
+ {
+ return dateTime?.ToLocalizedDateString();
+ }
+
+ public static string ToLocalizedTimeString(DateTime? dateTime)
+ {
+ return dateTime?.ToLocalizedTimeString();
+ }
+
+ public static DateTime? FromLocalizedDateString(string input)
+ {
+ var shamsiMatch = ShamsiDateRegex.Match(input);
+ DateTime? result;
+
+ if (shamsiMatch.Success)
+ result = TryParseShamsiDate(shamsiMatch.Groups[1].Value, shamsiMatch.Groups[2].Value, shamsiMatch.Groups[3].Value);
+ else
+ result = TryParseGregorianDate(input);
+
+ return result;
+
+ }
+
+ #endregion
+
+ #region Private helper methods
+
+ private static DateTime? TryParseGregorianDate(string stringValue)
+ {
+ DateTime result;
+ if (!DateTime.TryParse(stringValue, out result))
+ return null;
+
+ return result;
+ }
+
+ private static DateTime? TryParseShamsiDate(string yearString, string monthString, string dayString)
+ {
+ int year;
+ int month;
+ int day;
+
+ if (!int.TryParse(yearString, out year)) return null;
+ if (!int.TryParse(monthString, out month)) return null;
+ if (!int.TryParse(dayString, out day)) return null;
+
+ var calendar = new PersianCalendar();
+ try
+ {
+ return calendar.ToDateTime(year, month, day, 0, 0, 0, 0);
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ return null;
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/General/Localization/EnumExtensions.cs b/src/General/Localization/EnumExtensions.cs
new file mode 100644
index 0000000..d54b5ff
--- /dev/null
+++ b/src/General/Localization/EnumExtensions.cs
@@ -0,0 +1,23 @@
+using System.Resources;
+
+namespace hydrogen.General.Localization
+{
+ public static class EnumExtensions
+ {
+ public static string Label(this TEnum enumObject, ResourceManager resourceManager = null)
+ where TEnum : struct
+ {
+ return resourceManager == null
+ ? enumObject.ToString()
+ : resourceManager.GetString(typeof (TEnum).Name + "_" + enumObject) ??
+ resourceManager.GetString(typeof (TEnum).Name) ??
+ enumObject.ToString();
+ }
+
+ public static string Label(this TEnum? enumObject, ResourceManager resourceManager = null)
+ where TEnum : struct
+ {
+ return enumObject?.Label(resourceManager);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Localization/EnumLocalizationUtils.cs b/src/General/Localization/EnumLocalizationUtils.cs
new file mode 100644
index 0000000..bae92bc
--- /dev/null
+++ b/src/General/Localization/EnumLocalizationUtils.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Resources;
+using hydrogen.General.Text;
+using hydrogen.General.Utils;
+
+namespace hydrogen.General.Localization
+{
+ public static class EnumLocalizationUtils
+ {
+ public static string ToLocalizedString(string fullName, ResourceManager resourceManager = null)
+ {
+ return resourceManager?.GetString(fullName);
+ }
+
+ public static string ToLocalizedString(string enumTypeName, string enumItemName,
+ ResourceManager resourceManager = null)
+ {
+ if (enumItemName.IsNullOrWhitespace())
+ return null;
+
+ if (enumTypeName.IsNullOrWhitespace())
+ return ToLocalizedString(enumItemName, resourceManager);
+
+ return
+ ToLocalizedString(enumTypeName + "_" + enumItemName, resourceManager) ??
+ ToLocalizedString(enumItemName, resourceManager) ??
+ enumItemName;
+ }
+
+ public static string ToLocalizedString(Type enumType, object enumObject, ResourceManager resourceManager = null)
+ {
+ return ToLocalizedString(
+ enumType.IfNotNull(t => t.Name),
+ enumObject.IfNotNull(o => o.ToString()),
+ resourceManager);
+ }
+
+ public static string ToLocalizedString(this TEnum enumObject, ResourceManager resourceManager = null)
+ where TEnum : struct
+ {
+ return ToLocalizedString(typeof(TEnum), enumObject, resourceManager);
+ }
+
+ public static string ToLocalizedString(this TEnum? enumObject, ResourceManager resourceManager = null)
+ where TEnum : struct
+ {
+ return enumObject?.ToLocalizedString(resourceManager);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Localization/TimeSpanLocalizationExtensions.cs b/src/General/Localization/TimeSpanLocalizationExtensions.cs
new file mode 100644
index 0000000..0862af6
--- /dev/null
+++ b/src/General/Localization/TimeSpanLocalizationExtensions.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace hydrogen.General.Localization
+{
+ public static class TimeSpanLocalizationExtensions
+ {
+ public static string ToLocalizedDurationString(this TimeSpan timeSpan, int maxNumberOfParts = 2, string zeroDurationString = null)
+ {
+ return TimeSpanLocalizationUtils.BuildDurationText(timeSpan, maxNumberOfParts, zeroDurationString);
+ }
+
+ public static string ToLocalizedRelativeString(this TimeSpan timeSpan, int maxNumberOfParts = 2, string zeroDurationString = null)
+ {
+ return TimeSpanLocalizationUtils.BuildRelativeText(timeSpan, maxNumberOfParts, zeroDurationString);
+ }
+
+ public static string ToLocalizedDurationString(this TimeSpan? timeSpan, int maxNumberOfParts = 2, string zeroDurationString = null)
+ {
+ return TimeSpanLocalizationUtils.BuildDurationText(timeSpan, maxNumberOfParts, zeroDurationString);
+ }
+
+ public static string ToLocalizedRelativeString(this TimeSpan? timeSpan, int maxNumberOfParts = 2, string zeroDurationString = null)
+ {
+ return TimeSpanLocalizationUtils.BuildRelativeText(timeSpan, maxNumberOfParts, zeroDurationString);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Localization/TimeSpanLocalizationResources.Designer.cs b/src/General/Localization/TimeSpanLocalizationResources.Designer.cs
new file mode 100644
index 0000000..f0e14e4
--- /dev/null
+++ b/src/General/Localization/TimeSpanLocalizationResources.Designer.cs
@@ -0,0 +1,243 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace hydrogen.General.Localization {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class TimeSpanLocalizationResources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal TimeSpanLocalizationResources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("hydrogen.General.Localization.TimeSpanLocalizationResources", typeof(TimeSpanLocalizationResources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} پیش.
+ ///
+ internal static string Ago {
+ get {
+ return ResourceManager.GetString("Ago", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} روز.
+ ///
+ internal static string Days {
+ get {
+ return ResourceManager.GetString("Days", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} ساعت.
+ ///
+ internal static string Hours {
+ get {
+ return ResourceManager.GetString("Hours", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to و .
+ ///
+ internal static string Joiner {
+ get {
+ return ResourceManager.GetString("Joiner", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} بعد.
+ ///
+ internal static string Later {
+ get {
+ return ResourceManager.GetString("Later", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} دقیقه.
+ ///
+ internal static string Minutes {
+ get {
+ return ResourceManager.GetString("Minutes", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} ماه.
+ ///
+ internal static string Months {
+ get {
+ return ResourceManager.GetString("Months", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to یک روز.
+ ///
+ internal static string OneDay {
+ get {
+ return ResourceManager.GetString("OneDay", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to یک ساعت.
+ ///
+ internal static string OneHour {
+ get {
+ return ResourceManager.GetString("OneHour", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to یک دقیقه.
+ ///
+ internal static string OneMinute {
+ get {
+ return ResourceManager.GetString("OneMinute", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to یک ماه.
+ ///
+ internal static string OneMonth {
+ get {
+ return ResourceManager.GetString("OneMonth", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to یک ثانیه.
+ ///
+ internal static string OneSecod {
+ get {
+ return ResourceManager.GetString("OneSecod", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to یک هفته.
+ ///
+ internal static string OneWeek {
+ get {
+ return ResourceManager.GetString("OneWeek", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to یک سال.
+ ///
+ internal static string OneYear {
+ get {
+ return ResourceManager.GetString("OneYear", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to همان لحظه.
+ ///
+ internal static string SameMoment {
+ get {
+ return ResourceManager.GetString("SameMoment", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} ثانیه.
+ ///
+ internal static string Seconds {
+ get {
+ return ResourceManager.GetString("Seconds", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to نامشخص.
+ ///
+ internal static string Unknown {
+ get {
+ return ResourceManager.GetString("Unknown", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} هفته.
+ ///
+ internal static string Weeks {
+ get {
+ return ResourceManager.GetString("Weeks", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} سال.
+ ///
+ internal static string Years {
+ get {
+ return ResourceManager.GetString("Years", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to صفر.
+ ///
+ internal static string ZeroDuration {
+ get {
+ return ResourceManager.GetString("ZeroDuration", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/General/Localization/TimeSpanLocalizationResources.resx b/src/General/Localization/TimeSpanLocalizationResources.resx
new file mode 100644
index 0000000..c882506
--- /dev/null
+++ b/src/General/Localization/TimeSpanLocalizationResources.resx
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ {0} پیش
+
+
+ {0} روز
+
+
+ {0} ساعت
+
+
+ و
+
+
+ {0} بعد
+
+
+ {0} دقیقه
+
+
+ {0} ماه
+
+
+ یک روز
+
+
+ یک ساعت
+
+
+ یک دقیقه
+
+
+ یک ماه
+
+
+ یک ثانیه
+
+
+ یک هفته
+
+
+ یک سال
+
+
+ همان لحظه
+
+
+ {0} ثانیه
+
+
+ نامشخص
+
+
+ {0} هفته
+
+
+ {0} سال
+
+
+ صفر
+
+
\ No newline at end of file
diff --git a/src/General/Localization/TimeSpanLocalizationUtils.cs b/src/General/Localization/TimeSpanLocalizationUtils.cs
new file mode 100644
index 0000000..681248b
--- /dev/null
+++ b/src/General/Localization/TimeSpanLocalizationUtils.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+
+namespace hydrogen.General.Localization
+{
+ public static class TimeSpanLocalizationUtils
+ {
+ public static string BuildRelativeText(TimeSpan? timeSpan, int maxNumberOfParts, string zeroDurationString = null)
+ {
+ if (!timeSpan.HasValue)
+ return TimeSpanLocalizationResources.Unknown;
+
+ return BuildRelativeText(timeSpan.Value, maxNumberOfParts, zeroDurationString);
+ }
+
+ public static string BuildDurationText(TimeSpan? timeSpan, int maxNumberOfParts, string zeroDurationString = null)
+ {
+ if (!timeSpan.HasValue)
+ return TimeSpanLocalizationResources.Unknown;
+
+ return BuildDurationText(timeSpan.Value, maxNumberOfParts, zeroDurationString);
+ }
+
+ public static string BuildRelativeText(TimeSpan timeSpan, int maxNumberOfParts = 2, string zeroDurationString = null)
+ {
+ if (Math.Abs(timeSpan.TotalSeconds) < 1)
+ return zeroDurationString ?? TimeSpanLocalizationResources.SameMoment;
+
+ string result = BuildDurationText(timeSpan, maxNumberOfParts);
+ return string.Format(timeSpan.Ticks < 0 ? TimeSpanLocalizationResources.Ago : TimeSpanLocalizationResources.Later, result);
+ }
+
+ public static string BuildDurationText(TimeSpan timeSpan, int maxNumberOfParts = 2, string zeroDurationString = null)
+ {
+ if (maxNumberOfParts > 7 || maxNumberOfParts < 1)
+ throw new ArgumentException("maxNumberOfParts should be between 1 and 7");
+
+ if (timeSpan.Ticks < 0)
+ timeSpan = timeSpan.Duration();
+
+ var days = timeSpan.Days;
+ int years = days/365;
+ days %= 365;
+ int months = days/30;
+ days %= 30;
+ int weeks = days/7;
+ days %= 7;
+
+ var resultParts = BuildTextArray(years, months, weeks, days, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds, maxNumberOfParts);
+ if (resultParts == null || resultParts.Count < 1)
+ return zeroDurationString ?? TimeSpanLocalizationResources.ZeroDuration;
+
+ return string.Join(TimeSpanLocalizationResources.Joiner, resultParts);
+ }
+
+ private static IList BuildTextArray(int years, int months, int weeks, int days, int hours, int minutes, int seconds, int maxNumberOfParts)
+ {
+ var result = new List(maxNumberOfParts);
+ var remainingNumberOfParts = maxNumberOfParts;
+
+ if (years != 0)
+ {
+ result.Add(years == 1 ? TimeSpanLocalizationResources.OneYear : string.Format(TimeSpanLocalizationResources.Years, years));
+ remainingNumberOfParts--;
+ }
+
+ if (months != 0 && remainingNumberOfParts > 0)
+ {
+ result.Add(months == 1 ? TimeSpanLocalizationResources.OneMonth : string.Format(TimeSpanLocalizationResources.Months, months));
+ remainingNumberOfParts--;
+ }
+
+ if (weeks != 0 && remainingNumberOfParts > 0)
+ {
+ result.Add(weeks == 1 ? TimeSpanLocalizationResources.OneWeek : string.Format(TimeSpanLocalizationResources.Weeks, weeks));
+ remainingNumberOfParts--;
+ }
+
+ if (days != 0 && remainingNumberOfParts > 0)
+ {
+ result.Add(days == 1 ? TimeSpanLocalizationResources.OneDay : string.Format(TimeSpanLocalizationResources.Days, days));
+ remainingNumberOfParts--;
+ }
+
+ if (hours != 0 && remainingNumberOfParts > 0)
+ {
+ result.Add(hours == 1 ? TimeSpanLocalizationResources.OneHour : string.Format(TimeSpanLocalizationResources.Hours, hours));
+ remainingNumberOfParts--;
+ }
+
+ if (minutes != 0 && remainingNumberOfParts > 0)
+ {
+ result.Add(minutes == 1 ? TimeSpanLocalizationResources.OneMinute : string.Format(TimeSpanLocalizationResources.Minutes, minutes));
+ remainingNumberOfParts--;
+ }
+
+ if (seconds != 0 && remainingNumberOfParts > 0)
+ {
+ result.Add(seconds == 1 ? TimeSpanLocalizationResources.OneSecod : string.Format(TimeSpanLocalizationResources.Seconds, seconds));
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Model/IChangeHistoryEntity.cs b/src/General/Model/IChangeHistoryEntity.cs
new file mode 100644
index 0000000..619104e
--- /dev/null
+++ b/src/General/Model/IChangeHistoryEntity.cs
@@ -0,0 +1,6 @@
+namespace hydrogen.General.Model
+{
+ public interface IChangeHistoryEntity
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/General/Model/ICreationTime.cs b/src/General/Model/ICreationTime.cs
new file mode 100644
index 0000000..da7a226
--- /dev/null
+++ b/src/General/Model/ICreationTime.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace hydrogen.General.Model
+{
+ public interface ICreationTime
+ {
+ DateTime CreationTime { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Model/IDecimalIdEntity.cs b/src/General/Model/IDecimalIdEntity.cs
new file mode 100644
index 0000000..c0b13b1
--- /dev/null
+++ b/src/General/Model/IDecimalIdEntity.cs
@@ -0,0 +1,12 @@
+namespace hydrogen.General.Model
+{
+ public interface IDecimalIdEntity
+ {
+ decimal Id { get; }
+ }
+
+ public interface IDecimalIDEntity
+ {
+ decimal Id { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Model/IEntityContent.cs b/src/General/Model/IEntityContent.cs
new file mode 100644
index 0000000..496117f
--- /dev/null
+++ b/src/General/Model/IEntityContent.cs
@@ -0,0 +1,6 @@
+namespace hydrogen.General.Model
+{
+ public interface IEntityContent
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/General/Model/IEntityContentContainer.cs b/src/General/Model/IEntityContentContainer.cs
new file mode 100644
index 0000000..cb51044
--- /dev/null
+++ b/src/General/Model/IEntityContentContainer.cs
@@ -0,0 +1,8 @@
+namespace hydrogen.General.Model
+{
+ public interface IEntityContentContainer where TContent : class, IEntityContent
+ {
+ string ContentString { get; set; }
+ TContent Content { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Model/IIndexedEntity.cs b/src/General/Model/IIndexedEntity.cs
new file mode 100644
index 0000000..a8ceb1e
--- /dev/null
+++ b/src/General/Model/IIndexedEntity.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace hydrogen.General.Model
+{
+ public interface IIndexedEntity
+ {
+ DateTime? IndexedTime { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Model/ILastModificationTime.cs b/src/General/Model/ILastModificationTime.cs
new file mode 100644
index 0000000..2f276a8
--- /dev/null
+++ b/src/General/Model/ILastModificationTime.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace hydrogen.General.Model
+{
+ public interface ILastModificationTime
+ {
+ DateTime LastModificationTime { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Model/ISimpleIDEntity.cs b/src/General/Model/ISimpleIDEntity.cs
new file mode 100644
index 0000000..2eebf18
--- /dev/null
+++ b/src/General/Model/ISimpleIDEntity.cs
@@ -0,0 +1,12 @@
+namespace hydrogen.General.Model
+{
+ public interface ISimpleIDEntity
+ {
+ long ID { get; }
+ }
+
+ public interface ISimpleIdEntity
+ {
+ long Id { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Model/IStateTime.cs b/src/General/Model/IStateTime.cs
new file mode 100644
index 0000000..dc83bd2
--- /dev/null
+++ b/src/General/Model/IStateTime.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace hydrogen.General.Model
+{
+ public interface IStateTime
+ {
+ DateTime StateTime { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Model/PagedListOutput.cs b/src/General/Model/PagedListOutput.cs
new file mode 100644
index 0000000..e900b82
--- /dev/null
+++ b/src/General/Model/PagedListOutput.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace hydrogen.General.Model
+{
+ public class PagedListOutput
+ {
+ public List PageItems { get; set; }
+ public int PageNumber { get; set; }
+ public int TotalNumberOfPages { get; set; }
+ public int TotalNumberOfItems { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Security/CryptoRandomNumberUtil.cs b/src/General/Security/CryptoRandomNumberUtil.cs
new file mode 100644
index 0000000..4aa2b65
--- /dev/null
+++ b/src/General/Security/CryptoRandomNumberUtil.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace hydrogen.General.Security
+{
+ public class CryptoRandomNumberUtil
+ {
+ private static readonly RandomNumberGenerator RandomNumberGenerator = new RNGCryptoServiceProvider();
+
+ public static int GetInt32()
+ {
+ byte[] resultBytes = GetBytes(4);
+ return BitConverter.ToInt32(resultBytes, 0);
+ }
+
+ public static int GetInt32(int min, int max)
+ {
+ if (max < min)
+ throw new ArgumentException("Invalid range");
+
+ return (Math.Abs(GetInt32()%(max - min + 1))) + min;
+ }
+
+ public static byte[] GetBytes(int length, bool nonZeroOnly = false)
+ {
+ var randomBytes = new byte[length];
+ if (nonZeroOnly)
+ RandomNumberGenerator.GetNonZeroBytes(randomBytes);
+ else
+ RandomNumberGenerator.GetBytes(randomBytes);
+
+ return randomBytes;
+ }
+
+ public static string GetNumericString(int length)
+ {
+ var randomBytes = GetBytes(length);
+ var result = new StringBuilder();
+
+ foreach (byte b in randomBytes)
+ result.Append(b%10);
+
+ return result.ToString();
+ }
+
+ public static string GetAlphaNumericString(int length)
+ {
+ var randomBytes = GetBytes(length);
+ var result = new StringBuilder();
+
+ foreach (byte b in randomBytes)
+ {
+ int i = b%36;
+
+ if (i < 10)
+ result.Append(i);
+ else
+ result.Append((char)('a' + (i - 10)));
+ }
+
+ return result.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Streams/ManifestResourceUtil.cs b/src/General/Streams/ManifestResourceUtil.cs
new file mode 100644
index 0000000..341ce2f
--- /dev/null
+++ b/src/General/Streams/ManifestResourceUtil.cs
@@ -0,0 +1,23 @@
+using System.IO;
+using System.Reflection;
+
+namespace hydrogen.General.Streams
+{
+ public static class ManifestResourceUtil
+ {
+ public static byte[] GetManifestResourceBytes(Assembly assembly, string manifestResourceName)
+ {
+ using (var resourceStream = assembly.GetManifestResourceStream(manifestResourceName))
+ {
+ if (resourceStream == null)
+ return null;
+
+ using (var memStream = new MemoryStream())
+ {
+ resourceStream.CopyTo(memStream);
+ return memStream.ToArray();
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Text/Base32.cs b/src/General/Text/Base32.cs
new file mode 100644
index 0000000..498da9d
--- /dev/null
+++ b/src/General/Text/Base32.cs
@@ -0,0 +1,129 @@
+using System;
+
+namespace hydrogen.General.Text
+{
+ public class Base32
+ {
+ public static byte[] ToBytes(string input)
+ {
+ if (string.IsNullOrEmpty(input))
+ {
+ throw new ArgumentNullException(nameof(input));
+ }
+
+ input = input.TrimEnd('='); //remove padding characters
+ int byteCount = input.Length * 5 / 8; //this must be TRUNCATED
+ byte[] returnArray = new byte[byteCount];
+
+ byte curByte = 0, bitsRemaining = 8;
+ int arrayIndex = 0;
+
+ foreach (char c in input)
+ {
+ int cValue = CharToValue(c);
+
+ int mask;
+ if (bitsRemaining > 5)
+ {
+ mask = cValue << (bitsRemaining - 5);
+ curByte = (byte)(curByte | mask);
+ bitsRemaining -= 5;
+ }
+ else
+ {
+ mask = cValue >> (5 - bitsRemaining);
+ curByte = (byte)(curByte | mask);
+ returnArray[arrayIndex++] = curByte;
+ curByte = (byte)(cValue << (3 + bitsRemaining));
+ bitsRemaining += 3;
+ }
+ }
+
+ //if we didn't end with a full byte
+ if (arrayIndex != byteCount)
+ {
+ returnArray[arrayIndex] = curByte;
+ }
+
+ return returnArray;
+ }
+
+ public static string ToString(byte[] input)
+ {
+ if (input == null || input.Length == 0)
+ {
+ throw new ArgumentNullException(nameof(input));
+ }
+
+ int charCount = (int)Math.Ceiling(input.Length / 5d) * 8;
+ char[] returnArray = new char[charCount];
+
+ byte nextChar = 0, bitsRemaining = 5;
+ int arrayIndex = 0;
+
+ foreach (byte b in input)
+ {
+ nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));
+ returnArray[arrayIndex++] = ValueToChar(nextChar);
+
+ if (bitsRemaining < 4)
+ {
+ nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);
+ returnArray[arrayIndex++] = ValueToChar(nextChar);
+ bitsRemaining += 5;
+ }
+
+ bitsRemaining -= 3;
+ nextChar = (byte)((b << bitsRemaining) & 31);
+ }
+
+ //if we didn't end with a full char
+ if (arrayIndex != charCount)
+ {
+ returnArray[arrayIndex++] = ValueToChar(nextChar);
+ while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; //padding
+ }
+
+ return new string(returnArray);
+ }
+
+ private static int CharToValue(char c)
+ {
+ int value = c;
+
+ //65-90 == uppercase letters
+ if (value < 91 && value > 64)
+ {
+ return value - 65;
+ }
+ //50-55 == numbers 2-7
+ if (value < 56 && value > 49)
+ {
+ return value - 24;
+ }
+ //97-122 == lowercase letters
+ if (value < 123 && value > 96)
+ {
+ return value - 97;
+ }
+
+ throw new ArgumentException("Character is not a Base32 character.", nameof(c));
+ }
+
+ private static char ValueToChar(byte b)
+ {
+ if (b < 26)
+ {
+ return (char)(b + 65);
+ }
+
+ if (b < 32)
+ {
+ return (char)(b + 24);
+ }
+
+ throw new ArgumentException("Byte is not a value Base32 value.", nameof(b));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/General/Text/Base64Url.cs b/src/General/Text/Base64Url.cs
new file mode 100644
index 0000000..8ee1243
--- /dev/null
+++ b/src/General/Text/Base64Url.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Text;
+
+namespace hydrogen.General.Text
+{
+ ///
+ /// Modified Base64 for URL applications ('base64url' encoding)
+ ///
+ /// See http://tools.ietf.org/html/rfc4648
+ /// For more information see http://en.wikipedia.org/wiki/Base64
+ ///
+ public class Base64Url
+ {
+ ///
+ /// Modified Base64 for URL applications ('base64url' encoding)
+ ///
+ /// See http://tools.ietf.org/html/rfc4648
+ /// For more information see http://en.wikipedia.org/wiki/Base64
+ ///
+ ///
+ /// Input byte array converted to a base64ForUrl encoded string
+ public static string ToBase64ForUrlString(byte[] input)
+ {
+ StringBuilder result = new StringBuilder(Convert.ToBase64String(input).TrimEnd('='));
+ result.Replace('+', '-');
+ result.Replace('/', '_');
+ return result.ToString();
+ }
+ ///
+ /// Modified Base64 for URL applications ('base64url' encoding)
+ ///
+ /// See http://tools.ietf.org/html/rfc4648
+ /// For more information see http://en.wikipedia.org/wiki/Base64
+ ///
+ ///
+ /// Input base64ForUrl encoded string as the original byte array
+ public static byte[] FromBase64ForUrlString(string base64ForUrlInput)
+ {
+ int padChars = (base64ForUrlInput.Length % 4) == 0 ? 0 : (4 - (base64ForUrlInput.Length % 4));
+ StringBuilder result = new StringBuilder(base64ForUrlInput, base64ForUrlInput.Length + padChars);
+ result.Append(String.Empty.PadRight(padChars, '='));
+ result.Replace('-', '+');
+ result.Replace('_', '/');
+ return Convert.FromBase64String(result.ToString());
+ }
+ }
+}
diff --git a/src/General/Text/DigitLocalizationUtils.cs b/src/General/Text/DigitLocalizationUtils.cs
new file mode 100644
index 0000000..77b7db3
--- /dev/null
+++ b/src/General/Text/DigitLocalizationUtils.cs
@@ -0,0 +1,37 @@
+using System.Text;
+
+namespace hydrogen.General.Text
+{
+ public static class DigitLocalizationUtils
+ {
+ public static string ToEnglish(string input)
+ {
+ var result = new StringBuilder(input);
+ for (int i = 0; i < result.Length; i++)
+ {
+ if (result[i] >= 0x0660 && result[i] <= 0x669)
+ result[i] = (char) (result[i] + '0' - 0x0660);
+
+ if (result[i] >= 0x06F0 && result[i] <= 0x6F9)
+ result[i] = (char) (result[i] + '0' - 0x06F0);
+ }
+
+ return result.ToString();
+ }
+
+ public static string ToPersian(string input)
+ {
+ var result = new StringBuilder(input);
+ for (int i = 0; i < result.Length; i++)
+ {
+ if (result[i] >= '0' && result[i] <= '9')
+ result[i] = (char) (result[i] + 0x06F0 - '0');
+
+ if (result[i] >= 0x0660 && result[i] <= 0x669)
+ result[i] = (char) (result[i] + 0x06F0 - 0x0660);
+ }
+
+ return result.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Text/EmailUtils.cs b/src/General/Text/EmailUtils.cs
new file mode 100644
index 0000000..57d42a4
--- /dev/null
+++ b/src/General/Text/EmailUtils.cs
@@ -0,0 +1,22 @@
+using System.Text.RegularExpressions;
+
+namespace hydrogen.General.Text
+{
+ public static class EmailUtils
+ {
+ private const string EmailRegexString = @"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?";
+
+ private static readonly Regex EmailRegex = new Regex(EmailRegexString);
+ private static readonly Regex WholeEmailRegex = new Regex(RegexUtils.CreateWholeInputRegex(EmailRegexString));
+
+ public static bool IsValidEmail(string email, bool allowPartial)
+ {
+ return (allowPartial ? EmailRegex : WholeEmailRegex).IsMatch(email);
+ }
+
+ public static bool IsValidEmail(string email)
+ {
+ return IsValidEmail(email, false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Text/NumericStringUtils.cs b/src/General/Text/NumericStringUtils.cs
new file mode 100644
index 0000000..e5ce93c
--- /dev/null
+++ b/src/General/Text/NumericStringUtils.cs
@@ -0,0 +1,152 @@
+using System;
+using System.Globalization;
+
+namespace hydrogen.General.Text
+{
+ public static class NumericStringUtils
+ {
+ private const string CardinalTextualNumberResourcePrefix = "CardinalTextual";
+ private const string OrdinalTextualNumberResourcePrefix = "OrdinalTextual";
+
+ public static string ShortNumericString(decimal? input)
+ {
+ if (!input.HasValue)
+ return null;
+
+ if (input == 0)
+ return NumericStringUtilsResources.CardinalTextual0;
+
+ decimal number = input.Value;
+ string format = NumericStringUtilsResources.NumericOnes;
+
+ if (number >= 1000)
+ {
+ number /= 1000;
+ format = NumericStringUtilsResources.NumericThousands;
+ }
+
+ if (number >= 1000)
+ {
+ number /= 1000;
+ format = NumericStringUtilsResources.NumericMillions;
+ }
+
+ if (number >= 1000)
+ {
+ number /= 1000;
+ format = NumericStringUtilsResources.NumericBillions;
+ }
+
+ number = Math.Round(number, Math.Max(3 - CountIntegralDigits(number), 0));
+ return string.Format(format, number);
+ }
+
+ public static string FullyTextualNumber(decimal? input)
+ {
+ if (!input.HasValue)
+ return null;
+
+ bool negative = input.Value < 0;
+ input = Math.Abs(input.Value);
+
+ // Fractions are not supported for now.
+ long integerPart = Convert.ToInt32(input);
+
+ // Only implementing numbers under 1000 for now
+ if (input > 1000)
+ return ShortNumericString(input);
+
+ string result = FullyTextualNumber3Digits(integerPart, false);
+ return negative ? string.Format(NumericStringUtilsResources.TextualNegativeFormat, result) : result;
+ }
+
+ public static string FullyTextualOrdinalNumber(long? input)
+ {
+ if (!input.HasValue)
+ return null;
+
+ bool negative = input.Value < 0;
+ input = Math.Abs(input.Value);
+
+ // Only implementing numbers under 1000 for now
+ string result;
+
+ if (input.Value == 1)
+ result = NumericStringUtilsResources.First;
+ else if (input > 1000)
+ result = string.Format(NumericStringUtilsResources.GenericOrdinalFormat, ShortNumericString(input));
+ else
+ result = FullyTextualNumber3Digits(input.Value, true);
+
+ return negative ? string.Format(NumericStringUtilsResources.TextualNegativeFormat, result) : result;
+ }
+
+ public static String BytesToString(long byteCount)
+ {
+ // Copied from the following SO question:
+ // http://stackoverflow.com/questions/281640/how-do-i-get-a-human-readable-file-size-in-bytes-abbreviation-using-net
+
+ string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; //Longs run out around EB
+ if (byteCount == 0)
+ return "0 " + suf[0];
+
+ long bytes = Math.Abs(byteCount);
+ int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
+ double num = Math.Round(bytes / Math.Pow(1024, place), 1);
+ return (Math.Sign(byteCount) * num).ToString(CultureInfo.InvariantCulture) + " " + suf[place];
+ }
+
+ #region Private helper methods
+
+ private static int CountIntegralDigits(decimal input)
+ {
+ int result = 1;
+ while (input >= 10)
+ {
+ result++;
+ input /= 10;
+ }
+
+ return result;
+ }
+
+ private static string FullyTextualNumber2Digits(long input, bool ordinal)
+ {
+ if (input <= 20)
+ return NumericStringUtilsResources.ResourceManager.GetString(GetTextualNumberResourcePrefix(ordinal) + input);
+
+ long ones = input%10;
+ long tens = input - ones;
+
+ if (ones == 0)
+ return NumericStringUtilsResources.ResourceManager.GetString(GetTextualNumberResourcePrefix(ordinal) + tens);
+
+ return NumericStringUtilsResources.ResourceManager.GetString(GetTextualNumberResourcePrefix(false) + tens) +
+ NumericStringUtilsResources.TextualJointer +
+ NumericStringUtilsResources.ResourceManager.GetString(GetTextualNumberResourcePrefix(ordinal) + ones);
+ }
+
+ private static string FullyTextualNumber3Digits(long input, bool ordinal)
+ {
+ if (input < 100)
+ return FullyTextualNumber2Digits(input, ordinal);
+
+ long ones = input%100;
+ long hundreds = input - ones;
+
+ if (ones == 0)
+ return NumericStringUtilsResources.ResourceManager.GetString(GetTextualNumberResourcePrefix(ordinal) + hundreds);
+
+ return NumericStringUtilsResources.ResourceManager.GetString(GetTextualNumberResourcePrefix(false) + hundreds) +
+ NumericStringUtilsResources.TextualJointer +
+ FullyTextualNumber2Digits(ones, ordinal);
+ }
+
+ private static string GetTextualNumberResourcePrefix(bool ordinal)
+ {
+ return ordinal ? OrdinalTextualNumberResourcePrefix : CardinalTextualNumberResourcePrefix;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/General/Text/NumericStringUtilsResources.Designer.cs b/src/General/Text/NumericStringUtilsResources.Designer.cs
new file mode 100644
index 0000000..7a0acca
--- /dev/null
+++ b/src/General/Text/NumericStringUtilsResources.Designer.cs
@@ -0,0 +1,819 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace hydrogen.General.Text {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class NumericStringUtilsResources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal NumericStringUtilsResources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("hydrogen.General.Text.NumericStringUtilsResources", typeof(NumericStringUtilsResources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to صفر.
+ ///
+ internal static string CardinalTextual0 {
+ get {
+ return ResourceManager.GetString("CardinalTextual0", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to یک.
+ ///
+ internal static string CardinalTextual1 {
+ get {
+ return ResourceManager.GetString("CardinalTextual1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to ده.
+ ///
+ internal static string CardinalTextual10 {
+ get {
+ return ResourceManager.GetString("CardinalTextual10", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to صد.
+ ///
+ internal static string CardinalTextual100 {
+ get {
+ return ResourceManager.GetString("CardinalTextual100", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هزار.
+ ///
+ internal static string CardinalTextual1000 {
+ get {
+ return ResourceManager.GetString("CardinalTextual1000", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to یازده.
+ ///
+ internal static string CardinalTextual11 {
+ get {
+ return ResourceManager.GetString("CardinalTextual11", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to دوازده.
+ ///
+ internal static string CardinalTextual12 {
+ get {
+ return ResourceManager.GetString("CardinalTextual12", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to سیزده.
+ ///
+ internal static string CardinalTextual13 {
+ get {
+ return ResourceManager.GetString("CardinalTextual13", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to چهارده.
+ ///
+ internal static string CardinalTextual14 {
+ get {
+ return ResourceManager.GetString("CardinalTextual14", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to پانزده.
+ ///
+ internal static string CardinalTextual15 {
+ get {
+ return ResourceManager.GetString("CardinalTextual15", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to شانزده.
+ ///
+ internal static string CardinalTextual16 {
+ get {
+ return ResourceManager.GetString("CardinalTextual16", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هفده.
+ ///
+ internal static string CardinalTextual17 {
+ get {
+ return ResourceManager.GetString("CardinalTextual17", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هجده.
+ ///
+ internal static string CardinalTextual18 {
+ get {
+ return ResourceManager.GetString("CardinalTextual18", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to نوزده.
+ ///
+ internal static string CardinalTextual19 {
+ get {
+ return ResourceManager.GetString("CardinalTextual19", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to دو.
+ ///
+ internal static string CardinalTextual2 {
+ get {
+ return ResourceManager.GetString("CardinalTextual2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to بیست.
+ ///
+ internal static string CardinalTextual20 {
+ get {
+ return ResourceManager.GetString("CardinalTextual20", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to دویست.
+ ///
+ internal static string CardinalTextual200 {
+ get {
+ return ResourceManager.GetString("CardinalTextual200", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to سه.
+ ///
+ internal static string CardinalTextual3 {
+ get {
+ return ResourceManager.GetString("CardinalTextual3", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to سی.
+ ///
+ internal static string CardinalTextual30 {
+ get {
+ return ResourceManager.GetString("CardinalTextual30", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to سیصد.
+ ///
+ internal static string CardinalTextual300 {
+ get {
+ return ResourceManager.GetString("CardinalTextual300", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to چهار.
+ ///
+ internal static string CardinalTextual4 {
+ get {
+ return ResourceManager.GetString("CardinalTextual4", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to چهل.
+ ///
+ internal static string CardinalTextual40 {
+ get {
+ return ResourceManager.GetString("CardinalTextual40", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to چهارصد.
+ ///
+ internal static string CardinalTextual400 {
+ get {
+ return ResourceManager.GetString("CardinalTextual400", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to پنج.
+ ///
+ internal static string CardinalTextual5 {
+ get {
+ return ResourceManager.GetString("CardinalTextual5", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to پنجاه.
+ ///
+ internal static string CardinalTextual50 {
+ get {
+ return ResourceManager.GetString("CardinalTextual50", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to پانصد.
+ ///
+ internal static string CardinalTextual500 {
+ get {
+ return ResourceManager.GetString("CardinalTextual500", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to شش.
+ ///
+ internal static string CardinalTextual6 {
+ get {
+ return ResourceManager.GetString("CardinalTextual6", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to شصت.
+ ///
+ internal static string CardinalTextual60 {
+ get {
+ return ResourceManager.GetString("CardinalTextual60", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to ششصد.
+ ///
+ internal static string CardinalTextual600 {
+ get {
+ return ResourceManager.GetString("CardinalTextual600", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هفت.
+ ///
+ internal static string CardinalTextual7 {
+ get {
+ return ResourceManager.GetString("CardinalTextual7", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هفتاد.
+ ///
+ internal static string CardinalTextual70 {
+ get {
+ return ResourceManager.GetString("CardinalTextual70", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هفتصد.
+ ///
+ internal static string CardinalTextual700 {
+ get {
+ return ResourceManager.GetString("CardinalTextual700", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هشت.
+ ///
+ internal static string CardinalTextual8 {
+ get {
+ return ResourceManager.GetString("CardinalTextual8", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هشتاد.
+ ///
+ internal static string CardinalTextual80 {
+ get {
+ return ResourceManager.GetString("CardinalTextual80", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هشتصد.
+ ///
+ internal static string CardinalTextual800 {
+ get {
+ return ResourceManager.GetString("CardinalTextual800", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to نه.
+ ///
+ internal static string CardinalTextual9 {
+ get {
+ return ResourceManager.GetString("CardinalTextual9", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to نود.
+ ///
+ internal static string CardinalTextual90 {
+ get {
+ return ResourceManager.GetString("CardinalTextual90", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to نهصد.
+ ///
+ internal static string CardinalTextual900 {
+ get {
+ return ResourceManager.GetString("CardinalTextual900", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to اول.
+ ///
+ internal static string First {
+ get {
+ return ResourceManager.GetString("First", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} ام.
+ ///
+ internal static string GenericOrdinalFormat {
+ get {
+ return ResourceManager.GetString("GenericOrdinalFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0:#.##} میلیارد.
+ ///
+ internal static string NumericBillions {
+ get {
+ return ResourceManager.GetString("NumericBillions", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0:#.##} میلیون.
+ ///
+ internal static string NumericMillions {
+ get {
+ return ResourceManager.GetString("NumericMillions", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0:#.##}.
+ ///
+ internal static string NumericOnes {
+ get {
+ return ResourceManager.GetString("NumericOnes", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0:#.##} هزار.
+ ///
+ internal static string NumericThousands {
+ get {
+ return ResourceManager.GetString("NumericThousands", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to صفرم.
+ ///
+ internal static string OrdinalTextual0 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual0", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to یکم.
+ ///
+ internal static string OrdinalTextual1 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to دهم.
+ ///
+ internal static string OrdinalTextual10 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual10", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to صدم.
+ ///
+ internal static string OrdinalTextual100 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual100", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هزارم.
+ ///
+ internal static string OrdinalTextual1000 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual1000", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to یازدهم.
+ ///
+ internal static string OrdinalTextual11 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual11", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to دوازدهم.
+ ///
+ internal static string OrdinalTextual12 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual12", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to سیزدهم.
+ ///
+ internal static string OrdinalTextual13 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual13", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to چهاردهم.
+ ///
+ internal static string OrdinalTextual14 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual14", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to پانزدهم.
+ ///
+ internal static string OrdinalTextual15 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual15", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to شانزدهم.
+ ///
+ internal static string OrdinalTextual16 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual16", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هفدهم.
+ ///
+ internal static string OrdinalTextual17 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual17", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هجدهم.
+ ///
+ internal static string OrdinalTextual18 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual18", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to نوزدهم.
+ ///
+ internal static string OrdinalTextual19 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual19", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to دوم.
+ ///
+ internal static string OrdinalTextual2 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to بیستم.
+ ///
+ internal static string OrdinalTextual20 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual20", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to دویستم.
+ ///
+ internal static string OrdinalTextual200 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual200", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to سوم.
+ ///
+ internal static string OrdinalTextual3 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual3", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to سی ام.
+ ///
+ internal static string OrdinalTextual30 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual30", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to سیصدم.
+ ///
+ internal static string OrdinalTextual300 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual300", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to چهارم.
+ ///
+ internal static string OrdinalTextual4 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual4", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to چهلم.
+ ///
+ internal static string OrdinalTextual40 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual40", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to چهارصدم.
+ ///
+ internal static string OrdinalTextual400 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual400", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to پنجم.
+ ///
+ internal static string OrdinalTextual5 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual5", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to پنجاهم.
+ ///
+ internal static string OrdinalTextual50 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual50", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to پانصدم.
+ ///
+ internal static string OrdinalTextual500 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual500", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to ششم.
+ ///
+ internal static string OrdinalTextual6 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual6", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to شصتم.
+ ///
+ internal static string OrdinalTextual60 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual60", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to ششصدم.
+ ///
+ internal static string OrdinalTextual600 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual600", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هفتم.
+ ///
+ internal static string OrdinalTextual7 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual7", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هفتادم.
+ ///
+ internal static string OrdinalTextual70 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual70", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هفتصدم.
+ ///
+ internal static string OrdinalTextual700 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual700", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هشتم.
+ ///
+ internal static string OrdinalTextual8 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual8", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هشتادم.
+ ///
+ internal static string OrdinalTextual80 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual80", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to هشتصدم.
+ ///
+ internal static string OrdinalTextual800 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual800", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to نهم.
+ ///
+ internal static string OrdinalTextual9 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual9", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to نودم.
+ ///
+ internal static string OrdinalTextual90 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual90", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to نهصدم.
+ ///
+ internal static string OrdinalTextual900 {
+ get {
+ return ResourceManager.GetString("OrdinalTextual900", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to و .
+ ///
+ internal static string TextualJointer {
+ get {
+ return ResourceManager.GetString("TextualJointer", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to منهای {0}.
+ ///
+ internal static string TextualNegativeFormat {
+ get {
+ return ResourceManager.GetString("TextualNegativeFormat", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/General/Text/NumericStringUtilsResources.resx b/src/General/Text/NumericStringUtilsResources.resx
new file mode 100644
index 0000000..fd95c60
--- /dev/null
+++ b/src/General/Text/NumericStringUtilsResources.resx
@@ -0,0 +1,372 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ صفر
+
+
+ یک
+
+
+ ده
+
+
+ صد
+
+
+ هزار
+
+
+ یازده
+
+
+ دوازده
+
+
+ سیزده
+
+
+ چهارده
+
+
+ پانزده
+
+
+ شانزده
+
+
+ هفده
+
+
+ هجده
+
+
+ نوزده
+
+
+ دو
+
+
+ بیست
+
+
+ دویست
+
+
+ سه
+
+
+ سی
+
+
+ سیصد
+
+
+ چهار
+
+
+ چهل
+
+
+ چهارصد
+
+
+ پنج
+
+
+ پنجاه
+
+
+ پانصد
+
+
+ شش
+
+
+ شصت
+
+
+ ششصد
+
+
+ هفت
+
+
+ هفتاد
+
+
+ هفتصد
+
+
+ هشت
+
+
+ هشتاد
+
+
+ هشتصد
+
+
+ نه
+
+
+ نود
+
+
+ نهصد
+
+
+ اول
+
+
+ {0} ام
+
+
+ {0:#.##} میلیارد
+
+
+ {0:#.##} میلیون
+
+
+ {0:#.##}
+
+
+ {0:#.##} هزار
+
+
+ صفرم
+
+
+ یکم
+
+
+ دهم
+
+
+ صدم
+
+
+ هزارم
+
+
+ یازدهم
+
+
+ دوازدهم
+
+
+ سیزدهم
+
+
+ چهاردهم
+
+
+ پانزدهم
+
+
+ شانزدهم
+
+
+ هفدهم
+
+
+ هجدهم
+
+
+ نوزدهم
+
+
+ دوم
+
+
+ بیستم
+
+
+ دویستم
+
+
+ سوم
+
+
+ سی ام
+
+
+ سیصدم
+
+
+ چهارم
+
+
+ چهلم
+
+
+ چهارصدم
+
+
+ پنجم
+
+
+ پنجاهم
+
+
+ پانصدم
+
+
+ ششم
+
+
+ شصتم
+
+
+ ششصدم
+
+
+ هفتم
+
+
+ هفتادم
+
+
+ هفتصدم
+
+
+ هشتم
+
+
+ هشتادم
+
+
+ هشتصدم
+
+
+ نهم
+
+
+ نودم
+
+
+ نهصدم
+
+
+ و
+
+
+ منهای {0}
+
+
\ No newline at end of file
diff --git a/src/General/Text/PersianCharacterNormalizer.cs b/src/General/Text/PersianCharacterNormalizer.cs
new file mode 100644
index 0000000..024763d
--- /dev/null
+++ b/src/General/Text/PersianCharacterNormalizer.cs
@@ -0,0 +1,53 @@
+using System;
+
+namespace hydrogen.General.Text
+{
+ ///
+ /// Source code copied from PersianNormalizer class within Lucene.Net
+ ///
+ public class PersianCharacterNormalizer
+ {
+ public const char Yeh = 'ي';
+ public const char FarsiYeh = 'ی';
+ public const char YehBarree = 'ے';
+ public const char Keheh = 'ک';
+ public const char Kaf = 'ك';
+ public const char HamzaAbove = 'ٔ';
+ public const char HehYeh = 'ۀ';
+ public const char HehGoal = 'ہ';
+ public const char Heh = 'ه';
+
+ public static int Normalize(char[] s, int len)
+ {
+ for (int pos = 0; pos < len; ++pos)
+ {
+ switch (s[pos])
+ {
+ case 'ۀ':
+ case 'ہ':
+ s[pos] = 'ه';
+ break;
+ case 'ی':
+ case 'ے':
+ s[pos] = 'ي';
+ break;
+ case 'ٔ':
+ len = Delete(s, pos, len);
+ --pos;
+ break;
+ case 'ک':
+ s[pos] = 'ك';
+ break;
+ }
+ }
+ return len;
+ }
+
+ private static int Delete(char[] s, int pos, int len)
+ {
+ if (pos < len)
+ Array.Copy(s, pos + 1, s, pos, len - pos - 1);
+ return len - 1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Text/RegexCache.cs b/src/General/Text/RegexCache.cs
new file mode 100644
index 0000000..aabd990
--- /dev/null
+++ b/src/General/Text/RegexCache.cs
@@ -0,0 +1,74 @@
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
+using hydrogen.General.Collections;
+
+namespace hydrogen.General.Text
+{
+ public class RegexCache
+ {
+ private readonly LruCache _cache;
+
+ public RegexCache(int size)
+ {
+ _cache = new LruCache(size);
+ }
+
+ public Regex GetPatternForRegex(string regex)
+ {
+ Regex pattern = _cache.Get(regex);
+ if (pattern == null)
+ {
+ pattern = new Regex(regex);
+ _cache.Put(regex, pattern);
+ }
+ return pattern;
+ }
+
+ // This method is used for testing.
+ public bool ContainsRegex(string regex)
+ {
+ return _cache.ContainsKey(regex);
+ }
+
+ private class LruCache
+ {
+ // LinkedHashMap offers a straightforward implementation of LRU cache.
+ private readonly LruDictionary _map;
+ private readonly int _size;
+
+ public LruCache(int size)
+ {
+ _size = size;
+ _map = new LruDictionary();
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public TV Get(TK key)
+ {
+ TV result;
+ if (!_map.TryGetValue(key, out result))
+ return default(TV);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public void Put(TK key, TV value)
+ {
+ _map[key] = value;
+ if (_map.Count > _size)
+ {
+ var first = _map.Keys.First();
+ _map.Remove(first);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public bool ContainsKey(TK key)
+ {
+ return _map.ContainsKey(key);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Text/RegexExtensions.cs b/src/General/Text/RegexExtensions.cs
new file mode 100644
index 0000000..d107b04
--- /dev/null
+++ b/src/General/Text/RegexExtensions.cs
@@ -0,0 +1,27 @@
+using System.Text.RegularExpressions;
+
+namespace hydrogen.General.Text
+{
+ public static class RegexExtensions
+ {
+ public static bool IsMatchWhole(this Regex regex, string input)
+ {
+ return regex.Match(input).SuccessWholeInput(input);
+ }
+
+ public static bool IsMatchStart(this Regex regex, string input)
+ {
+ return regex.Match(input).SuccessInputStart();
+ }
+
+ public static bool SuccessWholeInput(this Match match, string input)
+ {
+ return match.Success && match.Index == 0 && match.Length == input.Length;
+ }
+
+ public static bool SuccessInputStart(this Match match)
+ {
+ return match.Success && match.Index == 0;
+ }
+ }
+}
diff --git a/src/General/Text/RegexUtils.cs b/src/General/Text/RegexUtils.cs
new file mode 100644
index 0000000..cabf45f
--- /dev/null
+++ b/src/General/Text/RegexUtils.cs
@@ -0,0 +1,10 @@
+namespace hydrogen.General.Text
+{
+ public static class RegexUtils
+ {
+ public static string CreateWholeInputRegex(string regex)
+ {
+ return "\\A(?:" + regex + ")\\z";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Text/SmsMessageUtils.cs b/src/General/Text/SmsMessageUtils.cs
new file mode 100644
index 0000000..50e6317
--- /dev/null
+++ b/src/General/Text/SmsMessageUtils.cs
@@ -0,0 +1,19 @@
+namespace hydrogen.General.Text
+{
+ public static class SmsMessageUtils
+ {
+ public const int MaxUnicodeFirstSegmentLength = 70;
+ public const int MaxUnicodeSegmentLength = 67;
+
+ public static int CalculateNumberOfSegments(string messageText)
+ {
+ if (messageText == null)
+ return 0;
+
+ if (messageText.Length <= MaxUnicodeFirstSegmentLength)
+ return 1;
+
+ return (messageText.Length + MaxUnicodeSegmentLength - 1)/MaxUnicodeSegmentLength;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Text/StringBuilderExtensions.cs b/src/General/Text/StringBuilderExtensions.cs
new file mode 100644
index 0000000..f8d3b11
--- /dev/null
+++ b/src/General/Text/StringBuilderExtensions.cs
@@ -0,0 +1,23 @@
+using System.Text;
+
+namespace hydrogen.General.Text
+{
+ public static class StringBuilderExtensions
+ {
+ public static StringBuilder AppendSeparator(this StringBuilder builder, string separator)
+ {
+ if (builder.Length > 0)
+ return builder.Append(separator);
+
+ return builder;
+ }
+
+ public static StringBuilder AppendIf(this StringBuilder builder, bool condition, string value)
+ {
+ if (condition)
+ builder.Append(value);
+
+ return builder;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Text/StringUtils.cs b/src/General/Text/StringUtils.cs
new file mode 100644
index 0000000..bab4728
--- /dev/null
+++ b/src/General/Text/StringUtils.cs
@@ -0,0 +1,20 @@
+using System.Linq;
+
+namespace hydrogen.General.Text
+{
+ public static class StringUtils
+ {
+ public static string JoinNonEmpty(string separator, params string[] strings)
+ {
+ return string.Join(separator, strings.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray());
+ }
+
+ public static string PrependIfNotEmpty(string prefix, string content)
+ {
+ if (string.IsNullOrWhiteSpace(content))
+ return string.Empty;
+
+ return prefix + content;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Utils/CsvUtils.cs b/src/General/Utils/CsvUtils.cs
new file mode 100644
index 0000000..cd69b30
--- /dev/null
+++ b/src/General/Utils/CsvUtils.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace hydrogen.General.Utils
+{
+ public static class CsvUtils
+ {
+ public static string ToCsvString(IEnumerable enumerable, Func toString = null)
+ {
+ if (enumerable == null)
+ return string.Empty;
+
+ toString = toString ?? (t => t.ToString());
+ return string.Join(",", enumerable.Select(toString));
+ }
+
+ public static IEnumerable ParseInt32Enumerable(string csv)
+ {
+ if (string.IsNullOrWhiteSpace(csv))
+ return Enumerable.Empty();
+
+ return csv.Split(',').Where(v => !string.IsNullOrWhiteSpace(v)).Select(int.Parse);
+ }
+
+ public static IEnumerable ParseInt64Enumerable(string csv)
+ {
+ if (string.IsNullOrWhiteSpace(csv))
+ return Enumerable.Empty();
+
+ return csv.Split(',').Where(v => !string.IsNullOrWhiteSpace(v)).Select(long.Parse);
+ }
+
+ public static IEnumerable ParseEnumArray(string csv)
+ {
+ if (string.IsNullOrWhiteSpace(csv))
+ return Enumerable.Empty();
+
+ return csv.Split(',').Where(p => !string.IsNullOrWhiteSpace(p)).Select(p => (T) Enum.ToObject(typeof (T), int.Parse(p)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Utils/DisposableExtensions.cs b/src/General/Utils/DisposableExtensions.cs
new file mode 100644
index 0000000..1d91ec2
--- /dev/null
+++ b/src/General/Utils/DisposableExtensions.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace hydrogen.General.Utils
+{
+ public static class DisposableExtensions
+ {
+ public static void Use(this TDisposable disposable, Action a) where TDisposable : IDisposable
+ {
+ using (disposable)
+ {
+ a(disposable);
+ }
+ }
+
+ public static TResult Use(this TDisposable disposable, Func f) where TDisposable : IDisposable
+ {
+ using (disposable)
+ {
+ return f(disposable);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Utils/GuidUtils.cs b/src/General/Utils/GuidUtils.cs
new file mode 100644
index 0000000..f6b6a75
--- /dev/null
+++ b/src/General/Utils/GuidUtils.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace hydrogen.General.Utils
+{
+ public static class GuidUtils
+ {
+ public static string ToUrlFriendly(this Guid guid)
+ {
+ string enc = Convert.ToBase64String(guid.ToByteArray());
+ enc = enc.Replace("/", "_");
+ enc = enc.Replace("+", "-");
+ return enc.Substring(0, 22);
+ }
+
+ public static Guid ParseUrlFriendlyGuid(string urlFriendlyGuid)
+ {
+ urlFriendlyGuid = urlFriendlyGuid.Replace("_", "/");
+ urlFriendlyGuid = urlFriendlyGuid.Replace("-", "+");
+ byte[] buffer = Convert.FromBase64String(urlFriendlyGuid + "==");
+ return new Guid(buffer);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Utils/ObjectUtils.cs b/src/General/Utils/ObjectUtils.cs
new file mode 100644
index 0000000..5caab45
--- /dev/null
+++ b/src/General/Utils/ObjectUtils.cs
@@ -0,0 +1,12 @@
+namespace hydrogen.General.Utils
+{
+ public static class ObjectUtils
+ {
+ public static void Swap(ref T t1, ref T t2)
+ {
+ var temp = t1;
+ t1 = t2;
+ t2 = temp;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Utils/RandomUtil.cs b/src/General/Utils/RandomUtil.cs
new file mode 100644
index 0000000..fee9b62
--- /dev/null
+++ b/src/General/Utils/RandomUtil.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Threading;
+
+namespace hydrogen.General.Utils
+{
+ ///
+ /// Provides thread-based random number generators, and guarantees unique seeds for their initialization.
+ /// Code copied from Jon Skeet's website, from the following URL:
+ /// http://csharpindepth.com/Articles/Chapter12/Random.aspx
+ ///
+ public static class RandomProvider
+ {
+ private static int _seed = Environment.TickCount;
+
+ private static readonly ThreadLocal RandomWrapper = new ThreadLocal(() =>
+ new Random(Interlocked.Increment(ref _seed))
+ );
+
+ public static Random GetThreadRandom()
+ {
+ return RandomWrapper.Value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/General/Utils/UtilResources.Designer.cs b/src/General/Utils/UtilResources.Designer.cs
new file mode 100644
index 0000000..eae0236
--- /dev/null
+++ b/src/General/Utils/UtilResources.Designer.cs
@@ -0,0 +1,126 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace hydrogen.General.Utils {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class UtilResources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal UtilResources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("hydrogen.General.Utils.UtilResources", typeof(UtilResources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to محیط تولید.
+ ///
+ internal static string ApplicationEnvironmentType_Development {
+ get {
+ return ResourceManager.GetString("ApplicationEnvironmentType_Development", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to محیط بهره برداری.
+ ///
+ internal static string ApplicationEnvironmentType_Production {
+ get {
+ return ResourceManager.GetString("ApplicationEnvironmentType_Production", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to محیط آماده سازی.
+ ///
+ internal static string ApplicationEnvironmentType_Staging {
+ get {
+ return ResourceManager.GetString("ApplicationEnvironmentType_Staging", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to محیط تست.
+ ///
+ internal static string ApplicationEnvironmentType_Test {
+ get {
+ return ResourceManager.GetString("ApplicationEnvironmentType_Test", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to خیر.
+ ///
+ internal static string Boolean_False {
+ get {
+ return ResourceManager.GetString("Boolean_False", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to بله.
+ ///
+ internal static string Boolean_True {
+ get {
+ return ResourceManager.GetString("Boolean_True", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to ----.
+ ///
+ internal static string SelectItem_NotSelected {
+ get {
+ return ResourceManager.GetString("SelectItem_NotSelected", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/General/Utils/UtilResources.resx b/src/General/Utils/UtilResources.resx
new file mode 100644
index 0000000..7519649
--- /dev/null
+++ b/src/General/Utils/UtilResources.resx
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ محیط تولید
+
+
+ محیط بهره برداری
+
+
+ محیط آماده سازی
+
+
+ محیط تست
+
+
+ خیر
+
+
+ بله
+
+
+ ----
+
+
\ No newline at end of file
diff --git a/src/hydrogen.sln b/src/hydrogen.sln
index 455cf15..f8319d2 100644
--- a/src/hydrogen.sln
+++ b/src/hydrogen.sln
@@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2035
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "General", "General\General.csproj", "{2436344A-7627-4F64-81FA-E9B5AB6FB844}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "General", "General\General.csproj", "{2436344A-7627-4F64-81FA-E9B5AB6FB844}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoMapper", "AutoMapper\AutoMapper.csproj", "{4280757D-3D3D-4DBD-9F9C-7F89F1027452}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +17,10 @@ Global
{2436344A-7627-4F64-81FA-E9B5AB6FB844}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2436344A-7627-4F64-81FA-E9B5AB6FB844}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2436344A-7627-4F64-81FA-E9B5AB6FB844}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4280757D-3D3D-4DBD-9F9C-7F89F1027452}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4280757D-3D3D-4DBD-9F9C-7F89F1027452}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4280757D-3D3D-4DBD-9F9C-7F89F1027452}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4280757D-3D3D-4DBD-9F9C-7F89F1027452}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE