mirror of
https://github.com/tribufu/ServerManagers
synced 2026-05-18 09:35:48 +00:00
source code checkin
This commit is contained in:
parent
5f8fb2c825
commit
7e57b72e35
675 changed files with 168433 additions and 0 deletions
16
src/NeXtVdf/NeXt.Vdf.csproj
Normal file
16
src/NeXtVdf/NeXt.Vdf.csproj
Normal 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>
|
||||
35
src/NeXtVdf/Properties/AssemblyInfo.cs
Normal file
35
src/NeXtVdf/Properties/AssemblyInfo.cs
Normal 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")]
|
||||
38
src/NeXtVdf/StringRepeatUtils.cs
Normal file
38
src/NeXtVdf/StringRepeatUtils.cs
Normal 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
26
src/NeXtVdf/VdfDecimal.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
464
src/NeXtVdf/VdfDeserializer.cs
Normal file
464
src/NeXtVdf/VdfDeserializer.cs
Normal 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
20
src/NeXtVdf/VdfLong.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
155
src/NeXtVdf/VdfSerializer.cs
Normal file
155
src/NeXtVdf/VdfSerializer.cs
Normal 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
20
src/NeXtVdf/VdfString.cs
Normal 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
282
src/NeXtVdf/VdfTable.cs
Normal 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
37
src/NeXtVdf/VdfValue.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
20
src/NeXtVdf/VdfValueType.cs
Normal file
20
src/NeXtVdf/VdfValueType.cs
Normal 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,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue