Esempio n. 1
0
		void Parse ()
		{
			string url = Url;
			parameterNames = new Dictionary <string, bool> (StringComparer.OrdinalIgnoreCase);
			
			if (!String.IsNullOrEmpty (url)) {
				if (url [0] == '~' || url [0] == '/')
					throw new ArgumentException ("Url must not start with '~' or '/'");
				if (url.IndexOf ('?') >= 0)
					throw new ArgumentException ("Url must not contain '?'");
			} else {
				segments = new PatternSegment [0];
				tokens = new PatternToken [0];
				return;
			}
			
			string[] parts = url.Split ('/');
			int partsCount = segmentCount = parts.Length;
			var allTokens = new List <PatternToken> ();
			PatternToken tmpToken;
			
			segments = new PatternSegment [partsCount];
			
			for (int i = 0; i < partsCount; i++) {
				if (haveSegmentWithCatchAll)
					throw new ArgumentException ("A catch-all parameter can only appear as the last segment of the route URL");
				
				int catchAlls = 0;
				string part = parts [i];
				int partLength = part.Length;
				var tokens = new List <PatternToken> ();

				if (partLength == 0 && i < partsCount - 1)
					throw new ArgumentException ("Consecutive URL segment separators '/' are not allowed");

				if (part.IndexOf ("{}") != -1)
					throw new ArgumentException ("Empty URL parameter name is not allowed");

				if (i > 0)
					allTokens.Add (null);
				
				if (part.IndexOfAny (placeholderDelimiters) == -1) {
					// no placeholders here, short-circuit it
					tmpToken = new PatternToken (PatternTokenType.Literal, part);
					tokens.Add (tmpToken);
					allTokens.Add (tmpToken);
					segments [i].AllLiteral = true;
					segments [i].Tokens = tokens;
					continue;
				}

				string tmp;
				int from = 0, start;
				bool allLiteral = true;
				while (from < partLength) {
					start = part.IndexOf ('{', from);
					if (start >= partLength - 2)
						throw new ArgumentException ("Unterminated URL parameter. It must contain matching '}'");

					if (start < 0) {
						if (part.IndexOf ('}', from) >= from)
							throw new ArgumentException ("Unmatched URL parameter closer '}'. A corresponding '{' must precede");
						tmp = part.Substring (from);
						tmpToken = new PatternToken (PatternTokenType.Literal, tmp);
						tokens.Add (tmpToken);
						allTokens.Add (tmpToken);
						from += tmp.Length;
						break;
					}

					if (from == 0 && start > 0) {
						tmpToken = new PatternToken (PatternTokenType.Literal, part.Substring (0, start));
						tokens.Add (tmpToken);
						allTokens.Add (tmpToken);
					}
					
					int end = part.IndexOf ('}', start + 1);
					int next = part.IndexOf ('{', start + 1);
					
					if (end < 0 || next >= 0 && next < end)
						throw new ArgumentException ("Unterminated URL parameter. It must contain matching '}'");
					if (next == end + 1)
						throw new ArgumentException ("Two consecutive URL parameters are not allowed. Split into a different segment by '/', or a literal string.");

					if (next == -1)
						next = partLength;
					
					string token = part.Substring (start + 1, end - start - 1);
					PatternTokenType type;
					if (token [0] == '*') {
						catchAlls++;
						haveSegmentWithCatchAll = true;
						type = PatternTokenType.CatchAll;
						token = token.Substring (1);
					} else
						type = PatternTokenType.Standard;

					if (!parameterNames.ContainsKey (token))
						parameterNames.Add (token, true);

					tmpToken = new PatternToken (type, token);
					tokens.Add (tmpToken);
					allTokens.Add (tmpToken);
					allLiteral = false;
					
					if (end < partLength - 1) {
						token = part.Substring (end + 1, next - end - 1);
						tmpToken = new PatternToken (PatternTokenType.Literal, token);
						tokens.Add (tmpToken);
						allTokens.Add (tmpToken);
						end += token.Length;
					}

					if (catchAlls > 1 || (catchAlls == 1 && tokens.Count > 1))
						throw new ArgumentException ("A path segment that contains more than one section, such as a literal section or a parameter, cannot contain a catch-all parameter.");
					from = end + 1;
				}
				
				segments [i].AllLiteral = allLiteral;
				segments [i].Tokens = tokens;
			}

			if (allTokens.Count > 0)
				this.tokens = allTokens.ToArray ();
			allTokens = null;
		}
Esempio n. 2
0
        void Parse()
        {
            string url = Url;

            parameterNames = new Dictionary <string, bool> (StringComparer.OrdinalIgnoreCase);

            if (!String.IsNullOrEmpty(url))
            {
                if (url [0] == '~' || url [0] == '/')
                {
                    throw new ArgumentException("Url must not start with '~' or '/'");
                }
                if (url.IndexOf('?') >= 0)
                {
                    throw new ArgumentException("Url must not contain '?'");
                }
            }
            else
            {
                segments = new PatternSegment [0];
                tokens   = new PatternToken [0];
                return;
            }

            string[]     parts      = url.Split('/');
            int          partsCount = segmentCount = parts.Length;
            var          allTokens  = new List <PatternToken> ();
            PatternToken tmpToken;

            segments = new PatternSegment [partsCount];

            for (int i = 0; i < partsCount; i++)
            {
                if (haveSegmentWithCatchAll)
                {
                    throw new ArgumentException("A catch-all parameter can only appear as the last segment of the route URL");
                }

                int    catchAlls  = 0;
                string part       = parts [i];
                int    partLength = part.Length;
                var    tokens     = new List <PatternToken> ();

                if (partLength == 0 && i < partsCount - 1)
                {
                    throw new ArgumentException("Consecutive URL segment separators '/' are not allowed");
                }

                if (part.IndexOf("{}") != -1)
                {
                    throw new ArgumentException("Empty URL parameter name is not allowed");
                }

                if (i > 0)
                {
                    allTokens.Add(null);
                }

                if (part.IndexOfAny(placeholderDelimiters) == -1)
                {
                    // no placeholders here, short-circuit it
                    tmpToken = new PatternToken(PatternTokenType.Literal, part);
                    tokens.Add(tmpToken);
                    allTokens.Add(tmpToken);
                    segments [i].AllLiteral = true;
                    segments [i].Tokens     = tokens;
                    continue;
                }

                string tmp;
                int    from = 0, start;
                bool   allLiteral = true;
                while (from < partLength)
                {
                    start = part.IndexOf('{', from);
                    if (start >= partLength - 2)
                    {
                        throw new ArgumentException("Unterminated URL parameter. It must contain matching '}'");
                    }

                    if (start < 0)
                    {
                        if (part.IndexOf('}', from) >= from)
                        {
                            throw new ArgumentException("Unmatched URL parameter closer '}'. A corresponding '{' must precede");
                        }
                        tmp      = part.Substring(from);
                        tmpToken = new PatternToken(PatternTokenType.Literal, tmp);
                        tokens.Add(tmpToken);
                        allTokens.Add(tmpToken);
                        from += tmp.Length;
                        break;
                    }

                    if (from == 0 && start > 0)
                    {
                        tmpToken = new PatternToken(PatternTokenType.Literal, part.Substring(0, start));
                        tokens.Add(tmpToken);
                        allTokens.Add(tmpToken);
                    }

                    int end  = part.IndexOf('}', start + 1);
                    int next = part.IndexOf('{', start + 1);

                    if (end < 0 || next >= 0 && next < end)
                    {
                        throw new ArgumentException("Unterminated URL parameter. It must contain matching '}'");
                    }
                    if (next == end + 1)
                    {
                        throw new ArgumentException("Two consecutive URL parameters are not allowed. Split into a different segment by '/', or a literal string.");
                    }

                    if (next == -1)
                    {
                        next = partLength;
                    }

                    string           token = part.Substring(start + 1, end - start - 1);
                    PatternTokenType type;
                    if (token [0] == '*')
                    {
                        catchAlls++;
                        haveSegmentWithCatchAll = true;
                        type  = PatternTokenType.CatchAll;
                        token = token.Substring(1);
                    }
                    else
                    {
                        type = PatternTokenType.Standard;
                    }

                    if (!parameterNames.ContainsKey(token))
                    {
                        parameterNames.Add(token, true);
                    }

                    tmpToken = new PatternToken(type, token);
                    tokens.Add(tmpToken);
                    allTokens.Add(tmpToken);
                    allLiteral = false;

                    if (end < partLength - 1)
                    {
                        token    = part.Substring(end + 1, next - end - 1);
                        tmpToken = new PatternToken(PatternTokenType.Literal, token);
                        tokens.Add(tmpToken);
                        allTokens.Add(tmpToken);
                        end += token.Length;
                    }

                    if (catchAlls > 1 || (catchAlls == 1 && tokens.Count > 1))
                    {
                        throw new ArgumentException("A path segment that contains more than one section, such as a literal section or a parameter, cannot contain a catch-all parameter.");
                    }
                    from = end + 1;
                }

                segments [i].AllLiteral = allLiteral;
                segments [i].Tokens     = tokens;
            }

            if (allTokens.Count > 0)
            {
                this.tokens = allTokens.ToArray();
            }
            allTokens = null;
        }
Esempio n. 3
0
        public bool BuildUrl(Route route, RequestContext requestContext, RouteValueDictionary userValues, out string value)
        {
            value = null;
            if (requestContext == null)
            {
                return(false);
            }

            RouteData            routeData     = requestContext.RouteData;
            RouteValueDictionary defaultValues = route != null ? route.Defaults : null;
            RouteValueDictionary ambientValues = routeData.Values;

            if (defaultValues != null && defaultValues.Count == 0)
            {
                defaultValues = null;
            }
            if (ambientValues != null && ambientValues.Count == 0)
            {
                ambientValues = null;
            }
            if (userValues != null && userValues.Count == 0)
            {
                userValues = null;
            }

            // Check URL parameters
            // It is allowed to take ambient values for required parameters if:
            //
            //   - there are no default values provided
            //   - the default values dictionary contains at least one required
            //     parameter value
            //
            bool canTakeFromAmbient;

            if (defaultValues == null)
            {
                canTakeFromAmbient = true;
            }
            else
            {
                canTakeFromAmbient = false;
                foreach (KeyValuePair <string, bool> de in parameterNames)
                {
                    if (defaultValues.ContainsKey(de.Key))
                    {
                        canTakeFromAmbient = true;
                        break;
                    }
                }
            }

            bool allMustBeInUserValues = false;

            foreach (KeyValuePair <string, bool> de in parameterNames)
            {
                string parameterName = de.Key;
                // Is the parameter required?
                if (defaultValues == null || !defaultValues.ContainsKey(parameterName))
                {
                    // Yes, it is required (no value in defaults)
                    // Has the user provided value for it?
                    if (userValues == null || !userValues.ContainsKey(parameterName))
                    {
                        if (allMustBeInUserValues)
                        {
                            return(false);                            // partial override => no match
                        }
                        if (!canTakeFromAmbient || ambientValues == null || !ambientValues.ContainsKey(parameterName))
                        {
                            return(false);                            // no value provided => no match
                        }
                    }
                    else if (canTakeFromAmbient)
                    {
                        allMustBeInUserValues = true;
                    }
                }
            }

            // Check for non-url parameters
            if (defaultValues != null)
            {
                foreach (var de in defaultValues)
                {
                    string parameterName = de.Key;

                    if (parameterNames.ContainsKey(parameterName))
                    {
                        continue;
                    }

                    object parameterValue = null;
                    // Has the user specified value for this parameter and, if
                    // yes, is it the same as the one in defaults?
                    if (userValues != null && userValues.TryGetValue(parameterName, out parameterValue))
                    {
                        object defaultValue = de.Value;
                        if (defaultValue is string && parameterValue is string)
                        {
                            if (String.Compare((string)defaultValue, (string)parameterValue, StringComparison.Ordinal) != 0)
                            {
                                return(false);                                // different value => no match
                            }
                        }
                        else if (defaultValue != parameterValue)
                        {
                            return(false);                            // different value => no match
                        }
                    }
                }
            }

            // Check the constraints
            RouteValueDictionary constraints = route != null ? route.Constraints : null;

            if (constraints != null && constraints.Count > 0)
            {
                HttpContextBase context = requestContext.HttpContext;
                bool            invalidConstraint;

                foreach (var de in constraints)
                {
                    if (!Route.ProcessConstraintInternal(context, route, de.Value, de.Key, userValues, RouteDirection.UrlGeneration, out invalidConstraint) ||
                        invalidConstraint)
                    {
                        return(false);                        // constraint not met => no match
                    }
                }
            }

            // We're a match, generate the URL
            var  ret     = new StringBuilder();
            bool canTrim = true;

            // Going in reverse order, so that we can trim without much ado
            int tokensCount = tokens.Length - 1;

            for (int i = tokensCount; i >= 0; i--)
            {
                PatternToken token = tokens [i];
                if (token == null)
                {
                    if (i < tokensCount && ret.Length > 0 && ret [0] != '/')
                    {
                        ret.Insert(0, '/');
                    }
                    continue;
                }

                if (token.Type == PatternTokenType.Literal)
                {
                    ret.Insert(0, token.Name);
                    continue;
                }

                string parameterName = token.Name;
                object tokenValue;

#if SYSTEMCORE_DEP
                if (userValues.GetValue(parameterName, out tokenValue))
                {
                    if (!defaultValues.Has(parameterName, tokenValue))
                    {
                        canTrim = false;
                        if (tokenValue != null)
                        {
                            ret.Insert(0, tokenValue.ToString());
                        }
                        continue;
                    }

                    if (!canTrim && tokenValue != null)
                    {
                        ret.Insert(0, tokenValue.ToString());
                    }
                    continue;
                }

                if (defaultValues.GetValue(parameterName, out tokenValue))
                {
                    object ambientTokenValue;
                    if (ambientValues.GetValue(parameterName, out ambientTokenValue))
                    {
                        tokenValue = ambientTokenValue;
                    }

                    if (!canTrim && tokenValue != null)
                    {
                        ret.Insert(0, tokenValue.ToString());
                    }
                    continue;
                }

                canTrim = false;
                if (ambientValues.GetValue(parameterName, out tokenValue))
                {
                    if (tokenValue != null)
                    {
                        ret.Insert(0, tokenValue.ToString());
                    }
                    continue;
                }
#endif
            }

            // All the values specified in userValues that aren't part of the original
            // URL, the constraints or defaults collections are treated as overflow
            // values - they are appended as query parameters to the URL
            if (userValues != null)
            {
                bool first = true;
                foreach (var de in userValues)
                {
                    string parameterName = de.Key;

#if SYSTEMCORE_DEP
                    if (parameterNames.ContainsKey(parameterName) || defaultValues.Has(parameterName) || constraints.Has(parameterName))
                    {
                        continue;
                    }
#endif

                    object parameterValue = de.Value;
                    if (parameterValue == null)
                    {
                        continue;
                    }

                    var parameterValueAsString = parameterValue as string;
                    if (parameterValueAsString != null && parameterValueAsString.Length == 0)
                    {
                        continue;
                    }

                    if (first)
                    {
                        ret.Append('?');
                        first = false;
                    }
                    else
                    {
                        ret.Append('&');
                    }


                    ret.Append(Uri.EscapeDataString(parameterName));
                    ret.Append('=');
                    if (parameterValue != null)
                    {
                        ret.Append(Uri.EscapeDataString(de.Value.ToString()));
                    }
                }
            }

            value = ret.ToString();
            return(true);
        }