diff --git a/CHANGELOG.md b/CHANGELOG.md
index edae9f3..0f6b732 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
- Added GetAttributeAsEnum xml utils method
- Adding short parsing methods to XML utils
+ - Added methods to IcdUriBuilder for appending path
## [8.0.0] - 2018-11-20
### Added
diff --git a/ICD.Common.Utils/IcdUriBuilder.cs b/ICD.Common.Utils/IcdUriBuilder.cs
index a7dda90..4a385f0 100644
--- a/ICD.Common.Utils/IcdUriBuilder.cs
+++ b/ICD.Common.Utils/IcdUriBuilder.cs
@@ -1,5 +1,9 @@
using System;
+#if !SIMPLSHARP
+using System.Linq;
+#endif
using System.Text;
+using System.Text.RegularExpressions;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils
@@ -159,6 +163,125 @@ namespace ICD.Common.Utils
}
return builder.ToString();
+ }
+
+ ///
+ /// Appends the given path to the current path, ensuring only one separator between parts.
+ ///
+ ///
+ ///
+ public void AppendPath(params string[] parts)
+ {
+ parts = parts.Prepend(Path).ToArray(parts.Length + 1);
+ Path = Combine(parts);
}
+
+ #region Flurl
+
+ // The following region is taken from Flurl https://github.com/tmenier/Flurl
+
+ /*
+ MIT License
+
+ Copyright (c) 2018 Todd Menier
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+ ///
+ /// Basically a Path.Combine for URLs. Ensures exactly one '/' seperates each segment,
+ /// and exactly on '&' seperates each query parameter.
+ /// URL-encodes illegal characters but not reserved characters.
+ ///
+ /// URL parts to combine.
+ public static string Combine(params string[] parts)
+ {
+ if (parts == null)
+ throw new ArgumentNullException("parts");
+
+ string result = "";
+ bool inQuery = false, inFragment = false;
+
+ foreach (var part in parts)
+ {
+ if (string.IsNullOrEmpty(part))
+ continue;
+
+ if (result.EndsWith("?") || part.StartsWith("?"))
+ result = CombineEnsureSingleSeperator(result, part, '?');
+ else if (result.EndsWith("#") || part.StartsWith("#"))
+ result = CombineEnsureSingleSeperator(result, part, '#');
+ else if (inFragment)
+ result += part;
+ else if (inQuery)
+ result = CombineEnsureSingleSeperator(result, part, '&');
+ else
+ result = CombineEnsureSingleSeperator(result, part, '/');
+
+ if (part.Contains("#"))
+ {
+ inQuery = false;
+ inFragment = true;
+ }
+ else if (!inFragment && part.Contains("?"))
+ {
+ inQuery = true;
+ }
+ }
+ return EncodeIllegalCharacters(result, false);
+ }
+
+ private static string CombineEnsureSingleSeperator(string a, string b, char seperator)
+ {
+ if (string.IsNullOrEmpty(a)) return b;
+ if (string.IsNullOrEmpty(b)) return a;
+ return a.TrimEnd(seperator) + seperator + b.TrimStart(seperator);
+ }
+
+ ///
+ /// URL-encodes characters in a string that are neither reserved nor unreserved. Avoids encoding reserved characters such as '/' and '?'. Avoids encoding '%' if it begins a %-hex-hex sequence (i.e. avoids double-encoding).
+ ///
+ /// The string to encode.
+ /// If true, spaces will be encoded as + signs. Otherwise, they'll be encoded as %20.
+ /// The encoded URL.
+ private static string EncodeIllegalCharacters(string s, bool encodeSpaceAsPlus) {
+ if (string.IsNullOrEmpty(s))
+ return s;
+
+ if (encodeSpaceAsPlus)
+ s = s.Replace(" ", "+");
+
+ // Uri.EscapeUriString mostly does what we want - encodes illegal characters only - but it has a quirk
+ // in that % isn't illegal if it's the start of a %-encoded sequence https://stackoverflow.com/a/47636037/62600
+
+ // no % characters, so avoid the regex overhead
+ if (!s.Contains("%"))
+ return Uri.EscapeUriString(s);
+
+ // pick out all %-hex-hex matches and avoid double-encoding
+ return Regex.Replace(s, "(.*?)((%[0-9A-Fa-f]{2})|$)", c => {
+ var a = c.Groups[1].Value; // group 1 is a sequence with no %-encoding - encode illegal characters
+ var b = c.Groups[2].Value; // group 2 is a valid 3-character %-encoded sequence - leave it alone!
+ return Uri.EscapeUriString(a) + b;
+ });
+ }
+
+ #endregion
}
}