source code checkin

This commit is contained in:
Brett Hewitson 2021-01-07 16:23:23 +10:00
parent 5f8fb2c825
commit 7e57b72e35
675 changed files with 168433 additions and 0 deletions

View file

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Globals">
<SccProjectName>%24/Development/ServerManagers/Main/NeXtVdf</SccProjectName>
<SccProvider>{4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}</SccProvider>
<SccAuxPath>https://dev.azure.com/bretthewitson</SccAuxPath>
<SccLocalPath>.</SccLocalPath>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net462</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die mit einer Assembly verknüpft sind.
[assembly: AssemblyTitle("NeXt.Vdf")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Mirco Gericke")]
[assembly: AssemblyProduct("NeXt.Vdf")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("4a5c4314-a7bc-48a6-938e-58e224bd0879")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,38 @@
using System.Text;
namespace NeXt.Vdf
{
static class StringRepeatUtils
{
/// <summary>
/// Repeats a string i times
/// </summary>
/// <param name="self"></param>
/// <param name="i"></param>
/// <returns></returns>
public static string Repeat(this string self, int i)
{
if(i < 1)
{
return string.Empty;
}
else if(i < 2)
{
return self;
}
else if(i < 3)
{
return self + self;
}
else
{
StringBuilder sb = new StringBuilder();
for(int x = 0; x < i ; x++)
{
sb.Append(self);
}
return sb.ToString();
}
}
}
}

26
src/NeXtVdf/VdfDecimal.cs Normal file
View file

@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NeXt.Vdf
{
/// <summary>
/// A VdfValue that represents a double
/// </summary>
public sealed class VdfDecimal : VdfValue
{
public VdfDecimal(string name) : base(name)
{
Type = VdfValueType.Decimal;
}
public VdfDecimal(string name, decimal value) : this(name)
{
Content = value;
}
public decimal Content { get; set; }
}
}

View file

@ -0,0 +1,464 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.IO;
namespace NeXt.Vdf
{
/// <summary>
/// Base class for al Deserialization exceptions
/// </summary>
public class VdfDeserializationException : Exception
{
public VdfDeserializationException() : base() { }
public VdfDeserializationException(string message) : base() { }
public VdfDeserializationException(string message, Exception innerException) : base(message, innerException) { }
}
/// <summary>
/// A character was unexpected during deserialization
/// </summary>
public class UnexpectedCharacterException : VdfDeserializationException
{
public UnexpectedCharacterException(string message, char c) : base(message)
{
Character = c;
}
public char Character {get; private set;}
}
/// <summary>
/// Deserializes Vdf formatted text into a VdfValue
/// </summary>
public class VdfDeserializer
{
/// <summary>
/// Creates a VdfDeserializer object
/// </summary>
/// <param name="vdfText">the text to deserialize</param>
public VdfDeserializer(string vdfText)
{
VdfText = vdfText;
}
/// <summary>
/// Creates a vdfdeserializer object
/// </summary>
/// <param name="filePath">path to the file to deserialize</param>
/// <returns></returns>
public static VdfDeserializer FromFile(string filePath)
{
using (var reader = new StreamReader(filePath))
{
return new VdfDeserializer(reader.ReadToEnd());
}
}
private string VdfText;
private enum TokenType
{
String,
TableStart,
TableEnd,
Comment,
None,
}
private struct Token
{
public TokenType type;
public string content;
}
private enum CharacterType
{
Whitespace,
Newline,
SequenceDelimiter,
CommentDelimiter,
TableOpen,
TableClose,
EscapeChar,
Char,
}
private CharacterType GetCharType(char c)
{
switch(c)
{
case '\n': return CharacterType.Newline;
case '\r':
case '\t':
case ' ':
{
return CharacterType.Whitespace;
}
case '{': return CharacterType.TableOpen;
case '}': return CharacterType.TableClose;
case '\\': return CharacterType.EscapeChar;
case '/': return CharacterType.CommentDelimiter;
case '"': return CharacterType.SequenceDelimiter;
default: return CharacterType.Char;
}
}
/// <summary>
/// gets the character a single escape char represents
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
private char GetUnescapedChar(char c)
{
switch(c)
{
case 'n': return '\n';
case 't': return '\t';
default: return c;
}
}
/// <summary>
/// returns the next full text (or as much as possible if incomplete)
/// </summary>
/// <param name="s">text to search in</param>
/// <param name="startindex">first index to look at</param>
/// <param name="endIndex">the index the returned string ended at</param>
/// <param name="fullStop">true if the string was over, false if it was incomplete</param>
/// <returns></returns>
private string GetTextToDelimiter(string s, int startindex, out int endIndex, out bool fullStop)
{
fullStop = true;
bool openEscape = false;
bool EscapedSequence = GetCharType(s[startindex]) == CharacterType.SequenceDelimiter;
StringBuilder sb = new StringBuilder();
for(int i = startindex; i < s.Length; i++)
{
switch(GetCharType(s[i]))
{
case CharacterType.SequenceDelimiter:
{
if(!openEscape && EscapedSequence && i > startindex)
{
endIndex = i + 1;
return sb.ToString();
}
else if(!EscapedSequence)
{
throw new UnexpectedCharacterException("Non-Escape sequences cannot contain sequence delimiters", s[i]);
}
else if(openEscape)
{
sb.Append(GetUnescapedChar(s[i]));
openEscape = false;
}
break;
}
case CharacterType.Whitespace:
{
if(EscapedSequence)
{
sb.Append(s[i]);
}
else
{
endIndex = i;
return sb.ToString();
}
break;
}
case CharacterType.EscapeChar:
{
if(openEscape)
{
sb.Append(GetUnescapedChar(s[i]));
}
openEscape = !openEscape;
break;
}
default:
{
if(openEscape)
{
sb.Append(GetUnescapedChar(s[i]));
openEscape = false;
}
else
{
sb.Append(s[i]);
}
break;
}
}
}
endIndex = s.Length;
if(EscapedSequence)
{
if((GetCharType(s[s.Length-1]) != CharacterType.SequenceDelimiter))
{
fullStop = false;
}
else
{
int c = 0;
for (int i = s.Length - 2; i >= 0 && GetCharType(s[i]) == CharacterType.EscapeChar; i--)
{
c++;
}
fullStop = (c % 2) == 0;
}
}
return sb.ToString();
}
private Token startedToken;
private bool unclosedLine = false;
private void HandleUnclosedLine(Action<Token> callback, string line)
{
int endindex;
bool isEnd;
string text = GetTextToDelimiter("\""+line, 0, out endindex, out isEnd);
if (!isEnd)
{
startedToken.content += text;
unclosedLine = true;
}
else
{
unclosedLine = false;
callback(startedToken);
if (endindex < line.Length)
{
HandleLine(callback, line.Substring(endindex).Trim());
}
}
}
private void HandleLine(Action<Token> callback, string line)
{
if(string.IsNullOrEmpty(line))
{
return;
}
CharacterType ct = GetCharType(line[0]);
switch(ct)
{
case CharacterType.TableOpen:
{
callback(new Token(){type=TokenType.TableStart, content=line[0].ToString() });
break;
}
case CharacterType.TableClose:
{
callback(new Token(){type=TokenType.TableEnd, content=line[0].ToString() });
break;
}
case CharacterType.CommentDelimiter:
{
if(line.Length < 2 || GetCharType(line[1]) != CharacterType.CommentDelimiter)
{
throw new UnexpectedCharacterException("Single comment delimiter is not allowed", line[0]);
}
callback(new Token(){type=TokenType.Comment, content=line });
break;
}
default:
{
int endindex;
bool isEnd;
string text = GetTextToDelimiter(line, 0, out endindex, out isEnd);
if(!isEnd)
{
startedToken = new Token() { type = TokenType.String, content = text };
unclosedLine = true;
}
else
{
callback(new Token() { type = TokenType.String, content = text });
if (endindex < line.Length)
{
HandleLine(callback, line.Substring(endindex).Trim());
}
}
break;
}
}
}
/// <summary>
/// Tokenizes the VdfFormatted string into a list of tokens
/// </summary>
/// <param name="s">the string to tokenize</param>
/// <returns>the token list</returns>
private List<Token> Tokenize(string s)
{
var result = new List<Token>();
var lines = s.Split('\n').Select((v) => v.Trim());
foreach(var line in lines)
{
if(unclosedLine)
{
HandleUnclosedLine(result.Add, line);
}
else
{
HandleLine(result.Add, line);
}
}
return result;
}
/// <summary>
/// Deserializes the Vdf string into a VdfValue
/// </summary>
/// <returns></returns>
public VdfValue Deserialize()
{
if(VdfText== null)
{
throw new ArgumentNullException("s");
}
if(VdfText.Length < 1)
{
throw new ArgumentException("s cannot be empty ", "s");
}
var tokens = Tokenize(VdfText);
if(tokens.Count < 1)
{
throw new ArgumentException("no tokens found in string", "s");
}
VdfValue root = null;
VdfTable current = null;
var comments = new List<string>();
string name = null;
foreach(var token in tokens)
{
if(token.type == TokenType.Comment)
{
comments.Add(token.content.Substring(2));
continue;
}
if(root == null)
{
if (token.type == TokenType.String)
{
if(name != null)
{
return new VdfString(name, token.content);
}
name = token.content;
}
else if (token.type == TokenType.TableStart)
{
root = new VdfTable(name);
if(comments.Count > 0)
{
foreach(var comment in comments)
{
root.Comments.Add(comment);
}
comments.Clear();
}
current = root as VdfTable;
name = null;
}
else
{
throw new VdfDeserializationException("Invalid format: First token was not a string");
}
continue;
}
if(name != null)
{
VdfValue v;
if(token.type == TokenType.String)
{
long i;
decimal d;
if(long.TryParse(token.content, NumberStyles.Integer, CultureInfo.InvariantCulture, out i))
{
v = new VdfLong(name, i);
}
else if(decimal.TryParse(token.content, NumberStyles.Number, CultureInfo.InvariantCulture, out d))
{
v = new VdfDecimal(name, d);
}
else
{
v = new VdfString(name, token.content);
}
if (comments.Count > 0)
{
foreach (var comment in comments)
{
v.Comments.Add(comment);
}
comments.Clear();
}
name = null;
current.Add(v);
}
else if (token.type == TokenType.TableStart)
{
v = new VdfTable(name);
if (comments.Count > 0)
{
foreach (var comment in comments)
{
v.Comments.Add(comment);
}
comments.Clear();
}
current.Add(v);
name = null;
current = v as VdfTable;
}
}
else
{
if(token.type == TokenType.String)
{
name = token.content;
}
else if(token.type == TokenType.TableEnd)
{
current = current.Parent as VdfTable;
}
else
{
throw new VdfDeserializationException("Invalid Format: a name was needed but not found");
}
}
}
if(current != null)
{
throw new VdfDeserializationException("Invalid format: unclosed table");
}
return root;
}
}
}

20
src/NeXtVdf/VdfLong.cs Normal file
View file

@ -0,0 +1,20 @@
namespace NeXt.Vdf
{
/// <summary>
/// A VdfValue that represents an integer
/// </summary>
public sealed class VdfLong : VdfValue
{
public VdfLong(string name) : base(name)
{
Type = VdfValueType.Long;
}
public VdfLong(string name, long value) : this(name)
{
Content = value;
}
public long Content { get; set; }
}
}

View file

@ -0,0 +1,155 @@
using System;
using System.Text;
using System.IO;
using System.Globalization;
namespace NeXt.Vdf
{
/// <summary>
/// Class that handles serialization of VdfValue objects into Vdf formatted strings
/// </summary>
public class VdfSerializer
{
/// <summary>
/// Creates a VdfSerializer object
/// </summary>
/// <param name="value">the VdfValue to serialize</param>
public VdfSerializer(VdfValue value)
{
root = value;
}
private const string Newline = "\n";
private const string ValueDelimiter = "\"";
private const string CommentDelimiter = "//";
private const string TableOpen = "{";
private const string TableClose = "}";
private const string KVSeperator = "\t\t";
private string IndentString
{
get
{
return "\t".Repeat(indentLevel);
}
}
private VdfValue root;
private int indentLevel = 0;
private string EscapeString(string v)
{
return v.Replace("\\", @"\\").Replace("\t", @"\t").Replace("\n", @"\n").Replace("\r",@"\r").Replace("\"", @"\""");
}
private void WriteString(Action<string> step, string text, bool escape)
{
step(ValueDelimiter);
if(escape)
{
step(EscapeString(text));
}
else
{
step(text);
}
step(ValueDelimiter);
}
private void RunSerialization(Action<string> onStep, VdfValue current)
{
if(current.Comments.Count > 0)
{
foreach(var s in current.Comments)
{
onStep(IndentString);
onStep(CommentDelimiter);
onStep(EscapeString(s));
onStep(Newline);
}
}
onStep(IndentString);
WriteString(onStep, current.Name, true);
switch(current.Type)
{
case VdfValueType.String:
{
onStep(KVSeperator);
WriteString(onStep, (current as VdfString).Content, true);
onStep(Newline);
break;
}
case VdfValueType.Long:
{
onStep(KVSeperator);
WriteString(onStep, (current as VdfLong).Content.ToString(CultureInfo.InvariantCulture), false);
onStep(Newline);
break;
}
case VdfValueType.Decimal:
{
onStep(KVSeperator);
WriteString(onStep, (current as VdfDecimal).Content.ToString(CultureInfo.InvariantCulture), false);
onStep(Newline);
break;
}
case VdfValueType.Table:
{
onStep(Newline);
onStep(IndentString);
onStep(TableOpen);
onStep(Newline);
indentLevel++;
foreach(var v in current as VdfTable)
{
RunSerialization(onStep, v);
}
indentLevel--;
onStep(IndentString);
onStep(TableClose);
onStep(Newline);
break;
}
}
}
/// <summary>
/// Serialize the object to string
/// </summary>
/// <returns>a string representing the VdfValue</returns>
public string Serialize()
{
StringBuilder sb = new StringBuilder();
RunSerialization((s) => sb.Append(s), root);
return sb.ToString();
}
/// <summary>
/// Serialize the object to a file
/// </summary>
/// <param name="filePath">full path to the file to serialize into</param>
public void Serialize(string filePath)
{
Serialize(filePath, Encoding.Unicode);
}
/// <summary>
/// Serialize the object to a file using the given encoding
/// </summary>
/// <param name="filePath"></param>
/// <param name="encoding"></param>
public void Serialize(string filePath, Encoding encoding)
{
using(StreamWriter writer = new StreamWriter(filePath, false, encoding))
{
RunSerialization(writer.Write, root);
writer.Flush();
}
}
}
}

20
src/NeXtVdf/VdfString.cs Normal file
View file

@ -0,0 +1,20 @@
namespace NeXt.Vdf
{
/// <summary>
/// A VdfValue that represents a string
/// </summary>
public sealed class VdfString : VdfValue
{
public VdfString(string name) : base(name)
{
Type = VdfValueType.String;
}
public VdfString(string name, string value) : this(name)
{
Content = value;
}
public string Content { get; set; }
}
}

282
src/NeXtVdf/VdfTable.cs Normal file
View file

@ -0,0 +1,282 @@
using System.Collections.Generic;
using System;
using System.Text;
namespace NeXt.Vdf
{
/// <summary>
/// A VdfValue that represents a table containing other VdfValues
/// </summary>
public sealed class VdfTable : VdfValue, IList<VdfValue>
{
public VdfTable(string name) : base(name) { }
public VdfTable(string name, IEnumerable<VdfValue> values) : this(name)
{
if (values == null)
{
throw new ArgumentNullException("values");
}
foreach(var val in values)
{
Add(val);
}
}
private List<VdfValue> values = new List<VdfValue>();
private Dictionary<string, VdfValue> valuelookup = new Dictionary<string,VdfValue>();
public int IndexOf(VdfValue item)
{
return values.IndexOf(item);
}
public void Insert(int index, VdfValue item)
{
if(item == null)
{
throw new ArgumentNullException("item");
}
if(index < 0 || index >= values.Count)
{
throw new ArgumentOutOfRangeException("index");
}
if(string.IsNullOrEmpty(item.Name))
{
throw new ArgumentException("item name cannot be empty or null");
}
if (ContainsName(item.Name))
{
throw new ArgumentException("a value with name " + item.Name + " already exists in the table");
}
item.Parent = this;
values.Insert(index, item);
valuelookup.Add(item.Name, item);
}
public void InsertAfter(VdfValue item, VdfValue newitem)
{
if(!Contains(item))
{
throw new ArgumentException("item needs to exist in this table", "item");
}
if (string.IsNullOrEmpty(newitem.Name))
{
throw new ArgumentException("newitem name cannot be empty or null");
}
if (ContainsName(newitem.Name))
{
throw new ArgumentException("a value with name " + newitem.Name + " already exists in the table");
}
int i = -1;
for(i = 0; i < values.Count; i++)
{
if(values[i] == item)
{
break;
}
}
if(i >= 0 && i < values.Count)
{
if(i == values.Count -1)
{
Add(newitem);
}
else
{
Insert(i + 1, newitem);
}
}
}
public void RemoveAt(int index)
{
var val = values[index];
values.RemoveAt(index);
valuelookup.Remove(val.Name);
}
public VdfValue this[int index]
{
get
{
return values[index];
}
set
{
if(values[index].Name != value.Name)
{
valuelookup.Remove(values[index].Name);
valuelookup.Add(value.Name, value);
}
else
{
valuelookup[value.Name] = value;
}
values[index] = value;
}
}
public VdfValue this[string name]
{
get
{
return valuelookup[name];
}
}
public void Add(VdfValue item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
if (string.IsNullOrEmpty(item.Name))
{
throw new ArgumentException("item name cannot be empty or null");
}
if (ContainsName(item.Name))
{
throw new ArgumentException("a value with name " + item.Name + " already exists in the table");
}
item.Parent = this;
values.Add(item);
valuelookup.Add(item.Name, item);
}
public void Clear()
{
values.Clear();
valuelookup.Clear();
}
public bool Contains(VdfValue item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
if (string.IsNullOrEmpty(item.Name))
{
throw new ArgumentException("item name cannot be empty or null");
}
return valuelookup.ContainsKey(item.Name) && (valuelookup[item.Name] == item);
}
public bool ContainsName(string name)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("name cannot be empty");
}
return valuelookup.ContainsKey(name);
}
public VdfValue GetByName(string name)
{
if(ContainsName(name))
{
return valuelookup[name];
}
return null;
}
public void CopyTo(VdfValue[] array, int arrayIndex)
{
values.CopyTo(array, arrayIndex);
}
public int Count
{
get { return values.Count; }
}
public bool Remove(VdfValue item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
if (string.IsNullOrEmpty(item.Name))
{
throw new ArgumentException("item name cannot be empty or null");
}
if(Contains(item))
{
valuelookup.Remove(item.Name);
values.Remove(item);
return true;
}
return false;
}
public IEnumerator<VdfValue> GetEnumerator()
{
return values.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return values.GetEnumerator();
}
bool ICollection<VdfValue>.IsReadOnly
{
get { return false; }
}
public void Traverse(Func<VdfValue, bool> call)
{
if (call == null)
{
throw new ArgumentNullException("call");
}
foreach (var value in values)
{
if (!call(value))
{
break;
}
}
}
public void TraverseRecursive(Func<VdfValue, bool> call)
{
if (call == null)
{
throw new ArgumentNullException("call");
}
foreach (var value in values)
{
if (value is VdfTable)
{
((VdfTable)value).TraverseRecursive(call);
}
else
{
if (!call(value))
{
break;
}
}
}
}
}
}

37
src/NeXtVdf/VdfValue.cs Normal file
View file

@ -0,0 +1,37 @@
using System.Collections.Generic;
namespace NeXt.Vdf
{
/// <summary>
/// Abstract VdfValue
/// </summary>
public abstract class VdfValue
{
public VdfValue(string name)
{
Name = name;
}
/// <summary>
/// This values name
/// </summary>
public string Name { get; private set; }
private List<string> comments = new List<string>();
/// <summary>
/// This values type, determines how it can be casted
/// </summary>
public VdfValueType Type { get; protected set; }
/// <summary>
/// Comments that where in front of this VdfValue
/// </summary>
public ICollection<string> Comments { get { return comments; } }
/// <summary>
/// This values Parent, null for root
/// </summary>
public VdfValue Parent { get; internal set; }
}
}

View file

@ -0,0 +1,20 @@
namespace NeXt.Vdf
{
/// <summary>
/// Flag that determines how to handle a VdfValue
/// </summary>
/// <remarks>
/// All VdfValues can be casted according to their Type:
/// VdfValueType.Table => VdfTable
/// VdfValueType.String => VdfString
/// VdfValueType.Integer => VdfInteger
/// VdfValueType.Double => VdfDouble
/// </remarks>
public enum VdfValueType
{
Table,
String,
Long,
Decimal,
}
}