/// <inheritdoc />
        public IUriComponents GetUriComponents(string uriString, Context context)
        {
            var uriMatch = Regex.Match(uriString, @"(?<entityset>/[^/\?]*)?(?<options>\?[^/]*)?");

            if (!uriMatch.Success)
            {
                throw new InvalidODataSyntax(InvalidUriSyntax, "Check URI syntax");
            }
            var entitySet = uriMatch.Groups["entityset"].Value.TrimStart('/');
            var options   = uriMatch.Groups["options"].Value.TrimStart('?');
            var uri       = new ODataUriComponents(this);

            switch (entitySet)
            {
            case "":
                uri.ResourceSpecifier = Resource <ServiceDocument> .ResourceSpecifier;
                break;

            case "$metadata":
                uri.ResourceSpecifier = Resource <MetadataDocument> .ResourceSpecifier;
                break;

            default:
                uri.ResourceSpecifier = entitySet;
                break;
            }
            if (options.Length != 0)
            {
                PopulateFromOptions(uri, options);
            }
            return(uri);
        }
        private static void PopulateFromOptions(ODataUriComponents args, string options)
        {
            foreach (var(optionKey, optionValue) in options.Split('&').Select(option => option.TSplit('=')))
            {
                if (string.IsNullOrWhiteSpace(optionKey))
                {
                    throw new InvalidODataSyntax(InvalidConditionSyntax, "An OData query option key was null or whitespace");
                }
                if (string.IsNullOrWhiteSpace(optionValue))
                {
                    throw new InvalidODataSyntax(InvalidConditionSyntax, $"The OData query option value for '{optionKey}' was invalid");
                }
                var decodedValue = HttpUtility.UrlDecode(optionValue);
                switch (optionKey)
                {
                case var system when optionKey[0] == '$':
                    if (!Enum.TryParse(system.Substring(1), out QueryOptions option) || option == none)
                    {
                        throw new FeatureNotImplemented($"Unknown or not implemented query option '{system}'");
                    }
                    switch (option)
                    {
                    case filter:
                        if (Regex.Match(decodedValue, @"(/| has | not | cast\(.*\)| mul | div | mod | add | sub | isof | or )") is Match m &&
                            m.Success)
                        {
                            throw new FeatureNotImplemented($"Not implemented operator '{m.Value}' in $filter");
                        }
                        decodedValue
                        .Replace("(", "")
                        .Replace(")", "")
                        .Split(" and ")
                        .Select(c =>
                        {
                            var parts = c.Split(' ');
                            if (parts.Length != 3)
                            {
                                throw new InvalidODataSyntax(InvalidConditionSyntax, "Invalid syntax in $filter query option");
                            }
                            return(new UriCondition(parts[0], GetOperator(parts[1]), parts[2], TypeCode.String));
                        })
                        .ForEach(cond => args.Conditions.Add(cond));
                        break;

                    case orderby:
                        if (decodedValue.Contains(","))
                        {
                            throw new FeatureNotImplemented("Multiple expressions not implemented for $orderby");
                        }
                        var(term, order) = decodedValue.TSplit(' ');
                        switch (order)
                        {
                        case null:
                        case "":
                        case "asc":
                            args.MetaConditions.Add(new UriCondition(Order_asc, term));
                            break;

                        case "desc":
                            args.MetaConditions.Add(new UriCondition(Order_desc, term));
                            break;

                        default:
                            throw new InvalidODataSyntax(InvalidConditionSyntax,
                                                         "The OData query option value for $orderby was invalid");
                        }
                        break;

                    case select:
                        args.MetaConditions.Add(new UriCondition(Select, decodedValue));
                        break;

                    case skip:
                        args.MetaConditions.Add(new UriCondition(Offset, decodedValue));
                        break;

                    case top:
                        args.MetaConditions.Add(new UriCondition(Limit, decodedValue));
                        break;

                    case search:
                        args.MetaConditions.Add(new UriCondition(Search, decodedValue));
                        break;

                    default: throw new ArgumentOutOfRangeException();
                    }
                    break;

                default:
                    args.MetaConditions.Add(new UriCondition(optionKey, Operators.EQUALS, optionValue, TypeCode.String));
                    break;
                }
            }
        }