/// <summary> /// Parses the string representation of a {@code URL} into a /// {@code URL} object. /// <para> /// If there is any inherited context, then it has already been /// copied into the {@code URL} argument. /// </para> /// <para> /// The {@code parseURL} method of {@code URLStreamHandler} /// parses the string representation as if it were an /// {@code http} specification. Most URL protocol families have a /// similar parsing. A stream protocol handler for a protocol that has /// a different syntax must override this routine. /// /// </para> /// </summary> /// <param name="u"> the {@code URL} to receive the result of parsing /// the spec. </param> /// <param name="spec"> the {@code String} representing the URL that /// must be parsed. </param> /// <param name="start"> the character index at which to begin parsing. This is /// just past the '{@code :}' (if there is one) that /// specifies the determination of the protocol name. </param> /// <param name="limit"> the character position to stop parsing at. This is the /// end of the string or the position of the /// "{@code #}" character, if present. All information /// after the sharp sign indicates an anchor. </param> protected internal virtual void ParseURL(URL u, String spec, int start, int limit) { // These fields may receive context content if this was relative URL String protocol = u.Protocol; String authority = u.Authority; String userInfo = u.UserInfo; String host = u.Host; int port = u.Port; String path = u.Path; String query = u.Query; // This field has already been parsed String @ref = u.Ref; bool isRelPath = false; bool queryOnly = false; // FIX: should not assume query if opaque // Strip off the query part if (start < limit) { int queryStart = spec.IndexOf('?'); queryOnly = queryStart == start; if ((queryStart != -1) && (queryStart < limit)) { query = StringHelperClass.SubstringSpecial(spec, queryStart + 1, limit); if (limit > queryStart) { limit = queryStart; } spec = spec.Substring(0, queryStart); } } int i = 0; // Parse the authority part if any bool isUNCName = (start <= limit - 4) && (spec.CharAt(start) == '/') && (spec.CharAt(start + 1) == '/') && (spec.CharAt(start + 2) == '/') && (spec.CharAt(start + 3) == '/'); if (!isUNCName && (start <= limit - 2) && (spec.CharAt(start) == '/') && (spec.CharAt(start + 1) == '/')) { start += 2; i = spec.IndexOf('/', start); if (i < 0) { i = spec.IndexOf('?', start); if (i < 0) { i = limit; } } host = authority = spec.Substring(start, i - start); int ind = authority.IndexOf('@'); if (ind != -1) { userInfo = authority.Substring(0, ind); host = authority.Substring(ind + 1); } else { userInfo = null; } if (host != null) { // If the host is surrounded by [ and ] then its an IPv6 // literal address as specified in RFC2732 if (host.Length() > 0 && (host.CharAt(0) == '[')) { if ((ind = host.IndexOf(']')) > 2) { String nhost = host; host = nhost.Substring(0, ind + 1); if (!IPAddressUtil.isIPv6LiteralAddress(host.Substring(1, ind - 1))) { throw new IllegalArgumentException("Invalid host: " + host); } port = -1; if (nhost.Length() > ind + 1) { if (nhost.CharAt(ind + 1) == ':') { ++ind; // port can be null according to RFC2396 if (nhost.Length() > (ind + 1)) { port = Convert.ToInt32(nhost.Substring(ind + 1)); } } else { throw new IllegalArgumentException("Invalid authority field: " + authority); } } } else { throw new IllegalArgumentException("Invalid authority field: " + authority); } } else { ind = host.IndexOf(':'); port = -1; if (ind >= 0) { // port can be null according to RFC2396 if (host.Length() > (ind + 1)) { port = Convert.ToInt32(host.Substring(ind + 1)); } host = host.Substring(0, ind); } } } else { host = ""; } if (port < -1) { throw new IllegalArgumentException("Invalid port number :" + port); } start = i; // If the authority is defined then the path is defined by the // spec only; See RFC 2396 Section 5.2.4. if (authority != null && authority.Length() > 0) { path = ""; } } if (host == null) { host = ""; } // Parse the file path if any if (start < limit) { if (spec.CharAt(start) == '/') { path = spec.Substring(start, limit - start); } else if (path != null && path.Length() > 0) { isRelPath = true; int ind = path.LastIndexOf('/'); String seperator = ""; if (ind == -1 && authority != null) { seperator = "/"; } path = path.Substring(0, ind + 1) + seperator + spec.Substring(start, limit - start); } else { String seperator = (authority != null) ? "/" : ""; path = seperator + spec.Substring(start, limit - start); } } else if (queryOnly && path != null) { int ind = path.LastIndexOf('/'); if (ind < 0) { ind = 0; } path = path.Substring(0, ind) + "/"; } if (path == null) { path = ""; } if (isRelPath) { // Remove embedded /./ while ((i = path.IndexOf("/./")) >= 0) { path = path.Substring(0, i) + path.Substring(i + 2); } // Remove embedded /../ if possible i = 0; while ((i = path.IndexOf("/../", i)) >= 0) { /* * A "/../" will cancel the previous segment and itself, * unless that segment is a "/../" itself * i.e. "/a/b/../c" becomes "/a/c" * but "/../../a" should stay unchanged */ if (i > 0 && (limit = path.LastIndexOf('/', i - 1)) >= 0 && (path.IndexOf("/../", limit) != 0)) { path = path.Substring(0, limit) + path.Substring(i + 3); i = 0; } else { i = i + 3; } } // Remove trailing .. if possible while (path.EndsWith("/..")) { i = path.IndexOf("/.."); if ((limit = path.LastIndexOf('/', i - 1)) >= 0) { path = path.Substring(0, limit + 1); } else { break; } } // Remove starting . if (path.StartsWith("./") && path.Length() > 2) { path = path.Substring(2); } // Remove trailing . if (path.EndsWith("/.")) { path = path.Substring(0, path.Length() - 1); } } SetURL(u, protocol, host, port, authority, userInfo, path, query, @ref); }