Ejemplo n.º 1
0
        // Segments have the following order:
        // 5 - Literal segments
        // 4 - Multi-part segments && Constrained parameter segments
        // 3 - Unconstrained parameter segements
        // 2 - Constrained wildcard parameter segments
        // 1 - Unconstrained wildcard parameter segments
        private static int ComputeGenerationDigit(TemplateSegment segment)
        {
            if(segment.Parts.Count > 1)
            {
                return 4;
            }

            var part = segment.Parts[0];
            if(part.IsLiteral)
            {
                return 5;
            }
            else
            {
                Debug.Assert(part.IsParameter);
                var digit = part.IsCatchAll ? 1 :  3;

                if (part.InlineConstraints != null && part.InlineConstraints.Any())
                {
                    digit++;
                }

                return digit;
            }
        }
        // Segments have the following order:
        // 1 - Literal segments
        // 2 - Constrained parameter segments / Multi-part segments
        // 3 - Unconstrained parameter segments
        // 4 - Constrained wildcard parameter segments
        // 5 - Unconstrained wildcard parameter segments
        private static int ComputeDigit(TemplateSegment segment)
        {
            if (segment.Parts.Count > 1)
            {
                // Multi-part segments should appear after literal segments but before parameter segments
                return 2;
            }

            var part = segment.Parts[0];
            // Literal segments always go first
            if (part.IsLiteral)
            {
                return 1;
            }
            else
            {
                Debug.Assert(part.IsParameter);
                var digit = part.IsCatchAll ? 5 : 3;

                // If there is a route constraint for the parameter, reduce order by 1
                // Constrained parameters end up with order 2, Constrained catch alls end up with order 4
                if (part.InlineConstraints != null && part.InlineConstraints.Any())
                {
                    digit--;
                }

                return digit;
            }
        }
Ejemplo n.º 3
0
        private bool MatchComplexSegment(
            TemplateSegment routeSegment,
            string requestSegment,
            IReadOnlyDictionary <string, object> defaults,
            RouteValueDictionary values)
        {
            var indexOfLastSegment = routeSegment.Parts.Count - 1;

            // We match the request to the template starting at the rightmost parameter
            // If the last segment of template is optional, then request can match the
            // template with or without the last parameter. So we start with regular matching,
            // but if it doesn't match, we start with next to last parameter. Example:
            // Template: {p1}/{p2}.{p3?}. If the request is foo/bar.moo it will match right away
            // giving p3 value of moo. But if the request is foo/bar, we start matching from the
            // rightmost giving p3 the value of bar, then we end up not matching the segment.
            // In this case we start again from p2 to match the request and we succeed giving
            // the value bar to p2
            if (routeSegment.Parts[indexOfLastSegment].IsOptional &&
                routeSegment.Parts[indexOfLastSegment - 1].IsOptionalSeperator)
            {
                if (MatchComplexSegmentCore(routeSegment, requestSegment, Defaults, values, indexOfLastSegment))
                {
                    return(true);
                }
                else
                {
                    if (requestSegment.EndsWith(
                            routeSegment.Parts[indexOfLastSegment - 1].Text,
                            StringComparison.OrdinalIgnoreCase))
                    {
                        return(false);
                    }

                    return(MatchComplexSegmentCore(
                               routeSegment,
                               requestSegment,
                               Defaults,
                               values,
                               indexOfLastSegment - 2));
                }
            }
            else
            {
                return(MatchComplexSegmentCore(routeSegment, requestSegment, Defaults, values, indexOfLastSegment));
            }
        }
Ejemplo n.º 4
0
        private static bool IsSegmentValid(TemplateParserContext context, TemplateSegment segment)
        {
            // If a segment has multiple parts, then it can't contain a catch all.
            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];
                if (part.IsParameter && part.IsCatchAll && segment.Parts.Count > 1)
                {
                    context.Error = Resources.TemplateRoute_CannotHaveCatchAllInMultiSegment;
                    return(false);
                }
            }

            // if a segment has multiple parts, then the parameters can't be optional
            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];
                if (part.IsParameter && part.IsOptional && segment.Parts.Count > 1)
                {
                    context.Error = Resources.TemplateRoute_CannotHaveOptionalParameterInMultiSegment;
                    return(false);
                }
            }

            // A segment cannot containt two consecutive parameters
            var isLastSegmentParameter = false;

            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];
                if (part.IsParameter && isLastSegmentParameter)
                {
                    context.Error = Resources.TemplateRoute_CannotHaveConsecutiveParameters;
                    return(false);
                }

                isLastSegmentParameter = part.IsParameter;
            }

            return(true);
        }
Ejemplo n.º 5
0
        private static bool ParseLiteral(TemplateParserContext context, TemplateSegment segment)
        {
            context.Mark();

            string encoded;
            while (true)
            {
                if (context.Current == Separator)
                {
                    encoded = context.Capture();
                    context.Back();
                    break;
                }
                else if (context.Current == OpenBrace)
                {
                    if (!context.Next())
                    {
                        // This is a dangling open-brace, which is not allowed
                        context.Error = Resources.TemplateRoute_MismatchedParameter;
                        return false;
                    }

                    if (context.Current == OpenBrace)
                    {
                        // This is an 'escaped' brace in a literal, like "{{foo" - keep going.
                    }
                    else
                    {
                        // We've just seen the start of a parameter, so back up and return
                        context.Back();
                        encoded = context.Capture();
                        context.Back();
                        break;
                    }
                }
                else if (context.Current == CloseBrace)
                {
                    if (!context.Next())
                    {
                        // This is a dangling close-brace, which is not allowed
                        context.Error = Resources.TemplateRoute_MismatchedParameter;
                        return false;
                    }

                    if (context.Current == CloseBrace)
                    {
                        // This is an 'escaped' brace in a literal, like "{{foo" - keep going.
                    }
                    else
                    {
                        // This is an unbalanced close-brace, which is not allowed
                        context.Error = Resources.TemplateRoute_MismatchedParameter;
                        return false;
                    }
                }

                if (!context.Next())
                {
                    encoded = context.Capture();
                    break;
                }
            }

            var decoded = encoded.Replace("}}", "}").Replace("{{", "{");
            if (IsValidLiteral(context, decoded))
            {
                segment.Parts.Add(TemplatePart.CreateLiteral(decoded));
                return true;
            }
            else
            {
                return false;
            }
        }
Ejemplo n.º 6
0
        private static bool ParseParameter(TemplateParserContext context, TemplateSegment segment)
        {
            context.Mark();

            while (true)
            {
                if (context.Current == OpenBrace)
                {
                    // This is an open brace inside of a parameter, it has to be escaped
                    if (context.Next())
                    {
                        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.Next())
                    {
                        // This is the end of the string -and we have a valid parameter
                        context.Back();
                        break;
                    }

                    if (context.Current == CloseBrace)
                    {
                        // This is an 'escaped' brace in a parameter name
                    }
                    else
                    {
                        // This is the end of the parameter
                        context.Back();
                        break;
                    }
                }

                if (!context.Next())
                {
                    // This is a dangling open-brace, which is not allowed
                    context.Error = Resources.TemplateRoute_MismatchedParameter;
                    return false;
                }
            }

            var rawParameter = context.Capture();
            var decoded = rawParameter.Replace("}}", "}").Replace("{{", "{");

            // At this point, we need to parse the raw name for inline constraint,
            // default values and optional parameters.
            var templatePart = InlineRouteParameterParser.ParseRouteParameter(decoded);

            if (templatePart.IsCatchAll && templatePart.IsOptional)
            {
                context.Error = Resources.TemplateRoute_CatchAllCannotBeOptional;
                return false;
            }

            if (templatePart.IsOptional && templatePart.DefaultValue != 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))
            {
                segment.Parts.Add(templatePart);
                return true;
            }
            else
            {
                return false;
            }
        }
Ejemplo n.º 7
0
        private bool MatchComplexSegment(TemplateSegment routeSegment,
                                         string requestSegment,
                                         IDictionary <string, object> defaults,
                                         RouteValueDictionary values)
        {
            Contract.Assert(routeSegment != null);
            Contract.Assert(routeSegment.Parts.Count > 1);

            // Find last literal segment and get its last index in the string
            var lastIndex = requestSegment.Length;
            var indexOfLastSegmentUsed = routeSegment.Parts.Count - 1;

            TemplatePart parameterNeedsValue = null; // Keeps track of a parameter segment that is pending a value
            TemplatePart lastLiteral         = null; // Keeps track of the left-most literal we've encountered

            while (indexOfLastSegmentUsed >= 0)
            {
                var newLastIndex = lastIndex;

                var part = routeSegment.Parts[indexOfLastSegmentUsed];
                if (part.IsParameter)
                {
                    // Hold on to the parameter so that we can fill it in when we locate the next literal
                    parameterNeedsValue = part;
                }
                else
                {
                    Contract.Assert(part.IsLiteral);
                    lastLiteral = part;

                    var startIndex = lastIndex - 1;
                    // If we have a pending parameter subsegment, we must leave at least one character for that
                    if (parameterNeedsValue != null)
                    {
                        startIndex--;
                    }

                    if (startIndex < 0)
                    {
                        return(false);
                    }

                    var indexOfLiteral = requestSegment.LastIndexOf(part.Text,
                                                                    startIndex,
                                                                    StringComparison.OrdinalIgnoreCase);
                    if (indexOfLiteral == -1)
                    {
                        // If we couldn't find this literal index, this segment cannot match
                        return(false);
                    }

                    // If the first subsegment is a literal, it must match at the right-most extent of the request URI.
                    // Without this check if your route had "/Foo/" we'd match the request URI "/somethingFoo/".
                    // This check is related to the check we do at the very end of this function.
                    if (indexOfLastSegmentUsed == (routeSegment.Parts.Count - 1))
                    {
                        if ((indexOfLiteral + part.Text.Length) != requestSegment.Length)
                        {
                            return(false);
                        }
                    }

                    newLastIndex = indexOfLiteral;
                }

                if ((parameterNeedsValue != null) &&
                    (((lastLiteral != null) && (part.IsLiteral)) || (indexOfLastSegmentUsed == 0)))
                {
                    // If we have a pending parameter that needs a value, grab that value

                    int parameterStartIndex;
                    int parameterTextLength;

                    if (lastLiteral == null)
                    {
                        if (indexOfLastSegmentUsed == 0)
                        {
                            parameterStartIndex = 0;
                        }
                        else
                        {
                            parameterStartIndex = newLastIndex;
                            Contract.Assert(false, "indexOfLastSegementUsed should always be 0 from the check above");
                        }
                        parameterTextLength = lastIndex;
                    }
                    else
                    {
                        // If we're getting a value for a parameter that is somewhere in the middle of the segment
                        if ((indexOfLastSegmentUsed == 0) && (part.IsParameter))
                        {
                            parameterStartIndex = 0;
                            parameterTextLength = lastIndex;
                        }
                        else
                        {
                            parameterStartIndex = newLastIndex + lastLiteral.Text.Length;
                            parameterTextLength = lastIndex - parameterStartIndex;
                        }
                    }

                    var parameterValueString = requestSegment.Substring(parameterStartIndex, parameterTextLength);

                    if (string.IsNullOrEmpty(parameterValueString))
                    {
                        // If we're here that means we have a segment that contains multiple sub-segments.
                        // For these segments all parameters must have non-empty values. If the parameter
                        // has an empty value it's not a match.
                        return(false);
                    }
                    else
                    {
                        // If there's a value in the segment for this parameter, use the subsegment value
                        values.Add(parameterNeedsValue.Name, parameterValueString);
                    }

                    parameterNeedsValue = null;
                    lastLiteral         = null;
                }

                lastIndex = newLastIndex;
                indexOfLastSegmentUsed--;
            }

            // If the last subsegment is a parameter, it's OK that we didn't parse all the way to the left extent of
            // the string since the parameter will have consumed all the remaining text anyway. If the last subsegment
            // is a literal then we *must* have consumed the entire text in that literal. Otherwise we end up matching
            // the route "Foo" to the request URI "somethingFoo". Thus we have to check that we parsed the *entire*
            // request URI in order for it to be a match.
            // This check is related to the check we do earlier in this function for LiteralSubsegments.
            return((lastIndex == 0) || routeSegment.Parts[0].IsParameter);
        }
Ejemplo n.º 8
0
        private static bool ParseSegment(TemplateParserContext context, List <TemplateSegment> segments)
        {
            Debug.Assert(context != null);
            Debug.Assert(segments != null);

            var segment = new TemplateSegment();

            while (true)
            {
                if (context.Current == OpenBrace)
                {
                    if (!context.Next())
                    {
                        // This is a dangling open-brace, which is not allowed
                        context.Error = Resources.TemplateRoute_MismatchedParameter;
                        return(false);
                    }

                    if (context.Current == OpenBrace)
                    {
                        // This is an 'escaped' brace in a literal, like "{{foo"
                        context.Back();
                        if (!ParseLiteral(context, segment))
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        // This is the inside of a parameter
                        if (!ParseParameter(context, segment))
                        {
                            return(false);
                        }
                    }
                }
                else if (context.Current == Separator)
                {
                    // We've reached the end of the segment
                    break;
                }
                else
                {
                    if (!ParseLiteral(context, segment))
                    {
                        return(false);
                    }
                }

                if (!context.Next())
                {
                    // We've reached the end of the string
                    break;
                }
            }

            if (IsSegmentValid(context, segment))
            {
                segments.Add(segment);
                return(true);
            }
            else
            {
                return(false);
            }
        }
Ejemplo n.º 9
0
        private static bool ParseLiteral(TemplateParserContext context, TemplateSegment segment)
        {
            context.Mark();

            string encoded;

            while (true)
            {
                if (context.Current == Separator)
                {
                    encoded = context.Capture();
                    context.Back();
                    break;
                }
                else if (context.Current == OpenBrace)
                {
                    if (!context.Next())
                    {
                        // This is a dangling open-brace, which is not allowed
                        context.Error = Resources.TemplateRoute_MismatchedParameter;
                        return(false);
                    }

                    if (context.Current == OpenBrace)
                    {
                        // This is an 'escaped' brace in a literal, like "{{foo" - keep going.
                    }
                    else
                    {
                        // We've just seen the start of a parameter, so back up and return
                        context.Back();
                        encoded = context.Capture();
                        context.Back();
                        break;
                    }
                }
                else if (context.Current == CloseBrace)
                {
                    if (!context.Next())
                    {
                        // This is a dangling close-brace, which is not allowed
                        context.Error = Resources.TemplateRoute_MismatchedParameter;
                        return(false);
                    }

                    if (context.Current == CloseBrace)
                    {
                        // This is an 'escaped' brace in a literal, like "{{foo" - keep going.
                    }
                    else
                    {
                        // This is an unbalanced close-brace, which is not allowed
                        context.Error = Resources.TemplateRoute_MismatchedParameter;
                        return(false);
                    }
                }

                if (!context.Next())
                {
                    encoded = context.Capture();
                    break;
                }
            }

            var decoded = encoded.Replace("}}", "}").Replace("{{", "}");

            if (IsValidLiteral(context, decoded))
            {
                segment.Parts.Add(TemplatePart.CreateLiteral(decoded));
                return(true);
            }
            else
            {
                return(false);
            }
        }
Ejemplo n.º 10
0
        private bool MatchComplexSegmentCore(
            TemplateSegment routeSegment,
            string requestSegment,
            IReadOnlyDictionary<string, object> defaults,
            RouteValueDictionary values,
            int indexOfLastSegmentUsed)
        {
            Debug.Assert(routeSegment != null);
            Debug.Assert(routeSegment.Parts.Count > 1);

            // Find last literal segment and get its last index in the string
            var lastIndex = requestSegment.Length;

            TemplatePart parameterNeedsValue = null; // Keeps track of a parameter segment that is pending a value
            TemplatePart lastLiteral = null; // Keeps track of the left-most literal we've encountered

            var outValues = new RouteValueDictionary();

            while (indexOfLastSegmentUsed >= 0)
            {
                var newLastIndex = lastIndex;

                var part = routeSegment.Parts[indexOfLastSegmentUsed];
                if (part.IsParameter)
                {
                    // Hold on to the parameter so that we can fill it in when we locate the next literal
                    parameterNeedsValue = part;
                }
                else
                {
                    Debug.Assert(part.IsLiteral);
                    lastLiteral = part;

                    var startIndex = lastIndex - 1;
                    // If we have a pending parameter subsegment, we must leave at least one character for that
                    if (parameterNeedsValue != null)
                    {
                        startIndex--;
                    }

                    if (startIndex < 0)
                    {
                        return false;
                    }

                    var indexOfLiteral = requestSegment.LastIndexOf(
                        part.Text,
                        startIndex,
                        StringComparison.OrdinalIgnoreCase);
                    if (indexOfLiteral == -1)
                    {
                        // If we couldn't find this literal index, this segment cannot match
                        return false;
                    }

                    // If the first subsegment is a literal, it must match at the right-most extent of the request URI.
                    // Without this check if your route had "/Foo/" we'd match the request URI "/somethingFoo/".
                    // This check is related to the check we do at the very end of this function.
                    if (indexOfLastSegmentUsed == (routeSegment.Parts.Count - 1))
                    {
                        if ((indexOfLiteral + part.Text.Length) != requestSegment.Length)
                        {
                            return false;
                        }
                    }

                    newLastIndex = indexOfLiteral;
                }

                if ((parameterNeedsValue != null) &&
                    (((lastLiteral != null) && (part.IsLiteral)) || (indexOfLastSegmentUsed == 0)))
                {
                    // If we have a pending parameter that needs a value, grab that value

                    int parameterStartIndex;
                    int parameterTextLength;

                    if (lastLiteral == null)
                    {
                        if (indexOfLastSegmentUsed == 0)
                        {
                            parameterStartIndex = 0;
                        }
                        else
                        {
                            parameterStartIndex = newLastIndex;
                            Debug.Assert(false, "indexOfLastSegementUsed should always be 0 from the check above");
                        }
                        parameterTextLength = lastIndex;
                    }
                    else
                    {
                        // If we're getting a value for a parameter that is somewhere in the middle of the segment
                        if ((indexOfLastSegmentUsed == 0) && (part.IsParameter))
                        {
                            parameterStartIndex = 0;
                            parameterTextLength = lastIndex;
                        }
                        else
                        {
                            parameterStartIndex = newLastIndex + lastLiteral.Text.Length;
                            parameterTextLength = lastIndex - parameterStartIndex;
                        }
                    }

                    var parameterValueString = requestSegment.Substring(parameterStartIndex, parameterTextLength);

                    if (string.IsNullOrEmpty(parameterValueString))
                    {
                        // If we're here that means we have a segment that contains multiple sub-segments.
                        // For these segments all parameters must have non-empty values. If the parameter
                        // has an empty value it's not a match.                        
                        return false;

                    }
                    else
                    {
                        // If there's a value in the segment for this parameter, use the subsegment value
                        outValues.Add(parameterNeedsValue.Name, parameterValueString);
                    }

                    parameterNeedsValue = null;
                    lastLiteral = null;
                }

                lastIndex = newLastIndex;
                indexOfLastSegmentUsed--;
            }

            // If the last subsegment is a parameter, it's OK that we didn't parse all the way to the left extent of
            // the string since the parameter will have consumed all the remaining text anyway. If the last subsegment
            // is a literal then we *must* have consumed the entire text in that literal. Otherwise we end up matching
            // the route "Foo" to the request URI "somethingFoo". Thus we have to check that we parsed the *entire*
            // request URI in order for it to be a match.
            // This check is related to the check we do earlier in this function for LiteralSubsegments.
            if (lastIndex == 0 || routeSegment.Parts[0].IsParameter)
            {
                foreach (var item in outValues)
                {
                    values.Add(item.Key, item.Value);
                }

                return true;
            }

            return false;
        }
Ejemplo n.º 11
0
        private bool MatchComplexSegment(
            TemplateSegment routeSegment,
            string requestSegment,
            IReadOnlyDictionary<string, object> defaults,
            RouteValueDictionary values)
        {
            var indexOfLastSegment = routeSegment.Parts.Count - 1;

            // We match the request to the template starting at the rightmost parameter
            // If the last segment of template is optional, then request can match the 
            // template with or without the last parameter. So we start with regular matching,
            // but if it doesn't match, we start with next to last parameter. Example:
            // Template: {p1}/{p2}.{p3?}. If the request is foo/bar.moo it will match right away
            // giving p3 value of moo. But if the request is foo/bar, we start matching from the
            // rightmost giving p3 the value of bar, then we end up not matching the segment.
            // In this case we start again from p2 to match the request and we succeed giving
            // the value bar to p2
            if (routeSegment.Parts[indexOfLastSegment].IsOptional &&
                routeSegment.Parts[indexOfLastSegment - 1].IsOptionalSeperator)
            {
                if (MatchComplexSegmentCore(routeSegment, requestSegment, Defaults, values, indexOfLastSegment))
                {
                    return true;
                }
                else
                {
                    if (requestSegment.EndsWith(
                        routeSegment.Parts[indexOfLastSegment - 1].Text,
                        StringComparison.OrdinalIgnoreCase))
                    {
                        return false;
                    }

                    return MatchComplexSegmentCore(
                        routeSegment,
                        requestSegment,
                        Defaults,
                        values,
                        indexOfLastSegment - 2);
                }
            }
            else
            {
                return MatchComplexSegmentCore(routeSegment, requestSegment, Defaults, values, indexOfLastSegment);
            }
        }
        private static bool IsSegmentValid(TemplateParserContext context, TemplateSegment segment)
        {
            // If a segment has multiple parts, then it can't contain a catch all.
            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];
                if (part.IsParameter && part.IsCatchAll && segment.Parts.Count > 1)
                {
                    context.Error = Resources.TemplateRoute_CannotHaveCatchAllInMultiSegment;
                    return(false);
                }
            }

            // if a segment has multiple parts, then only the last one parameter can be optional
            // if it is following a optional seperator.
            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];

                if (part.IsParameter && part.IsOptional && segment.Parts.Count > 1)
                {
                    // This is the last part
                    if (i == segment.Parts.Count - 1)
                    {
                        Debug.Assert(segment.Parts[i - 1].IsLiteral);

                        if (segment.Parts[i - 1].Text == PeriodString)
                        {
                            segment.Parts[i - 1].IsOptionalSeperator = true;
                        }
                        else
                        {
                            context.Error =
                                Resources.TemplateRoute_CanHaveOnlyLastParameterOptional_IfFollowingOptionalSeperator;
                            return(false);
                        }
                    }
                    else
                    {
                        context.Error =
                            Resources.TemplateRoute_CanHaveOnlyLastParameterOptional_IfFollowingOptionalSeperator;
                        return(false);
                    }
                }
            }

            // A segment cannot contain two consecutive parameters
            var isLastSegmentParameter = false;

            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];
                if (part.IsParameter && isLastSegmentParameter)
                {
                    context.Error = Resources.TemplateRoute_CannotHaveConsecutiveParameters;
                    return(false);
                }

                isLastSegmentParameter = part.IsParameter;
            }

            return(true);
        }
Ejemplo n.º 13
0
        private static bool IsSegmentValid(TemplateParserContext context, TemplateSegment segment)
        {
            // If a segment has multiple parts, then it can't contain a catch all.
            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];
                if (part.IsParameter && part.IsCatchAll && segment.Parts.Count > 1)
                {
                    context.Error = Resources.TemplateRoute_CannotHaveCatchAllInMultiSegment;
                    return(false);
                }
            }

            // if a segment has multiple parts, then only the last one parameter can be optional
            // if it is following a optional seperator.
            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];

                if (part.IsParameter && part.IsOptional && segment.Parts.Count > 1)
                {
                    // This optional parameter is the last part in the segment
                    if (i == segment.Parts.Count - 1)
                    {
                        Debug.Assert(segment.Parts[i - 1].IsLiteral);

                        // the optional parameter is preceded by a period
                        if (segment.Parts[i - 1].Text == PeriodString)
                        {
                            segment.Parts[i - 1].IsOptionalSeperator = true;
                        }
                        else
                        {
                            // The optional parameter is preceded by a literal other than period
                            // Example of error message:
                            // "In the complex segment {RouteValue}-{param?}, the optional parameter 'param'is preceded
                            // by an invalid segment "-". Only valid literal to precede an optional parameter is a
                            // period (.).
                            context.Error = string.Format(
                                Resources.TemplateRoute_OptionalParameterCanbBePrecededByPeriod,
                                segment.DebuggerToString(),
                                part.Name,
                                segment.Parts[i - 1].Text);

                            return(false);
                        }
                    }
                    else
                    {
                        // This optional parameter is not the last one in the segment
                        // Example:
                        // An optional parameter must be at the end of the segment.In the segment '{RouteValue?})',
                        // optional parameter 'RouteValue' is followed by ')'
                        var nextPart        = segment.Parts[i + 1];
                        var invalidPartText = nextPart.IsParameter ? nextPart.Name : nextPart.Text;

                        context.Error = string.Format(
                            Resources.TemplateRoute_OptionalParameterHasTobeTheLast,
                            segment.DebuggerToString(),
                            segment.Parts[i].Name,
                            invalidPartText
                            );

                        return(false);
                    }
                }
            }

            // A segment cannot contain two consecutive parameters
            var isLastSegmentParameter = false;

            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];
                if (part.IsParameter && isLastSegmentParameter)
                {
                    context.Error = Resources.TemplateRoute_CannotHaveConsecutiveParameters;
                    return(false);
                }

                isLastSegmentParameter = part.IsParameter;
            }

            return(true);
        }
Ejemplo n.º 14
0
        private static bool ParseParameter(TemplateParserContext context, TemplateSegment segment)
        {
            context.Mark();

            while (true)
            {
                if (context.Current == OpenBrace)
                {
                    // This is an open brace inside of a parameter, it has to be escaped
                    if (context.Next())
                    {
                        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.Next())
                    {
                        // This is the end of the string -and we have a valid parameter
                        context.Back();
                        break;
                    }

                    if (context.Current == CloseBrace)
                    {
                        // This is an 'escaped' brace in a parameter name
                    }
                    else
                    {
                        // This is the end of the parameter
                        context.Back();
                        break;
                    }
                }

                if (!context.Next())
                {
                    // This is a dangling open-brace, which is not allowed
                    context.Error = Resources.TemplateRoute_MismatchedParameter;
                    return(false);
                }
            }

            var rawParameter = context.Capture();
            var decoded      = rawParameter.Replace("}}", "}").Replace("{{", "{");

            // At this point, we need to parse the raw name for inline constraint,
            // default values and optional parameters.
            var templatePart = InlineRouteParameterParser.ParseRouteParameter(decoded);

            if (templatePart.IsCatchAll && templatePart.IsOptional)
            {
                context.Error = Resources.TemplateRoute_CatchAllCannotBeOptional;
                return(false);
            }

            if (templatePart.IsOptional && templatePart.DefaultValue != 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))
            {
                segment.Parts.Add(templatePart);
                return(true);
            }
            else
            {
                return(false);
            }
        }
Ejemplo n.º 15
0
        private static bool IsSegmentValid(TemplateParserContext context, TemplateSegment segment)
        {
            // If a segment has multiple parts, then it can't contain a catch all.
            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];
                if (part.IsParameter && part.IsCatchAll && segment.Parts.Count > 1)
                {
                    context.Error = Resources.TemplateRoute_CannotHaveCatchAllInMultiSegment;
                    return false;
                }
            }

            // if a segment has multiple parts, then only the last one parameter can be optional 
            // if it is following a optional seperator. 
            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];

                if (part.IsParameter && part.IsOptional && segment.Parts.Count > 1)
                {
                    // This optional parameter is the last part in the segment
                    if (i == segment.Parts.Count - 1)
                    {
                        Debug.Assert(segment.Parts[i - 1].IsLiteral);

                        // the optional parameter is preceded by a period
                        if (segment.Parts[i - 1].Text == PeriodString)
                        {
                            segment.Parts[i - 1].IsOptionalSeperator = true;
                        }
                        else
                        {
                            // The optional parameter is preceded by a literal other than period
                            // Example of error message:
                            // "In the complex segment {RouteValue}-{param?}, the optional parameter 'param'is preceded
                            // by an invalid segment "-". Only valid literal to precede an optional parameter is a 
                            // period (.).
                            context.Error = string.Format(
                                Resources.TemplateRoute_OptionalParameterCanbBePrecededByPeriod,
                                segment.DebuggerToString(),
                                part.Name,
                                segment.Parts[i - 1].Text);

                            return false;
                        }
                    }
                    else
                    {
                        // This optional parameter is not the last one in the segment
                        // Example:
                        // An optional parameter must be at the end of the segment.In the segment '{RouteValue?})', 
                        // optional parameter 'RouteValue' is followed by ')'
                        var nextPart = segment.Parts[i + 1];
                        var invalidPartText = nextPart.IsParameter ? nextPart.Name : nextPart.Text;

                        context.Error = string.Format(
                            Resources.TemplateRoute_OptionalParameterHasTobeTheLast,
                            segment.DebuggerToString(),
                            segment.Parts[i].Name,
                            invalidPartText
                            );

                        return false;
                    }
                }
            }

            // A segment cannot contain two consecutive parameters
            var isLastSegmentParameter = false;
            for (var i = 0; i < segment.Parts.Count; i++)
            {
                var part = segment.Parts[i];
                if (part.IsParameter && isLastSegmentParameter)
                {
                    context.Error = Resources.TemplateRoute_CannotHaveConsecutiveParameters;
                    return false;
                }

                isLastSegmentParameter = part.IsParameter;
            }

            return true;
        }
Ejemplo n.º 16
0
        private static bool ParseSegment(TemplateParserContext context, List<TemplateSegment> segments)
        {
            Debug.Assert(context != null);
            Debug.Assert(segments != null);

            var segment = new TemplateSegment();

            while (true)
            {
                if (context.Current == OpenBrace)
                {
                    if (!context.Next())
                    {
                        // This is a dangling open-brace, which is not allowed
                        context.Error = Resources.TemplateRoute_MismatchedParameter;
                        return false;
                    }

                    if (context.Current == OpenBrace)
                    {
                        // This is an 'escaped' brace in a literal, like "{{foo"
                        context.Back();
                        if (!ParseLiteral(context, segment))
                        {
                            return false;
                        }
                    }
                    else
                    {
                        // This is the inside of a parameter
                        if (!ParseParameter(context, segment))
                        {
                            return false;
                        }
                    }
                }
                else if (context.Current == Separator)
                {
                    // We've reached the end of the segment
                    break;
                }
                else
                {
                    if (!ParseLiteral(context, segment))
                    {
                        return false;
                    }
                }

                if (!context.Next())
                {
                    // We've reached the end of the string
                    break;
                }
            }

            if (IsSegmentValid(context, segment))
            {
                segments.Add(segment);
                return true;
            }
            else
            {
                return false;
            }
        }
Ejemplo n.º 17
0
        private static bool ParseParameter(TemplateParserContext context, TemplateSegment segment)
        {
            context.Mark();

            while (true)
            {
                if (context.Current == Separator)
                {
                    // This is a dangling open-brace, which is not allowed
                    context.Error = Resources.TemplateRoute_MismatchedParameter;
                    return(false);
                }
                else if (context.Current == OpenBrace)
                {
                    // If we see a '{' while parsing a parameter name it's invalid. We'll just accept it for now
                    // and let the validation code for the name find it.
                }
                else if (context.Current == CloseBrace)
                {
                    if (!context.Next())
                    {
                        // This is the end of the string - and we have a valid parameter
                        context.Back();
                        break;
                    }

                    if (context.Current == CloseBrace)
                    {
                        // This is an 'escaped' brace in a parameter name, which is not allowed.
                        // We'll just accept it for now and let the validation code for the name find it.
                    }
                    else
                    {
                        // This is the end of the parameter
                        context.Back();
                        break;
                    }
                }

                if (!context.Next())
                {
                    // This is a dangling open-brace, which is not allowed
                    context.Error = Resources.TemplateRoute_MismatchedParameter;
                    return(false);
                }
            }

            var rawParameter = context.Capture();

            // At this point, we need to parse the raw name for inline constraint,
            // default values and optional parameters.
            var templatePart = InlineRouteParameterParser.ParseRouteParameter(rawParameter);

            if (templatePart.IsCatchAll && templatePart.IsOptional)
            {
                context.Error = Resources.TemplateRoute_CatchAllCannotBeOptional;
                return(false);
            }

            if (templatePart.IsOptional && templatePart.DefaultValue != 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))
            {
                segment.Parts.Add(templatePart);
                return(true);
            }
            else
            {
                return(false);
            }
        }