diff --git a/src/MagicChunks.Tests/Documents/JsonDocumentTests.cs b/src/MagicChunks.Tests/Documents/JsonDocumentTests.cs index 1c5dceb..334187f 100644 --- a/src/MagicChunks.Tests/Documents/JsonDocumentTests.cs +++ b/src/MagicChunks.Tests/Documents/JsonDocumentTests.cs @@ -498,7 +498,7 @@ public void ValidateEmptyPath() ArgumentException result = Assert.Throws(() => 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] @@ -511,7 +511,7 @@ public void ValidateWithespacePath() ArgumentException result = Assert.Throws(() => 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.")); } } } \ No newline at end of file diff --git a/src/MagicChunks.Tests/Documents/XmlDocumentTests.cs b/src/MagicChunks.Tests/Documents/XmlDocumentTests.cs index 860a3b0..4ff51f3 100644 --- a/src/MagicChunks.Tests/Documents/XmlDocumentTests.cs +++ b/src/MagicChunks.Tests/Documents/XmlDocumentTests.cs @@ -616,7 +616,7 @@ public void ValidateEmptyPath() ArgumentException result = Assert.Throws(() => 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] @@ -629,7 +629,7 @@ public void ValidateWhiteSpacePath() ArgumentException result = Assert.Throws(() => 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] diff --git a/src/MagicChunks.Tests/Documents/YamlDocumentTests.cs b/src/MagicChunks.Tests/Documents/YamlDocumentTests.cs index 79c776c..5fd73fb 100644 --- a/src/MagicChunks.Tests/Documents/YamlDocumentTests.cs +++ b/src/MagicChunks.Tests/Documents/YamlDocumentTests.cs @@ -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"); // Act ArgumentException result = Assert.Throws(() => 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(() => 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(() => new YamlDocument("")); + + Assert.True(result.Message?.StartsWith("Root element is not present.")); } } } \ No newline at end of file diff --git a/src/MagicChunks/Core/Document.cs b/src/MagicChunks/Core/Document.cs new file mode 100644 index 0000000..2baff8c --- /dev/null +++ b/src/MagicChunks/Core/Document.cs @@ -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)); + } + } +} diff --git a/src/MagicChunks/Core/Transformer.cs b/src/MagicChunks/Core/Transformer.cs index b9d6db8..49462e1 100644 --- a/src/MagicChunks/Core/Transformer.cs +++ b/src/MagicChunks/Core/Transformer.cs @@ -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)); } diff --git a/src/MagicChunks/Documents/JsonDocument.cs b/src/MagicChunks/Documents/JsonDocument.cs index ea3815d..2974207 100644 --- a/src/MagicChunks/Documents/JsonDocument.cs +++ b/src/MagicChunks/Documents/JsonDocument.cs @@ -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); @@ -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) { @@ -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) { @@ -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) { @@ -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)) { @@ -103,6 +76,16 @@ public void RemoveKey(string[] path) } } + private (IEnumerable, 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 FindPath(IEnumerable path, JObject current) { var pathElements = new Queue(path); @@ -185,5 +168,6 @@ public override string ToString() public void Dispose() { } + } } \ No newline at end of file diff --git a/src/MagicChunks/Documents/XmlDocument.cs b/src/MagicChunks/Documents/XmlDocument.cs index 705d057..5ac158b 100644 --- a/src/MagicChunks/Documents/XmlDocument.cs +++ b/src/MagicChunks/Documents/XmlDocument.cs @@ -9,7 +9,7 @@ namespace MagicChunks.Documents { - public class XmlDocument : IDocument + public class XmlDocument : Document, IDocument { private static readonly Regex AttributeFilterRegex = new Regex(@"(?.+?)\[\s*\@(?[\w\:]+)\s*\=\s*[\'\""]?(?.+?)[\'\""]?\s*\]$", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline); private static readonly Regex ProcessingInstructionsPathElementRegex = new Regex(@"^\?.+", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline); @@ -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 @@ -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 @@ -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 path, XElement current, string documentNamespace) diff --git a/src/MagicChunks/Documents/YamlDocument.cs b/src/MagicChunks/Documents/YamlDocument.cs index e46172b..6d24513 100644 --- a/src/MagicChunks/Documents/YamlDocument.cs +++ b/src/MagicChunks/Documents/YamlDocument.cs @@ -9,7 +9,7 @@ namespace MagicChunks.Documents { - public class YamlDocument : IDocument + public class YamlDocument : Document, IDocument { protected readonly Dictionary Document; @@ -25,6 +25,10 @@ public YamlDocument(string source) try { Document = (Dictionary)deserializer.Deserialize(reader); + + if (Document == null) + throw new ArgumentException("Root element is not present.", nameof(source)); + } catch (YamlException ex) { @@ -35,55 +39,24 @@ public YamlDocument(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)); - - Dictionary current = Document; - - if (current == null) - throw new ArgumentException("Root element is not present.", nameof(path)); - - current = FindPath(path.Take(path.Length - 1), current); - - UpdateTargetArrayElement(current, path.Last(), value); + UpdateTargetArrayElement(Process(path), path.Last(), 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)); - - Dictionary current = Document; - - if (current == null) - throw new ArgumentException("Root element is not present.", nameof(path)); - - current = FindPath(path.Take(path.Length - 1), current); - - UpdateTargetElement(current, path.Last(), value); + UpdateTargetElement(Process(path), path.Last(), 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)); - - Dictionary current = Document; - - if (current == null) - throw new ArgumentException("Root element is not present.", nameof(path)); + Process(path).Remove(path.Last()); + } - current = FindPath(path.Take(path.Length - 1), current); - current.Remove(path.Last()); + private Dictionary Process(string[] path) + { + ValidatePath(path); + var current = FindPath(path.Take(path.Length - 1), Document); + return current; } private static Dictionary FindPath(IEnumerable path, Dictionary current)