private void Merge (Uri baseUri, string relativeUri)
{
#if NET_2_0
if (baseUri == null)
throw new ArgumentNullException ("baseUri");
if (!baseUri.IsAbsoluteUri)
throw new ArgumentOutOfRangeException ("baseUri");
if (relativeUri == null)
relativeUri = String.Empty;
#else
if (baseUri == null)
throw new NullReferenceException ("baseUri");
#endif
// See RFC 2396 Par 5.2 and Appendix C
// Check Windows UNC (for // it is scheme/host separator)
if (relativeUri.Length >= 2 && relativeUri [0] == '\\' && relativeUri [1] == '\\') {
source = relativeUri;
#if NET_2_0
ParseUri (UriKind.Absolute);
#else
Parse ();
#endif
return;
}
int pos = relativeUri.IndexOf (':');
if (pos != -1) {
int pos2 = relativeUri.IndexOfAny (new char [] {'/', '\\', '?'});
// pos2 < 0 ... e.g. mailto
// pos2 > pos ... to block ':' in query part
if (pos2 > pos || pos2 < 0) {
// in some cases, it is equivanent to new Uri (relativeUri, dontEscape):
// 1) when the URI scheme in the
// relative path is different from that
// of the baseUri, or
// 2) the URI scheme is non-standard
// ones (non-standard URIs are always
// treated as absolute here), or
// 3) the relative URI path is absolute.
if (String.CompareOrdinal (baseUri.Scheme, 0, relativeUri, 0, pos) != 0 ||
!IsPredefinedScheme (baseUri.Scheme) ||
relativeUri.Length > pos + 1 &&
relativeUri [pos + 1] == '/') {
source = relativeUri;
#if NET_2_0
ParseUri (UriKind.Absolute);
#else
Parse ();
#endif
return;
}
else
relativeUri = relativeUri.Substring (pos + 1);
}
}
this.scheme = baseUri.scheme;
this.host = baseUri.host;
this.port = baseUri.port;
this.userinfo = baseUri.userinfo;
this.isUnc = baseUri.isUnc;
this.isUnixFilePath = baseUri.isUnixFilePath;
this.isOpaquePart = baseUri.isOpaquePart;
if (relativeUri == String.Empty) {
this.path = baseUri.path;
this.query = baseUri.query;
this.fragment = baseUri.fragment;
return;
}
// 8 fragment
// Note that in relative constructor, file URI cannot handle '#' as a filename character, but just regarded as a fragment identifier.
pos = relativeUri.IndexOf ('#');
if (pos != -1) {
if (userEscaped)
fragment = relativeUri.Substring (pos);
else
fragment = "#" + EscapeString (relativeUri.Substring (pos+1));
relativeUri = relativeUri.Substring (0, pos);
}
// 6 query
pos = relativeUri.IndexOf ('?');
if (pos != -1) {
query = relativeUri.Substring (pos);
if (!userEscaped)
query = EscapeString (query);
relativeUri = relativeUri.Substring (0, pos);
}
if (relativeUri.Length > 0 && relativeUri [0] == '/') {
if (relativeUri.Length > 1 && relativeUri [1] == '/') {
source = scheme + ':' + relativeUri;
#if NET_2_0
ParseUri (UriKind.Absolute);
#else
Parse ();
#endif
return;
} else {
path = relativeUri;
if (!userEscaped)
path = EscapeString (path);
return;
}
}
// par 5.2 step 6 a)
path = baseUri.path;
#if NET_4_0
if (relativeUri.Length > 0) {
#else
if (relativeUri.Length > 0 || query.Length > 0) {
#endif
pos = path.LastIndexOf ('/');
if (pos >= 0)
path = path.Substring (0, pos + 1);
}
if(relativeUri.Length == 0)
return;
// 6 b)
path += relativeUri;
// 6 c)
int startIndex = 0;
while (true) {
pos = path.IndexOf ("./", startIndex);
if (pos == -1)
break;
if (pos == 0)
path = path.Remove (0, 2);
else if (path [pos - 1] != '.')
path = path.Remove (pos, 2);
else
startIndex = pos + 1;
}
// 6 d)
if (path.Length > 1 &&
path [path.Length - 1] == '.' &&
path [path.Length - 2] == '/')
path = path.Remove (path.Length - 1, 1);
// 6 e)
startIndex = 0;
while (true) {
pos = path.IndexOf ("/../", startIndex);
if (pos == -1)
break;
if (pos == 0) {
startIndex = 3;
continue;
}
int pos2 = path.LastIndexOf ('/', pos - 1);
if (pos2 == -1) {
startIndex = pos + 1;
} else {
if (path.Substring (pos2 + 1, pos - pos2 - 1) != "..")
path = path.Remove (pos2 + 1, pos - pos2 + 3);
else
startIndex = pos + 1;
}
}
// 6 f)
if (path.Length > 3 && path.EndsWith ("/..")) {
pos = path.LastIndexOf ('/', path.Length - 4);
if (pos != -1)
if (path.Substring (pos + 1, path.Length - pos - 4) != "..")
path = path.Remove (pos + 1, path.Length - pos - 1);
}
if (!userEscaped)
path = EscapeString (path);
}
// Properties
public string AbsolutePath {
get {
#if NET_2_0
EnsureAbsoluteUri ();
switch (Scheme) {
case "mailto":
case "file":
// faster (mailto) and special (file) cases
return path;
default:
if (path.Length == 0) {
string start = Scheme + SchemeDelimiter;
if (path.StartsWith (start))
return "/";
else
return String.Empty;
}
return path;
}
#else
return path;
#endif
}
}