Uri parse(String uri, [int start = 0, int end])

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++;
      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);
}