diff --git a/CHANGELOG.md b/CHANGELOG.md
index 179a748..923aae6 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 IcdOrderedDictionary collection
- Added PriorityQueue collection
+ - Added IcdUriBuilder and UriExtensions for reading/writing URI data
## [3.2.0] - 2018-05-09
### Added
diff --git a/ICD.Common.Utils.Tests/Extensions/UriExtensionsTest.cs b/ICD.Common.Utils.Tests/Extensions/UriExtensionsTest.cs
new file mode 100644
index 0000000..04514f1
--- /dev/null
+++ b/ICD.Common.Utils.Tests/Extensions/UriExtensionsTest.cs
@@ -0,0 +1,22 @@
+using System;
+using ICD.Common.Utils.Extensions;
+using NUnit.Framework;
+
+namespace ICD.Common.Utils.Tests.Extensions
+{
+ [TestFixture]
+ public sealed class UriExtensionsTest
+ {
+ [TestCase("http://username:password@test.com/", "username")]
+ public void GetUserName(string uriString, string expected)
+ {
+ Assert.AreEqual(expected, new Uri(uriString).GetUserName());
+ }
+
+ [TestCase("http://username:password@test.com/", "password")]
+ public void GetPassword(string uriString, string expected)
+ {
+ Assert.AreEqual(expected, new Uri(uriString).GetPassword());
+ }
+ }
+}
diff --git a/ICD.Common.Utils.Tests/IcdUriBuilderTest.cs b/ICD.Common.Utils.Tests/IcdUriBuilderTest.cs
new file mode 100644
index 0000000..1dc2d2f
--- /dev/null
+++ b/ICD.Common.Utils.Tests/IcdUriBuilderTest.cs
@@ -0,0 +1,88 @@
+using NUnit.Framework;
+
+namespace ICD.Common.Utils.Tests
+{
+ [TestFixture]
+ public sealed class IcdUriBuilderTest
+ {
+ #region Properties
+
+ [TestCase("test")]
+ public void FragmentTest(string fragment)
+ {
+ Assert.AreEqual(fragment, new IcdUriBuilder {Fragment = fragment}.Fragment);
+ }
+
+ [TestCase("test")]
+ public void HostTest(string host)
+ {
+ Assert.AreEqual(host, new IcdUriBuilder { Host = host }.Host);
+ }
+
+ [TestCase("test")]
+ public void PasswordTest(string fragment)
+ {
+ Assert.AreEqual(fragment, new IcdUriBuilder { Password = fragment }.Password);
+ }
+
+ [TestCase("test")]
+ public void PathTest(string fragment)
+ {
+ Assert.AreEqual(fragment, new IcdUriBuilder { Path = fragment }.Path);
+ }
+
+ [TestCase(80)]
+ public void PortTest(ushort port)
+ {
+ Assert.AreEqual(port, new IcdUriBuilder { Port = port }.Port);
+ }
+
+ [TestCase("test")]
+ public void QueryTest(string query)
+ {
+ Assert.AreEqual(query, new IcdUriBuilder { Query = query }.Fragment);
+ }
+
+ [TestCase("test")]
+ public void SchemeTest(string scheme)
+ {
+ Assert.AreEqual(scheme, new IcdUriBuilder { Scheme = scheme }.Fragment);
+ }
+
+ [TestCase("test")]
+ public void UserNameTest(string userName)
+ {
+ Assert.AreEqual(userName, new IcdUriBuilder { UserName = userName }.Fragment);
+ }
+
+ [Test]
+ public void UriTest()
+ {
+ Assert.Inconclusive();
+ }
+
+ #endregion
+
+ [TestCase("http://localhost/", null, null, null, 0, null, null, null)]
+ [TestCase("http://localhost:80/", null, null, null, 80, null, null, null)]
+ [TestCase("http://username:@localhost/", null, null, null, 0, null, null, "username")]
+ [TestCase("http://:password@localhost/", null, null, "password", 0, null, null, null)]
+ [TestCase("https://localhost/", null, null, null, 0, null, "https", null)]
+ public void ToStringTest(string expected, string fragment, string address, string password, ushort port, string query,
+ string scheme, string userName)
+ {
+ IcdUriBuilder builder = new IcdUriBuilder
+ {
+ Fragment = fragment,
+ Host = address,
+ Password = password,
+ Port = port,
+ Query = query,
+ Scheme = scheme,
+ UserName = userName
+ };
+
+ Assert.AreEqual(expected, builder.ToString());
+ }
+ }
+}
diff --git a/ICD.Common.Utils/Extensions/UriExtensions.cs b/ICD.Common.Utils/Extensions/UriExtensions.cs
new file mode 100644
index 0000000..2db241d
--- /dev/null
+++ b/ICD.Common.Utils/Extensions/UriExtensions.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Linq;
+
+namespace ICD.Common.Utils.Extensions
+{
+ public static class UriExtensions
+ {
+ ///
+ /// Gets the username from the given URI.
+ ///
+ ///
+ ///
+ public static string GetUserName(this Uri extends)
+ {
+ if (extends == null)
+ throw new ArgumentNullException("extends");
+
+ return extends.UserInfo.Split(':').FirstOrDefault(string.Empty);
+ }
+
+ ///
+ /// Gets the password from the given URI.
+ ///
+ ///
+ ///
+ public static string GetPassword(this Uri extends)
+ {
+ if (extends == null)
+ throw new ArgumentNullException("extends");
+
+ return extends.UserInfo.Split(':').Skip(0).FirstOrDefault(string.Empty);
+ }
+ }
+}
diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj
index 87c6a3b..957c471 100644
--- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj
+++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj
@@ -95,7 +95,9 @@
+
+
diff --git a/ICD.Common.Utils/IcdUriBuilder.cs b/ICD.Common.Utils/IcdUriBuilder.cs
new file mode 100644
index 0000000..1e061e4
--- /dev/null
+++ b/ICD.Common.Utils/IcdUriBuilder.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Text;
+
+namespace ICD.Common.Utils
+{
+ ///
+ /// Simple Compact Framework UriBuilder implementation.
+ ///
+ public sealed class IcdUriBuilder
+ {
+ #region Properties
+
+ ///
+ /// Gets or sets the fragment portion of the URI.
+ ///
+ public string Fragment { get; set; }
+
+ ///
+ /// Gets or sets the Domain Name System (DNS) host name or IP address of a server.
+ ///
+ public string Host { get; set; }
+
+ ///
+ /// Gets or sets the password associated with the user that accesses the URI.
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// Gets or sets the path to the resource referenced by the URI.
+ ///
+ public string Path { get; set; }
+
+ ///
+ /// Gets or sets the port number of the URI.
+ ///
+ public ushort Port { get; set; }
+
+ ///
+ /// Gets or sets any query information included in the URI.
+ ///
+ public string Query { get; set; }
+
+ ///
+ /// Gets or sets the scheme name of the URI.
+ ///
+ public string Scheme { get; set; }
+
+ ///
+ /// The user name associated with the user that accesses the URI.
+ ///
+ public string UserName { get; set; }
+
+ ///
+ /// Gets the Uri instance constructed by the specified UriBuilder instance.
+ ///
+ public Uri Uri { get { return new Uri(ToString()); } }
+
+ #endregion
+
+ ///
+ /// Builds the string representation for the URI.
+ ///
+ ///
+ public override string ToString()
+ {
+ // URI = scheme:[//authority]path[?query][#fragment]
+ // authority = [userinfo@]host[:port]
+ // userinfo = username[:password]
+
+ StringBuilder builder = new StringBuilder();
+
+ // Scheme
+ string scheme = string.IsNullOrEmpty(Scheme) ? "http" : Scheme;
+ builder.Append(scheme);
+
+ // Authority
+ builder.Append("//");
+
+ if (!string.IsNullOrEmpty(UserName))
+ {
+ builder.Append(UserName);
+
+ if (!string.IsNullOrEmpty(Password))
+ {
+ builder.Append(':');
+ builder.Append(Password);
+ }
+
+ builder.Append('@');
+ }
+
+ string host = string.IsNullOrEmpty(Host) ? "localhost" : Host;
+ builder.Append(host);
+
+ if (Port != 0)
+ {
+ builder.Append(':');
+ builder.Append(Port);
+ }
+
+ // Path
+ builder.Append('/');
+ builder.Append(Path);
+
+ // Query
+ if (!string.IsNullOrEmpty(Query))
+ {
+ builder.Append('?');
+ builder.Append(Query);
+ }
+
+ // Fragment
+ if (!string.IsNullOrEmpty(Fragment))
+ {
+ builder.Append('#');
+ builder.Append(Fragment);
+ }
+
+ return builder.ToString();
+ }
+ }
+}