QuickImage – Rev 1
?pathlinks?
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 //
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, //
// rights of fair usage, the disclaimer and warranty conditions. //
///////////////////////////////////////////////////////////////////////////
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace QuickImage.Utilities.Serialization.Comma_Separated_Values
{
public class Csv : IReadOnlyCollection<string>
{
#region Static Fields and Constants
/// <summary>
/// Special CSV characters.
/// </summary>
private static readonly char[] CsvEscapeCharacters = { '"', ' ', ',', '\r', '\n' };
#endregion
#region Public Enums, Properties and Fields
public int Length => _store.Count;
#endregion
#region Private Delegates, Events, Enums, Properties, Indexers and Fields
private readonly List<string> _store;
#endregion
#region Constructors, Destructors and Finalizers
public Csv(string csv) => _store = FromStringCsv(csv)
.ToList();
public Csv(IEnumerable<string> csv) : this(ToStringCsv(csv)) { }
public Csv() => _store = new List<string>();
public Csv(IDictionary<string, string> dictionary) : this(FromDictionary(dictionary)) { }
public Csv(ISet<string> set) : this(set.ToList()) { }
#endregion
#region Interface
public IEnumerator<string> GetEnumerator() => _store.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_store).GetEnumerator();
public int Count => _store.Count;
#endregion
#region Public Overrides
public override string ToString() => ToStringCsv(_store);
#endregion
#region Operators
public static implicit operator string(Csv csv) => ToStringCsv(csv);
public static implicit operator Csv(string @string) => new Csv(@string);
public static implicit operator Csv(Dictionary<string, string> dictionary) =>
new Csv(FromDictionary(dictionary));
public static implicit operator string[](Csv csv) => csv.ToArray();
public static implicit operator Csv(string[] array) => new Csv(array);
#endregion
#region Public Methods
public string[] ToArray() => _store.ToArray();
#endregion
#region Private Methods
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Converts a dictionary of strings to a comma-separated values string.
/// </summary>
/// <returns>a comma-separated list of values</returns>
/// <remarks>compliant with RFC 4180</remarks>
private static string FromDictionary<TK, TV>(IDictionary<TK, TV> input)
{
return string.Join(",",
input.Keys.Select(o => o.ToString())
.Zip(input.Values.Select(o => o.ToString()),
(o, p) =>
string.Join(",",
o.Replace("\"", "\"\"")
.IndexOfAny(CsvEscapeCharacters) ==
-1 ?
o :
$"\"{o}\"",
p.Replace("\"", "\"\"")
.IndexOfAny(CsvEscapeCharacters) ==
-1 ?
p :
$"\"{p}\"")));
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Converts a list of strings to a comma-separated values string.
/// </summary>
/// <returns>a comma-separated list of values</returns>
/// <remarks>compliant with RFC 4180</remarks>
private static string ToStringCsv(IEnumerable<string> csv)
{
var list = csv.ToList();
/*
* This is How the Cookie Crumbles (yes, it follows RFC 4180 as intended
* but not as designed):
*
* The CSV escape symbol is the double-quote and, as per RFC 4180, it
* the double-quote is used to escape cells by surrounding cells that
* contain line-breaks, double quotes and commas.
*
* The purpose for escaping CSV cells conforming with RFC 4180:
* * cells with double-quotes - general protection from clashes
* with CSV cell escape symbols but since there is only one single
* cell, there are no clashes possible since no other cells precede
* or follow.
* * cells with commas - the comma is used as a delimiter between
* cells, such that if a comma is part of a cell's contents, the
* cell must be escaped but since if there is only one single cell,
* then there is no reason to escape the cell if it contains a comma.
*
*/
if (list.Count == 1)
{
return list.Single();
}
return string.Join(",",
list
.Select(o => string.IsNullOrEmpty(o) ? string.Empty : o.Replace("\"", "\"\""))
.Select(o => o.IndexOfAny(CsvEscapeCharacters) == -1 ? o : $"\"{o}\""));
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Converts a comma-separated list of values to a list of strings.
/// </summary>
/// <param name="csv">a comma-separated list of values</param>
/// <returns>a list of strings</returns>
/// <remarks>compliant with RFC 4180</remarks>
private static IEnumerable<string> FromStringCsv(string csv)
{
if (string.IsNullOrEmpty(csv))
{
yield break;
}
var s = new Stack<char>();
var m = new StringBuilder();
for (var i = 0; i < csv.Length; ++i)
{
switch (csv[i])
{
case ',':
if (!s.Any() ||
!s.Peek()
.Equals('"'))
{
yield return m.ToString();
m = new StringBuilder();
continue;
}
m.Append(csv[i]);
continue;
case '"':
if (i + 1 < csv.Length &&
csv[i]
.Equals(csv[i + 1]))
{
m.Append(csv[i]);
++i;
continue;
}
if (!s.Any() ||
!s.Peek()
.Equals(csv[i]))
{
s.Push(csv[i]);
continue;
}
s.Pop();
continue;
}
m.Append(csv[i]);
}
yield return m.ToString();
}
#endregion
}
}
Generated by GNU Enscript 1.6.5.90.