-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New String extensions: EscapeJsonData, ToFileFormatString.
New Exception extensions: GetDetailedMessage (with call stack trice and innerExceptions). ProcessAgregated for Processing InnerExceptions when AggregatedException thrown. New NumberExtensions: PercentageString (for float, decemial, double) LockedCollections returned back.
- Loading branch information
1 parent
6b3d7cf
commit 773e998
Showing
15 changed files
with
838 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System; | ||
|
||
namespace Leaf.Core.Collections.Generic | ||
{ | ||
/// <summary> | ||
/// Расширения для потокобезопасных коллекций, реализующие работу со случайностями. | ||
/// </summary> | ||
public static class CollectionRandimizer | ||
{ | ||
[ThreadStatic] private static Random _rand; | ||
private static Random Rand => _rand ?? (_rand = new Random()); | ||
|
||
/// <summary> | ||
/// Получает случайный элемент коллекции | ||
/// </summary> | ||
/// <typeparam name="T"></typeparam> | ||
/// <param name="collection"></param> | ||
/// <returns></returns> | ||
/// | ||
/// HACK: Решение для Genetic Random. | ||
public static T GetNextRandom<T>(this ListStorage<T> collection) | ||
{ | ||
if (collection.Count == 0) | ||
return default(T); | ||
|
||
int index = Rand.Next(collection.Count - 1); | ||
var result = collection[index]; | ||
|
||
if (collection.Iteration == ListIteration.Removable) | ||
collection.RemoveAt(index); | ||
|
||
return result; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
namespace Leaf.Core.Collections.Generic | ||
{ | ||
/// <summary> | ||
/// Реализация потокобезопасного хранилища. | ||
/// </summary> | ||
/// <typeparam name="T">Тип хранимых объектов</typeparam> | ||
public interface IStorage<T> | ||
{ | ||
/// <summary> | ||
/// Число элементов в коллекции. | ||
/// </summary> | ||
int Count { get; } | ||
|
||
/// <summary> | ||
/// Очистить коллекцию. | ||
/// </summary> | ||
void Clear(); | ||
|
||
/// <summary> | ||
/// Добавляет элемент в коллекцию. | ||
/// </summary> | ||
/// <param name="item"></param> | ||
void AppendItem(T item); | ||
|
||
/// <summary> | ||
/// Возвращает следующий элемент коллекции. | ||
/// </summary> | ||
/// <returns>Следующий элемент коллекции.</returns> | ||
T GetNext(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace Leaf.Core.Collections.Generic | ||
{ | ||
public sealed class ListStorage<T> : List<T>, IStorage<T> | ||
{ | ||
public ListIteration Iteration = ListIteration.TillTheEnd; | ||
private int _currentIndex; | ||
|
||
/// <summary> | ||
/// Сбрасывает текущий элемент списка на первый (нулевой). | ||
/// </summary> | ||
public void ResetPointer() | ||
{ | ||
_currentIndex = 0; | ||
} | ||
|
||
void IStorage<T>.AppendItem(T item) | ||
{ | ||
Add(item); | ||
} | ||
|
||
T IStorage<T>.GetNext() | ||
{ | ||
if (Count == 0) | ||
return default(T); | ||
|
||
if (_currentIndex >= Count) | ||
{ | ||
if (Iteration == ListIteration.TillTheEnd) | ||
return default(T); | ||
|
||
_currentIndex = 0; | ||
} | ||
|
||
var result = this[_currentIndex]; | ||
if (Iteration == ListIteration.Removable) | ||
{ | ||
RemoveAt(_currentIndex); | ||
// Don't increment index! | ||
} | ||
else | ||
{ | ||
++_currentIndex; | ||
} | ||
|
||
return result; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
namespace Leaf.Core.Collections.Generic | ||
{ | ||
/// <summary> | ||
/// Абстрактная реализация потокобезопасной коллекции. | ||
/// </summary> | ||
/// <typeparam name="T">Тип хранимых объектов</typeparam> | ||
public abstract class LockedCollection<T> // TODO : IEnumerable<T> | ||
{ | ||
/// <summary> | ||
/// Коллекция реализующая общие методы для работы с элементами. | ||
/// </summary> | ||
protected IStorage<T> Storage; | ||
|
||
/// <summary> | ||
/// Удаляет все элементы из коллекции. | ||
/// </summary> | ||
public virtual void Clear() | ||
{ | ||
lock (Storage) | ||
Storage.Clear(); | ||
} | ||
|
||
/// <summary> | ||
/// Доступное число элементов в коллекции. | ||
/// </summary> | ||
public virtual int Count | ||
{ | ||
get { | ||
lock (Storage) | ||
return Storage.Count; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Возвращает следующий элемент коллекции. Если коллекция пуста будет возвращён null. | ||
/// </summary> | ||
public virtual T GetNext() | ||
{ | ||
if (Storage == null) | ||
return default(T); | ||
|
||
lock (Storage) | ||
return Storage.GetNext(); | ||
} | ||
|
||
/// <summary> | ||
/// Записывает следующий элемент коллекции в переменную. | ||
/// </summary> | ||
/// <param name="item">Переменная куда будет записан полученный элемент из коллекции</param> | ||
/// <returns>Вернет истину если элемент был возращен. Если коллеция уже пуста вернет ложь.</returns> | ||
public virtual bool GetNext(out T item) | ||
{ | ||
item = GetNext(); | ||
return item != null; | ||
} | ||
|
||
/// <summary> | ||
/// Добавляет элемент в коллекцию. | ||
/// </summary> | ||
/// <param name="item">Элемент</param> | ||
public virtual void AppendItem(T item) | ||
{ | ||
if (Storage == null) | ||
return; | ||
|
||
lock (Storage) | ||
Storage.AppendItem(item); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
using System.IO; | ||
using System.Threading.Tasks; | ||
using Leaf.Core.Runtime.Serialization; | ||
using Leaf.Core.Threading; | ||
|
||
namespace Leaf.Core.Collections.Generic | ||
{ | ||
/// <summary> | ||
/// Фабрика потокобезопасных коллекций. | ||
/// </summary> | ||
public static class LockedFactory | ||
{ | ||
#region # Public Synchronous Methods | ||
|
||
#region ## Generic | ||
|
||
/// <summary> | ||
/// Создаёт потокобезопасный список объектов из текстового файла. Десериализация проводится построчно. | ||
/// </summary> | ||
/// <param name="filePath">Путь до файла</param> | ||
/// <param name="ui">Потокобезопасный интерфейс, нужен для ведения лога в случае ошибки десериализации</param> | ||
/// <param name="includeComments">Если true, то строки с коментариями тоже будут включены в выборку для десериалиализации.</param> | ||
/// <param name="trim">Очищать начало и конец строк от отступов и пробелов.</param> | ||
/// <returns>Возвращает новый потокобезопасный список объектов</returns> | ||
public static LockedList<T> ListFromFile<T>(string filePath, ThreadSafeUI ui = null, | ||
bool includeComments = false, bool trim = true) | ||
where T : IStringSerializeable, new() | ||
{ | ||
var result = new LockedList<T>(); | ||
ReadAndDeserialize(result, filePath, ui, includeComments, trim); | ||
return result; | ||
} | ||
|
||
/// <summary> | ||
/// Создаёт потокобезопасную очередь объектов из текстового файла. Десериализация проводится построчно. | ||
/// </summary> | ||
/// <param name="filePath">Путь до файла</param> | ||
/// <param name="ui">Потокобезопасный интерфейс, нужен для ведения лога в случае ошибки десериализации</param> | ||
/// <param name="includeComments">Если true, то строки с коментариями тоже будут включены в выборку для десериалиализации.</param> | ||
/// <param name="trim">Очищать начало и конец строк от отступов и пробелов.</param> | ||
/// <returns>Возвращает новую потокобезопасную очередь объектов</returns> | ||
public static LockedQueue<T> QueueFromFile<T>(string filePath, ThreadSafeUI ui = null, | ||
bool includeComments = false, bool trim = true) | ||
where T : IStringSerializeable, new() | ||
{ | ||
var result = new LockedQueue<T>(); | ||
ReadAndDeserialize(result, filePath, ui, includeComments, trim); | ||
return result; | ||
} | ||
|
||
#endregion | ||
|
||
#region ## String | ||
|
||
/// <inheritdoc cref="ListFromFile{T}"/> | ||
/// <summary> | ||
/// Создаёт потокобезопасный список строк из текстового файла. Десериализация проводится построчно. | ||
/// </summary> | ||
/// <returns>Возвращает новый потокобезопасный список строк</returns> | ||
public static LockedList ListFromFile(string filePath, bool includeComments = false, bool trim = true) | ||
{ | ||
var result = new LockedList(); | ||
ReadAndAppend(result, filePath, includeComments, trim); | ||
return result; | ||
} | ||
|
||
/// <inheritdoc cref="QueueFromFile{T}"/> | ||
/// <summary> | ||
/// Создаёт потокобезопасную очередь строк из текстового файла. Десериализация проводится построчно. | ||
/// </summary> | ||
/// <returns>Возвращает новую потокобезопасную очередь строк</returns> | ||
public static LockedQueue QueueFromFile(string filePath, bool includeComments = false, bool trim = true) | ||
{ | ||
var result = new LockedQueue(); | ||
ReadAndAppend(result, filePath, includeComments, trim); | ||
return result; | ||
} | ||
|
||
#endregion | ||
|
||
#endregion | ||
|
||
#region # Public Asynchronous Methods | ||
|
||
#region ## Generic | ||
|
||
/// <inheritdoc cref="ListFromFile{T}"/> | ||
/// <summary> | ||
/// Асинхронно создаёт потокобезопасный список объектов из текстового файла. Десериализация проводится построчно. | ||
/// </summary> | ||
public static async Task<LockedList<T>> ListFromFileAsync<T>(string filePath, ThreadSafeUI ui = null, | ||
bool includeComments = false, bool trim = true) | ||
where T : IStringSerializeable, new() | ||
{ | ||
return await Task.Run(() => ListFromFile<T>(filePath, ui, includeComments, trim)); | ||
} | ||
|
||
/// <inheritdoc cref="QueueFromFile{T}"/> | ||
/// <summary> | ||
/// Асинхронно создаёт потокобезопасную очередь объектов из текстового файла. Десериализация проводится построчно. | ||
/// </summary> | ||
public static async Task<LockedQueue<T>> QueueFromFileAsync<T>(string filePath, ThreadSafeUI ui = null, | ||
bool includeComments = false, bool trim = true) | ||
where T : IStringSerializeable, new() | ||
{ | ||
return await Task.Run(() => QueueFromFile<T>(filePath, ui, includeComments, trim)); | ||
} | ||
|
||
#endregion | ||
|
||
#region ## String | ||
|
||
/// <inheritdoc cref="ListFromFile"/> | ||
/// <summary> | ||
/// Асинхронно создаёт потокобезопасный список строк из текстового файла. Десериализация проводится построчно. | ||
/// </summary> | ||
public static async Task<LockedList> ListFromFileAsync(string filePath, | ||
bool includeComments = false, bool trim = true) | ||
{ | ||
return await Task.Run(() => ListFromFile(filePath, includeComments, trim)); | ||
} | ||
|
||
/// <inheritdoc cref="QueueFromFile"/> | ||
/// <summary> | ||
/// Асинхронно создаёт потокобезопасную очередь строк из текстового файла. Десериализация проводится построчно. | ||
/// </summary> | ||
public static async Task<LockedQueue> QueueFromFileAsync(string filePath, | ||
bool includeComments = false, bool trim = true) | ||
{ | ||
return await Task.Run(() => QueueFromFile(filePath, includeComments, trim)); | ||
} | ||
|
||
#endregion | ||
|
||
#endregion | ||
|
||
#region Private helpers | ||
private delegate void LineProcessor(ulong lineNumber, string line); | ||
|
||
private static void ReadFileLineByLine(string filePath, bool includeComments, bool trim, LineProcessor lineProcessor) | ||
{ | ||
if (!File.Exists(filePath)) | ||
{ | ||
File.Create(filePath).Close(); | ||
return; | ||
} | ||
|
||
using (var file = new StreamReader(filePath)) | ||
{ | ||
ulong lineNumber = 0; | ||
|
||
while (!file.EndOfStream) | ||
{ | ||
string line = file.ReadLine(); | ||
++lineNumber; | ||
|
||
// Пропускаем пустые строки и комментарии если требуется | ||
if (string.IsNullOrWhiteSpace(line) || | ||
!includeComments && (line.StartsWith("//") || line.StartsWith("#"))) | ||
continue; | ||
|
||
if (trim) | ||
line = line.Trim(); | ||
|
||
lineProcessor(lineNumber, line); | ||
} | ||
} | ||
} | ||
|
||
private static void ReadAndDeserialize<T>(LockedCollection<T> collection, string filePath, ThreadSafeUI ui, | ||
bool includeComments, bool trim) | ||
where T : IStringSerializeable, new() | ||
{ | ||
ReadFileLineByLine(filePath, includeComments, trim, (lineNumber, line) => { | ||
// Десериализуем объект из строки | ||
var item = new T(); | ||
if (item.DeserializeFromString(line)) | ||
{ | ||
collection.AppendItem(item); | ||
} | ||
else | ||
ui?.Log($"Skipped {typeof(T).Name} because invalid format. Line #{lineNumber}: {line}"); | ||
}); | ||
} | ||
|
||
private static void ReadAndAppend(LockedCollection<string> collection, string filePath, | ||
bool includeComments, bool trim) | ||
{ | ||
ReadFileLineByLine(filePath, includeComments, trim, (lineNumber, line) => { | ||
collection.AppendItem(line); | ||
}); | ||
} | ||
#endregion | ||
} | ||
} |
Oops, something went wrong.