Creates a new Uri
object by parsing a URI string.
If start
and end
are provided, only the substring from start
to end
is parsed as a URI.
If the string is not valid as a URI or URI reference, a FormatException is thrown.
Source
static Uri parse(String uri, [int start = 0, int end]) {
// This parsing will not validate percent-encoding, IPv6, etc.
// When done splitting into parts, it will call, e.g., [_makeFragment]
// to do the final parsing.
//
// Important parts of the RFC 3986 used here:
// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
//
// hier-part = "//" authority path-abempty
// / path-absolute
// / path-rootless
// / path-empty
//
// URI-reference = URI / relative-ref
//
// absolute-URI = scheme ":" hier-part [ "?" query ]
//
// relative-ref = relative-part [ "?" query ] [ "#" fragment ]
//
// relative-part = "//" authority path-abempty
// / path-absolute
// / path-noscheme
// / path-empty
//
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
//
// authority = [ userinfo "@" ] host [ ":" port ]
// userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
// host = IP-literal / IPv4address / reg-name
// port = *DIGIT
// reg-name = *( unreserved / pct-encoded / sub-delims )
//
// path = path-abempty ; begins with "/" or is empty
// / path-absolute ; begins with "/" but not "//"
// / path-noscheme ; begins with a non-colon segment
// / path-rootless ; begins with a segment
// / path-empty ; zero characters
//
// path-abempty = *( "/" segment )
// path-absolute = "/" [ segment-nz *( "/" segment ) ]
// path-noscheme = segment-nz-nc *( "/" segment )
// path-rootless = segment-nz *( "/" segment )
// path-empty = 0<pchar>
//
// segment = *pchar
// segment-nz = 1*pchar
// segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
// ; non-zero-length segment without any colon ":"
//
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
//
// query = *( pchar / "/" / "?" )
//
// fragment = *( pchar / "/" / "?" )
const int EOI = -1;
String scheme = "";
String userinfo = "";
String host = null;
int port = null;
String path = null;
String query = null;
String fragment = null;
if (end == null) end = uri.length;
int index = start;
int pathStart = start;
// End of input-marker.
int char = EOI;
void parseAuth() {
if (index == end) {
char = EOI;
return;
}
int authStart = index;
int lastColon = -1;
int lastAt = -1;
char = uri.codeUnitAt(index);
while (index < end) {
char = uri.codeUnitAt(index);
if (char == _SLASH || char == _QUESTION || char == _NUMBER_SIGN) {
break;
}
if (char == _AT_SIGN) {
lastAt = index;
lastColon = -1;
} else if (char == _COLON) {
lastColon = index;
} else if (char == _LEFT_BRACKET) {
lastColon = -1;
int endBracket = uri.indexOf(']', index + 1);
if (endBracket == -1) {
index = end;
char = EOI;
break;
} else {
index = endBracket;
}
}
index++;
char = EOI;
}
int hostStart = authStart;
int hostEnd = index;
if (lastAt >= 0) {
userinfo = _makeUserInfo(uri, authStart, lastAt);
hostStart = lastAt + 1;
}
if (lastColon >= 0) {
int portNumber;
if (lastColon + 1 < index) {
portNumber = 0;
for (int i = lastColon + 1; i < index; i++) {
int digit = uri.codeUnitAt(i);
if (_ZERO > digit || _NINE < digit) {
_fail(uri, i, "Invalid port number");
}
portNumber = portNumber * 10 + (digit - _ZERO);
}
}
port = _makePort(portNumber, scheme);
hostEnd = lastColon;
}
host = _makeHost(uri, hostStart, hostEnd, true);
if (index < end) {
char = uri.codeUnitAt(index);
}
}
// When reaching path parsing, the current character is known to not
// be part of the path.
const int NOT_IN_PATH = 0;
// When reaching path parsing, the current character is part
// of the a non-empty path.
const int IN_PATH = 1;
// When reaching authority parsing, authority is possible.
// This is only true at start or right after scheme.
const int ALLOW_AUTH = 2;
// Current state.
// Initialized to the default value that is used when exiting the
// scheme loop by reaching the end of input.
// All other breaks set their own state.
int state = NOT_IN_PATH;
int i = index; // Temporary alias for index to avoid bug 19550 in dart2js.
while (i < end) {
char = uri.codeUnitAt(i);
if (char == _QUESTION || char == _NUMBER_SIGN) {
state = NOT_IN_PATH;
break;
}
if (char == _SLASH) {
state = (i == start) ? ALLOW_AUTH : IN_PATH;
break;
}
if (char == _COLON) {
if (i == start) _fail(uri, start, "Invalid empty scheme");
scheme = _makeScheme(uri, start, i);
i++;
if (scheme == "data") {
// This generates a URI that is (potentially) not path normalized.
// Applying part normalization to a non-hierarchial URI isn't
// meaningful.
return UriData._parse(uri, i, null).uri;
}
pathStart = i;
if (i == end) {
char = EOI;
state = NOT_IN_PATH;
} else {
char = uri.codeUnitAt(i);
if (char == _QUESTION || char == _NUMBER_SIGN) {
state = NOT_IN_PATH;
} else if (char == _SLASH) {
state = ALLOW_AUTH;
} else {
state = IN_PATH;
}
}
break;
}
i++;
char = EOI;
}
index = i; // Remove alias when bug is fixed.
if (state == ALLOW_AUTH) {
assert(char == _SLASH);
// Have seen one slash either at start or right after scheme.
// If two slashes, it's an authority, otherwise it's just the path.
index++;
if (index == end) {
char = EOI;
state = NOT_IN_PATH;
} else {
char = uri.codeUnitAt(index);
if (char == _SLASH) {
index++;
parseAuth();
pathStart = index;
}
if (char == _QUESTION || char == _NUMBER_SIGN || char == EOI) {
state = NOT_IN_PATH;
} else {
state = IN_PATH;
}
}
}
assert(state == IN_PATH || state == NOT_IN_PATH);
if (state == IN_PATH) {
// Characters from pathStart to index (inclusive) are known
// to be part of the path.
while (++index < end) {
char = uri.codeUnitAt(index);
if (char == _QUESTION || char == _NUMBER_SIGN) {
break;
}
char = EOI;
}
state = NOT_IN_PATH;
}
assert(state == NOT_IN_PATH);
bool hasAuthority = (host != null);
path = _makePath(uri, pathStart, index, null, scheme, hasAuthority);
if (char == _QUESTION) {
int numberSignIndex = -1;
for (int i = index + 1; i < end; i++) {
if (uri.codeUnitAt(i) == _NUMBER_SIGN) {
numberSignIndex = i;
break;
}
}
if (numberSignIndex < 0) {
query = _makeQuery(uri, index + 1, end, null);
} else {
query = _makeQuery(uri, index + 1, numberSignIndex, null);
fragment = _makeFragment(uri, numberSignIndex + 1, end);
}
} else if (char == _NUMBER_SIGN) {
fragment = _makeFragment(uri, index + 1, end);
}
return new Uri._internal(scheme,
userinfo,
host,
port,
path,
query,
fragment);
}