Skip to content

Commit

Permalink
New String extensions: EscapeJsonData, ToFileFormatString.
Browse files Browse the repository at this point in the history
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
grandsilence committed Jul 19, 2018
1 parent 6b3d7cf commit 773e998
Show file tree
Hide file tree
Showing 15 changed files with 838 additions and 3 deletions.
35 changes: 35 additions & 0 deletions Leaf.Core/Collections/Generic/CollectionRandomizer.cs
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;
}
}
}
31 changes: 31 additions & 0 deletions Leaf.Core/Collections/Generic/IStorage.cs
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();
}
}
50 changes: 50 additions & 0 deletions Leaf.Core/Collections/Generic/ListStorage.cs
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;
}
}
}
70 changes: 70 additions & 0 deletions Leaf.Core/Collections/Generic/LockedCollection.cs
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);
}
}
}
195 changes: 195 additions & 0 deletions Leaf.Core/Collections/Generic/LockedFactory.cs
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
}
}
Loading

0 comments on commit 773e998

Please sign in to comment.