Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor - Consolidating duplicate code. Minor bug fixes #73

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/MagicChunks.Tests/Documents/JsonDocumentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ public void ValidateEmptyPath()
ArgumentException result = Assert.Throws<ArgumentException>(() => document.ReplaceKey(new[] { "a", "", "b" }, ""));

// Arrange
Assert.True(result.Message?.StartsWith("There is empty items in the path."));
Assert.True(result.Message?.StartsWith("There is at least one empty segment in the path."));
}

[Fact]
Expand All @@ -511,7 +511,7 @@ public void ValidateWithespacePath()
ArgumentException result = Assert.Throws<ArgumentException>(() => document.ReplaceKey(new[] { "a", " ", "b" }, ""));

// Arrange
Assert.True(result.Message?.StartsWith("There is empty items in the path."));
Assert.True(result.Message?.StartsWith("There is at least one empty segment in the path."));
}
}
}
4 changes: 2 additions & 2 deletions src/MagicChunks.Tests/Documents/XmlDocumentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ public void ValidateEmptyPath()
ArgumentException result = Assert.Throws<ArgumentException>(() => document.ReplaceKey(new[] { "a", "", "b" }, ""));

// Assert
Assert.True(result.Message?.StartsWith("There is empty items in the path."));
Assert.True(result.Message?.StartsWith("There is at least one empty segment in the path."));
}

[Fact]
Expand All @@ -629,7 +629,7 @@ public void ValidateWhiteSpacePath()
ArgumentException result = Assert.Throws<ArgumentException>(() => document.ReplaceKey(new[] { "a", " ", "b" }, ""));

// Assert
Assert.True(result.Message?.StartsWith("There is empty items in the path."));
Assert.True(result.Message?.StartsWith("There is at least one empty segment in the path."));
}

[Fact]
Expand Down
22 changes: 18 additions & 4 deletions src/MagicChunks.Tests/Documents/YamlDocumentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,26 +111,40 @@ public void Remove()
public void ValidateEmptyPath()
{
// Assert
YamlDocument document = new YamlDocument("");
var document = new YamlDocument(@"a:
x: 1
b: 2
c: 3");
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the check for the lack of a root object from the call to ReplaceKey to the constructor since it could be checked with no other data required.

Because of this, the constructor now throws the Root Element Not Present error.

This test was modified to only validate the empty path exception, and a new test was added below to verify that the no root element exception is thrown by the constructor.


// Act
ArgumentException result = Assert.Throws<ArgumentException>(() => document.ReplaceKey(new[] { "a", "", "b" }, ""));

// Arrange
Assert.True(result.Message?.StartsWith("There is empty items in the path."));
Assert.True(result.Message?.StartsWith("There is at least one empty segment in the path."));
}

[Fact]
public void ValidateWithespacePath()
{
// Assert
YamlDocument document = new YamlDocument("");
var document = new YamlDocument(@"a:
x: 1
b: 2
c: 3");

// Act
ArgumentException result = Assert.Throws<ArgumentException>(() => document.ReplaceKey(new[] { "a", " ", "b" }, ""));

// Arrange
Assert.True(result.Message?.StartsWith("There is empty items in the path."));
Assert.True(result.Message?.StartsWith("There is at least one empty segment in the path."));
}

[Fact]
public void ConstructorThrowsErrorIfDocumentHasNoRoot()
{
ArgumentException result = Assert.Throws<ArgumentException>(() => new YamlDocument(""));

Assert.True(result.Message?.StartsWith("Root element is not present."));
}
}
}
19 changes: 19 additions & 0 deletions src/MagicChunks/Core/Document.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MagicChunks.Core
{
public class Document
{
protected static void ValidatePath(string[] path)
{
if (path == null || !path.Any())
throw new ArgumentException("Path is not speicified.", nameof(path));

if (path.Any(String.IsNullOrWhiteSpace))
throw new ArgumentException("There is at least one empty segment in the path.", nameof(path));
}
}
}
2 changes: 1 addition & 1 deletion src/MagicChunks/Core/Transformer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public string Transform(IDocument source, TransformationCollection transformatio
foreach (var key in transformations.KeysToRemove)
{
if (String.IsNullOrWhiteSpace(key))
throw new ArgumentNullException("Transformation key is empty.", nameof(transformations));
throw new ArgumentNullException(nameof(transformations), "Transformation key is empty.");

source.RemoveKey(SplitKey(key));
}
Expand Down
52 changes: 18 additions & 34 deletions src/MagicChunks/Documents/JsonDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace MagicChunks.Documents
{
public class JsonDocument : IDocument
public class JsonDocument : Document, IDocument
{
private static readonly Regex JsonObjectRegex = new Regex(@"^{.+}$$", RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex NodeIndexEndingRegex = new Regex(@"\[\d+\]$", RegexOptions.CultureInvariant | RegexOptions.Compiled);
Expand All @@ -21,6 +21,9 @@ public JsonDocument(string source)
try
{
Document = (JObject)JsonConvert.DeserializeObject(source);

if (Document.Root == null)
throw new ArgumentException("Root element is not present.", nameof(source));
}
catch (JsonReaderException ex)
{
Expand All @@ -30,17 +33,7 @@ public JsonDocument(string source)

public void AddElementToArray(string[] path, string value)
{
if ((path == null) || (path.Any() == false))
throw new ArgumentException("Path is not speicified.", nameof(path));

if (path.Any(String.IsNullOrWhiteSpace))
throw new ArgumentException("There is empty items in the path.", nameof(path));

if (Document.Root == null)
throw new ArgumentException("Root element is not present.", nameof(path));

var targets = FindPath(path.Take(path.Length - 1), (JObject)Document.Root);
var pathEnding = path.Last();
(var targets, var pathEnding) = Process(path);

foreach (var target in targets)
{
Expand All @@ -50,17 +43,7 @@ public void AddElementToArray(string[] path, string value)

public void ReplaceKey(string[] path, string value)
{
if ((path == null) || (path.Any() == false))
throw new ArgumentException("Path is not speicified.", nameof(path));

if (path.Any(String.IsNullOrWhiteSpace))
throw new ArgumentException("There is empty items in the path.", nameof(path));

if (Document.Root == null)
throw new ArgumentException("Root element is not present.", nameof(path));

var targets = FindPath(path.Take(path.Length - 1), (JObject)Document.Root);
var pathEnding = path.Last();
(var targets, var pathEnding) = Process(path);

foreach (var target in targets)
{
Expand All @@ -70,17 +53,7 @@ public void ReplaceKey(string[] path, string value)

public void RemoveKey(string[] path)
{
if ((path == null) || (path.Any() == false))
throw new ArgumentException("Path is not speicified.", nameof(path));

if (path.Any(String.IsNullOrWhiteSpace))
throw new ArgumentException("There is empty items in the path.", nameof(path));

if (Document.Root == null)
throw new ArgumentException("Root element is not present.", nameof(path));

var targets = FindPath(path.Take(path.Length - 1), (JObject)Document.Root);
var pathEnding = path.Last();
(var targets, var pathEnding) = Process(path);

if (NodeIndexEndingRegex.IsMatch(pathEnding) || NodeValueEndingRegex.IsMatch(pathEnding))
{
Expand All @@ -103,6 +76,16 @@ public void RemoveKey(string[] path)
}
}

private (IEnumerable<JObject>, string) Process(string[] path)
{
ValidatePath(path);

var targets = FindPath(path.Take(path.Length - 1), (JObject)Document.Root);
var pathEnding = path.Last();

return (targets, pathEnding);
}

private static IEnumerable<JObject> FindPath(IEnumerable<string> path, JObject current)
{
var pathElements = new Queue<string>(path);
Expand Down Expand Up @@ -185,5 +168,6 @@ public override string ToString()
public void Dispose()
{
}

}
}
82 changes: 29 additions & 53 deletions src/MagicChunks/Documents/XmlDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace MagicChunks.Documents
{
public class XmlDocument : IDocument
public class XmlDocument : Document, IDocument
{
private static readonly Regex AttributeFilterRegex = new Regex(@"(?<element>.+?)\[\s*\@(?<key>[\w\:]+)\s*\=\s*[\'\""]?(?<value>.+?)[\'\""]?\s*\]$", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline);
private static readonly Regex ProcessingInstructionsPathElementRegex = new Regex(@"^\?.+", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline);
Expand All @@ -32,24 +32,10 @@ public XmlDocument(string source)

public void AddElementToArray(string[] path, string value)
{
if ((path == null) || (path.Any() == false))
throw new ArgumentException("Path is not speicified.", nameof(path));

if (path.Any(String.IsNullOrWhiteSpace))
throw new ArgumentException("There is empty items in the path.", nameof(path));

XElement current = Document.Root;
string documentNamespace = Document.Root?.Name.NamespaceName ?? String.Empty;

if (current == null)
throw new ArgumentException("Root element is not present.", nameof(path));

if (String.Compare(current.Name.LocalName, path.First(), StringComparison.OrdinalIgnoreCase) != 0)
throw new ArgumentException("Root element name does not match path.", nameof(path));

if (!path.Any(p => ProcessingInstructionsPathElementRegex.IsMatch(p)))
(var match, var documentNamespace) = ProcessAndMatch(path);
if (!match)
{
current = FindPath(path.Skip(1).Take(path.Length - 2), current, documentNamespace) as XElement;
var current = FindPath(path.Skip(1).Take(path.Length - 2), Document.Root, documentNamespace) as XElement;
UpdateTargetArrayElement(value, path.Last(), current, documentNamespace);
}
else
Expand All @@ -60,24 +46,11 @@ public void AddElementToArray(string[] path, string value)

public void ReplaceKey(string[] path, string value)
{
if ((path == null) || (path.Any() == false))
throw new ArgumentException("Path is not speicified.", nameof(path));
(var match, var documentNamespace) = ProcessAndMatch(path);

if (path.Any(String.IsNullOrWhiteSpace))
throw new ArgumentException("There is empty items in the path.", nameof(path));

XElement current = Document.Root;
string documentNamespace = Document.Root?.Name.NamespaceName ?? String.Empty;

if (current == null)
throw new ArgumentException("Root element is not present.", nameof(path));

if (String.Compare(current.Name.LocalName, path.First(), StringComparison.OrdinalIgnoreCase) != 0)
throw new ArgumentException("Root element name does not match path.", nameof(path));

if (!path.Any(p => ProcessingInstructionsPathElementRegex.IsMatch(p)))
if (!match)
{
current = FindPath(path.Skip(1).Take(path.Length - 2), current, documentNamespace) as XElement;
var current = FindPath(path.Skip(1).Take(path.Length - 2), Document.Root, documentNamespace) as XElement;
UpdateTargetElement(value, path.Last(), current, documentNamespace);
}
else
Expand All @@ -97,43 +70,46 @@ public void ReplaceKey(string[] path, string value)
throw new ArgumentException("To update processing instruction you should point attribute name.", nameof(path));
}

var processingInstruction = FindPath(path.Skip(1).Take(path.Length - 2), current, documentNamespace) as XProcessingInstruction;
var processingInstruction = FindPath(path.Skip(1).Take(path.Length - 2), Document.Root, documentNamespace) as XProcessingInstruction;
UpdateProcessingInstruction(value, path.Last().TrimStart('@'), processingInstruction, documentNamespace);
}
}

public void RemoveKey(string[] path)
{
if ((path == null) || (path.Any() == false))
throw new ArgumentException("Path is not specified.", nameof(path));

if (path.Any(String.IsNullOrWhiteSpace))
throw new ArgumentException("There is empty items in the path.", nameof(path));
(var match, var documentNamespace) = ProcessAndMatch(path);

XElement current = Document.Root;
string documentNamespace = Document.Root?.Name.NamespaceName ?? String.Empty;

if (current == null)
throw new ArgumentException("Root element is not present.", nameof(path));

if (String.Compare(current.Name.LocalName, path.First(), StringComparison.OrdinalIgnoreCase) != 0)
throw new ArgumentException("Root element name does not match path.", nameof(path));

if (!path.Any(p => ProcessingInstructionsPathElementRegex.IsMatch(p)))
if (!match)
{
current = FindPath(path.Skip(1).Take(path.Length - 2), current, documentNamespace) as XElement;
var current = FindPath(path.Skip(1).Take(path.Length - 2), Document.Root, documentNamespace) as XElement;
RemoveTargetElement(path.Last(), current, documentNamespace);
}
else
{
var processingInstruction = FindPath(path.Skip(1).Take(path.Length - 2), current, documentNamespace) as XProcessingInstruction;
var processingInstruction = FindPath(path.Skip(1).Take(path.Length - 2), Document.Root, documentNamespace) as XProcessingInstruction;

// Remove whole processing instruction (not just single attribute)
if (processingInstruction == null)
processingInstruction = FindPath(path.Skip(1).Take(path.Length - 1), current, documentNamespace) as XProcessingInstruction;
processingInstruction = FindPath(path.Skip(1).Take(path.Length - 1), Document.Root, documentNamespace) as XProcessingInstruction;

RemoveProcessingInstruction(path.Last(), processingInstruction, documentNamespace);
}

}
private (bool, string) ProcessAndMatch(string[] path)
{
ValidatePath(path);

XElement current = Document.Root;
string documentNamespace = Document.Root?.Name.NamespaceName ?? String.Empty;

if (current == null)
throw new ArgumentException("Root element is not present.", nameof(path));

if (String.Compare(current.Name.LocalName, path.First(), StringComparison.OrdinalIgnoreCase) != 0)
throw new ArgumentException("Root element name does not match path.", nameof(path));

return (path.Any(p => ProcessingInstructionsPathElementRegex.IsMatch(p)), documentNamespace);
}

private static XNode FindPath(IEnumerable<string> path, XElement current, string documentNamespace)
Expand Down
Loading