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