Skip to content

Commit

Permalink
Tag, TagList, TagListParser
Browse files Browse the repository at this point in the history
A lot of things in MM are structured like :tag[value]trailer and this formalizes that structure
  • Loading branch information
blowfishpro committed Jul 4, 2018
1 parent 106581c commit ee3e340
Show file tree
Hide file tree
Showing 8 changed files with 672 additions and 0 deletions.
3 changes: 3 additions & 0 deletions ModuleManager/ModuleManager.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@
<Compile Include="Properties\Resources.Designer.cs" />
<Compile Include="Progress\IPatchProgress.cs" />
<Compile Include="Progress\PatchProgress.cs" />
<Compile Include="Tags\Tag.cs" />
<Compile Include="Tags\TagList.cs" />
<Compile Include="Tags\TagListParser.cs" />
<Compile Include="Threading\ITaskStatus.cs" />
<Compile Include="Threading\TaskStatus.cs" />
<Compile Include="Threading\TaskStatusWrapper.cs" />
Expand Down
34 changes: 34 additions & 0 deletions ModuleManager/Tags/Tag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;

namespace ModuleManager.Tags
{
public struct Tag
{
public readonly string key;
public readonly string value;
public readonly string trailer;

public Tag(string key, string value, string trailer)
{
this.key = key ?? throw new ArgumentNullException(nameof(key));
if (key == string.Empty) throw new ArgumentException("can't be empty", nameof(key));

if (value == null && trailer != null)
throw new ArgumentException("trailer must be null if value is null");

if (trailer == string.Empty) throw new ArgumentException("can't be empty (null allowed)", nameof(trailer));

this.value = value;
this.trailer = trailer;
}

public override string ToString()
{
string s = "< '" + key + "' ";
if (value != null) s += "[ '" + value + "' ] ";
if (trailer != null) s += "'" + trailer + "' ";
s += ">";
return s;
}
}
}
30 changes: 30 additions & 0 deletions ModuleManager/Tags/TagList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ModuleManager.Collections;

namespace ModuleManager.Tags
{
public interface ITagList : IEnumerable<Tag>
{
Tag PrimaryTag { get; }
}

public class TagList : ITagList
{
private readonly Tag[] tags;

public TagList(Tag primaryTag, IEnumerable<Tag> tags)
{
PrimaryTag = primaryTag;
this.tags = tags?.ToArray() ?? throw new ArgumentNullException(nameof(tags));
}

public Tag PrimaryTag { get; private set; }

public ArrayEnumerator<Tag> GetEnumerator() => new ArrayEnumerator<Tag>(tags);
IEnumerator<Tag> IEnumerable<Tag>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
160 changes: 160 additions & 0 deletions ModuleManager/Tags/TagListParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;

namespace ModuleManager.Tags
{
public interface ITagListParser
{
ITagList Parse(string ToParse);
}

public class TagListParser : ITagListParser
{
public ITagList Parse(string toParse)
{
if (toParse == null) throw new ArgumentNullException(nameof(toParse));
if (toParse.Length == 0) throw new FormatException("can't create tag list from empty string");
if (toParse[0] == '[') throw new FormatException("can't create tag list beginning with [");
if (toParse[0] == ':') throw new FormatException("can't create tag list beginning with :");
if (toParse[toParse.Length - 1] == ':') throw new FormatException("tag list can't end with :");

List<Tag> tags = new List<Tag>();
Tag primaryTag = ParsePrimaryTag(toParse, ref tags);
return new TagList(primaryTag, tags);
}

private static Tag ParsePrimaryTag(string toParse, ref List<Tag> tags)
{
for (int i = 1; i < toParse.Length; i++)
{
char c = toParse[i];

if (c == '[')
{
int j = ClosingBracketIndex(toParse, i + 1);
return ParsePrimaryTrailer(toParse, j + 1, ref tags, toParse.Substring(0, i), toParse.Substring(i + 1, j - i - 1));
}
else if (c == ':')
{
ParseTag(toParse, i + 1, ref tags);
return new Tag(toParse.Substring(0, i), null, null);
}
else if (c == ']')
{
throw new FormatException("encountered closing bracket in primary key");
}
}

return new Tag(toParse, null, null);
}

private static Tag ParsePrimaryTrailer(string toParse, int start, ref List<Tag> tags, string primaryKey, string primaryValue)
{
for (int i = start; i < toParse.Length; i++)
{
char c = toParse[i];

if (c == ':')
{
string trailer = i == start ? null : toParse.Substring(start, i - start);
ParseTag(toParse, i + 1, ref tags);
return new Tag(primaryKey, primaryValue, trailer);
}
else if (c == '[')
{
throw new FormatException("encountered opening bracket in primary trailer");
}
else if (c == ']')
{
throw new FormatException("encountered closing bracket in primary trailer");
}
}

string primaryTrailer = toParse.Length - start == 0 ? null : toParse.Substring(start);
return new Tag(primaryKey, primaryValue, primaryTrailer);
}

private static void ParseTag(string toParse, int start, ref List<Tag> tags)
{
for (int i = start; i < toParse.Length; i++)
{
char c = toParse[i];

if (c == '[')
{
if (i == start)
throw new FormatException("tag can't start with [");

int j = ClosingBracketIndex(toParse, i + 1);
ParseTrailer(toParse, j + 1, ref tags, toParse.Substring(start, i - start), toParse.Substring(i + 1, j - i - 1));
return;
}
else if (c == ':')
{
if (i == start)
throw new FormatException("tag can't start with :");

tags.Add(new Tag(toParse.Substring(start, i - start), null, null));
ParseTag(toParse, i + 1, ref tags);
return;
}
else if (c == ']')
{
throw new FormatException("encountered closing bracket in key");
}
}

tags.Add(new Tag(toParse.Substring(start), null, null));
}

private static void ParseTrailer(string toParse, int start, ref List<Tag> tags, string key, string value)
{
for (int i = start; i < toParse.Length; i++)
{
char c = toParse[i];

if (c == ':')
{
string trailer = i == start ? null : toParse.Substring(start, i - start);
tags.Add(new Tag(key, value, trailer));
ParseTag(toParse, i + 1, ref tags);
return;
}
else if (c == '[')
{
throw new FormatException("encountered opening bracket in trailer");
}
else if (c == ']')
{
throw new FormatException("encountered closing bracket in trailer");
}
}

string finalTrailer = toParse.Length - start == 0 ? null : toParse.Substring(start);
tags.Add(new Tag(key, value, finalTrailer));
}

private static int ClosingBracketIndex(string toParse, int start)
{
int bracketLevel = 0;

for (int i = start; i < toParse.Length; i++)
{
char c = toParse[i];

if (c == '[')
{
bracketLevel++;
}
else if (c == ']')
{
bracketLevel--;
}

if (bracketLevel == -1) return i;
}

throw new FormatException("reached end of the tag list without encountering a close bracket");
}
}
}
3 changes: 3 additions & 0 deletions ModuleManagerTests/ModuleManagerTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
<Compile Include="PatchExtractorTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Progress\PatchProgressTest.cs" />
<Compile Include="Tags\TagListParserTest.cs" />
<Compile Include="Tags\TagListTest.cs" />
<Compile Include="Tags\TagTest.cs" />
<Compile Include="Threading\TaskStatusTest.cs" />
<Compile Include="Threading\BackgroundTaskTest.cs" />
<Compile Include="Utils\CounterTest.cs" />
Expand Down
Loading

0 comments on commit ee3e340

Please sign in to comment.