private RoutePatternParameterPart ParseParameter(string routeParameter) { // See: #475 - these tests don't pass the 'whole' text. var templatePart = RouteParameterParser.ParseRouteParameter(routeParameter); return(templatePart); }
private static bool ParseParameter(Context context, List <RoutePatternPart> parts) { Debug.Assert(context.Current == OpenBrace); context.Mark(); context.MoveNext(); while (true) { if (context.Current == OpenBrace) { // This is an open brace inside of a parameter, it has to be escaped if (context.MoveNext()) { if (context.Current != OpenBrace) { // If we see something like "{p1:regex(^\d{3", we will come here. context.Error = Resources.TemplateRoute_UnescapedBrace; return(false); } } else { // This is a dangling open-brace, which is not allowed // Example: "{p1:regex(^\d{" context.Error = Resources.TemplateRoute_MismatchedParameter; return(false); } } else if (context.Current == CloseBrace) { // When we encounter Closed brace here, it either means end of the parameter or it is a closed // brace in the parameter, in that case it needs to be escaped. // Example: {p1:regex(([}}])\w+}. First pair is escaped one and last marks end of the parameter if (!context.MoveNext()) { // This is the end of the string -and we have a valid parameter break; } if (context.Current == CloseBrace) { // This is an 'escaped' brace in a parameter name } else { // This is the end of the parameter break; } } if (!context.MoveNext()) { // This is a dangling open-brace, which is not allowed context.Error = Resources.TemplateRoute_MismatchedParameter; return(false); } } var text = context.Capture(); if (text == "{}") { context.Error = Resources.FormatTemplateRoute_InvalidParameterName(string.Empty); return(false); } var inside = text.Substring(1, text.Length - 2); var decoded = inside.Replace("}}", "}").Replace("{{", "{"); // At this point, we need to parse the raw name for inline constraint, // default values and optional parameters. var templatePart = RouteParameterParser.ParseRouteParameter(decoded); // See #475 - this is here because InlineRouteParameterParser can't return errors if (decoded.StartsWith("*", StringComparison.Ordinal) && decoded.EndsWith("?", StringComparison.Ordinal)) { context.Error = Resources.TemplateRoute_CatchAllCannotBeOptional; return(false); } if (templatePart.IsOptional && templatePart.Default != null) { // Cannot be optional and have a default value. // The only way to declare an optional parameter is to have a ? at the end, // hence we cannot have both default value and optional parameter within the template. // A workaround is to add it as a separate entry in the defaults argument. context.Error = Resources.TemplateRoute_OptionalCannotHaveDefaultValue; return(false); } var parameterName = templatePart.Name; if (IsValidParameterName(context, parameterName)) { parts.Add(templatePart); return(true); } else { return(false); } }
private static bool ParseParameter(Context context, List <RoutePatternPart> parts) { Debug.Assert(context.Current == OpenBrace); context.Mark(); context.MoveNext(); while (true) { if (context.Current == OpenBrace) { // This is an open brace inside of a parameter, it has to be escaped if (context.MoveNext()) { if (context.Current != OpenBrace) { // If we see something like "{p1:regex(^\d{3", we will come here. context.Error = "In a route parameter, '{' and '}' must be escaped with '{{' and '}}'."; return(false); } } else { // This is a dangling open-brace, which is not allowed // Example: "{p1:regex(^\d{" context.Error = "There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character."; return(false); } } else if (context.Current == CloseBrace) { // When we encounter Closed brace here, it either means end of the parameter or it is a closed // brace in the parameter, in that case it needs to be escaped. // Example: {p1:regex(([}}])\w+}. First pair is escaped one and last marks end of the parameter if (!context.MoveNext()) { // This is the end of the string -and we have a valid parameter break; } if (context.Current == CloseBrace) { // This is an 'escaped' brace in a parameter name } else { // This is the end of the parameter break; } } if (!context.MoveNext()) { // This is a dangling open-brace, which is not allowed context.Error = "There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character."; return(false); } } var text = context.Capture(); if (text == "{}") { context.Error = $"The route parameter name '{string.Empty}' is invalid. Route parameter names must be non-empty and cannot contain these characters: '{{', '}}', '/'. The '?' character marks a parameter as optional, and can occur only at the end of the parameter. The '*' character marks a parameter as catch-all, and can occur only at the start of the parameter."; return(false); } var inside = text.Substring(1, text.Length - 2); var decoded = inside.Replace("}}", "}").Replace("{{", "{"); // At this point, we need to parse the raw name for inline constraint, // default values and optional parameters. var templatePart = RouteParameterParser.ParseRouteParameter(decoded); // See #475 - this is here because InlineRouteParameterParser can't return errors if (decoded.StartsWith("*", StringComparison.Ordinal) && decoded.EndsWith("?", StringComparison.Ordinal)) { context.Error = "A catch-all parameter cannot be marked optional."; return(false); } if (templatePart.IsOptional && templatePart.Default != null) { // Cannot be optional and have a default value. // The only way to declare an optional parameter is to have a ? at the end, // hence we cannot have both default value and optional parameter within the template. // A workaround is to add it as a separate entry in the defaults argument. context.Error = "An optional parameter cannot have default value."; return(false); } var parameterName = templatePart.Name; if (IsValidParameterName(context, parameterName)) { parts.Add(templatePart); return(true); } else { return(false); } }