1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
|
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System
{
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Channels;
static class UriTemplateHelpers
{
static UriTemplateQueryComparer queryComparer = new UriTemplateQueryComparer();
static UriTemplateQueryKeyComparer queryKeyComperar = new UriTemplateQueryKeyComparer();
[Conditional("DEBUG")]
public static void AssertCanonical(string s)
{
Fx.Assert(s == s.ToUpperInvariant(), "non-canonicalized");
}
public static bool CanMatchQueryInterestingly(UriTemplate ut, NameValueCollection query, bool mustBeEspeciallyInteresting)
{
if (ut.queries.Count == 0)
{
return false; // trivial, not interesting
}
string[] queryKeys = query.AllKeys;
foreach (KeyValuePair<string, UriTemplateQueryValue> kvp in ut.queries)
{
string queryKeyName = kvp.Key;
if (kvp.Value.Nature == UriTemplatePartType.Literal)
{
bool queryKeysContainsQueryVarName = false;
for (int i = 0; i < queryKeys.Length; ++i)
{
if (StringComparer.OrdinalIgnoreCase.Equals(queryKeys[i], queryKeyName))
{
queryKeysContainsQueryVarName = true;
break;
}
}
if (!queryKeysContainsQueryVarName)
{
return false;
}
if (kvp.Value == UriTemplateQueryValue.Empty)
{
if (!string.IsNullOrEmpty(query[queryKeyName]))
{
return false;
}
}
else
{
if (((UriTemplateLiteralQueryValue)(kvp.Value)).AsRawUnescapedString() != query[queryKeyName])
{
return false;
}
}
}
else
{
if (mustBeEspeciallyInteresting && Array.IndexOf(queryKeys, queryKeyName) == -1)
{
return false;
}
}
}
return true;
}
public static bool CanMatchQueryTrivially(UriTemplate ut)
{
return (ut.queries.Count == 0);
}
public static void DisambiguateSamePath(UriTemplate[] array, int a, int b, bool allowDuplicateEquivalentUriTemplates)
{
// [a,b) all have same path
// ensure queries make them unambiguous
Fx.Assert(b > a, "array bug");
// sort empty queries to front
Array.Sort<UriTemplate>(array, a, b - a, queryComparer);
if (b - a == 1)
{
return; // if only one, cannot be ambiguous
}
if (!allowDuplicateEquivalentUriTemplates)
{
// ensure at most one empty query and ignore it
if (array[a].queries.Count == 0)
{
a++;
}
if (array[a].queries.Count == 0)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
SR.UTTDuplicate, array[a].ToString(), array[a - 1].ToString())));
}
if (b - a == 1)
{
return; // if only one, cannot be ambiguous
}
}
else
{
while (a < b && array[a].queries.Count == 0) // all equivalent
{
a++;
}
if (b - a <= 1)
{
return;
}
}
Fx.Assert(b > a, "array bug");
// now consider non-empty queries
// more than one, so enforce that
// forall
// exist set of querystringvars S where
// every op has literal value foreach var in S, and
// those literal tuples are different
EnsureQueriesAreDistinct(array, a, b, allowDuplicateEquivalentUriTemplates);
}
public static IEqualityComparer<string> GetQueryKeyComparer()
{
return queryKeyComperar;
}
public static string GetUriPath(Uri uri)
{
return uri.GetComponents(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.Unescaped);
}
public static bool HasQueryLiteralRequirements(UriTemplate ut)
{
foreach (UriTemplateQueryValue utqv in ut.queries.Values)
{
if (utqv.Nature == UriTemplatePartType.Literal)
{
return true;
}
}
return false;
}
public static UriTemplatePartType IdentifyPartType(string part)
{
// Identifying the nature of a string - Literal|Compound|Variable
// Algorithem is based on the following steps:
// - Finding the position of the first open curlly brace ('{') and close curlly brace ('}')
// in the string
// - If we don't find any this is a Literal
// - otherwise, we validate that position of the close brace is at least two characters from
// the position of the open brace
// - Then we identify if we are dealing with a compound string or a single variable string
// + var name is not at the string start --> Compound
// + var name is shorter then the entire string (End < Length-2 or End==Length-2
// and string ends with '/') --> Compound
// + otherwise --> Variable
int varStartIndex = part.IndexOf("{", StringComparison.Ordinal);
int varEndIndex = part.IndexOf("}", StringComparison.Ordinal);
if (varStartIndex == -1)
{
if (varEndIndex != -1)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
SR.GetString(SR.UTInvalidFormatSegmentOrQueryPart, part)));
}
return UriTemplatePartType.Literal;
}
else
{
if (varEndIndex < varStartIndex + 2)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
SR.GetString(SR.UTInvalidFormatSegmentOrQueryPart, part)));
}
if (varStartIndex > 0)
{
return UriTemplatePartType.Compound;
}
else if ((varEndIndex < part.Length - 2) ||
((varEndIndex == part.Length - 2) && !part.EndsWith("/", StringComparison.Ordinal)))
{
return UriTemplatePartType.Compound;
}
else
{
return UriTemplatePartType.Variable;
}
}
}
public static bool IsWildcardPath(string path)
{
if (path.IndexOf('/') != -1)
{
return false;
}
UriTemplatePartType partType;
return IsWildcardSegment(path, out partType);
}
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is internal method that needs to return multiple things")]
public static bool IsWildcardSegment(string segment, out UriTemplatePartType type)
{
type = IdentifyPartType(segment);
switch (type)
{
case UriTemplatePartType.Literal:
return (string.Compare(segment, UriTemplate.WildcardPath, StringComparison.Ordinal) == 0);
case UriTemplatePartType.Compound:
return false;
case UriTemplatePartType.Variable:
return ((segment.IndexOf(UriTemplate.WildcardPath, StringComparison.Ordinal) == 1) &&
!segment.EndsWith("/", StringComparison.Ordinal) &&
(segment.Length > UriTemplate.WildcardPath.Length + 2));
default:
Fx.Assert("Bad part type identification !");
return false;
}
}
public static NameValueCollection ParseQueryString(string query)
{
// We are adjusting the parsing of UrlUtility.ParseQueryString, which identify
// ?wsdl as a null key with wsdl as a value
NameValueCollection result = UrlUtility.ParseQueryString(query);
string nullKeyValuesString = result[(string)null];
if (!string.IsNullOrEmpty(nullKeyValuesString))
{
result.Remove(null);
string[] nullKeyValues = nullKeyValuesString.Split(',');
for (int i = 0; i < nullKeyValues.Length; i++)
{
result.Add(nullKeyValues[i], null);
}
}
return result;
}
static bool AllTemplatesAreEquivalent(IList<UriTemplate> array, int a, int b)
{
for (int i = a; i < b - 1; ++i)
{
if (!array[i].IsEquivalentTo(array[i + 1]))
{
return false;
}
}
return true;
}
static void EnsureQueriesAreDistinct(UriTemplate[] array, int a, int b, bool allowDuplicateEquivalentUriTemplates)
{
Dictionary<string, byte> queryVarNamesWithLiteralVals = new Dictionary<string, byte>(StringComparer.OrdinalIgnoreCase);
for (int i = a; i < b; ++i)
{
foreach (KeyValuePair<string, UriTemplateQueryValue> kvp in array[i].queries)
{
if (kvp.Value.Nature == UriTemplatePartType.Literal)
{
if (!queryVarNamesWithLiteralVals.ContainsKey(kvp.Key))
{
queryVarNamesWithLiteralVals.Add(kvp.Key, 0);
}
}
}
}
// now we have set of possibilities:
// further refine to only those for whom all templates have literals
Dictionary<string, byte> queryVarNamesAllLiterals = new Dictionary<string, byte>(queryVarNamesWithLiteralVals);
for (int i = a; i < b; ++i)
{
foreach (string s in queryVarNamesWithLiteralVals.Keys)
{
if (!array[i].queries.ContainsKey(s) || (array[i].queries[s].Nature != UriTemplatePartType.Literal))
{
queryVarNamesAllLiterals.Remove(s);
}
}
}
queryVarNamesWithLiteralVals = null; // ensure we don't reference this variable any more
// now we have the set of names that every operation has as a literal
if (queryVarNamesAllLiterals.Count == 0)
{
if (allowDuplicateEquivalentUriTemplates && AllTemplatesAreEquivalent(array, a, b))
{
// we're ok, do nothing
}
else
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
SR.UTTOtherAmbiguousQueries, array[a].ToString())));
}
}
// now just ensure that each template has a unique tuple of values for the names
string[][] upsLits = new string[b - a][];
for (int i = 0; i < b - a; ++i)
{
upsLits[i] = GetQueryLiterals(array[i + a], queryVarNamesAllLiterals);
}
for (int i = 0; i < b - a; ++i)
{
for (int j = i + 1; j < b - a; ++j)
{
if (Same(upsLits[i], upsLits[j]))
{
if (!array[i + a].IsEquivalentTo(array[j + a]))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
SR.UTTAmbiguousQueries, array[a + i].ToString(), array[j + a].ToString())));
}
Fx.Assert(array[i + a].IsEquivalentTo(array[j + a]), "bad equiv logic");
if (!allowDuplicateEquivalentUriTemplates)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
SR.UTTDuplicate, array[a + i].ToString(), array[j + a].ToString())));
}
}
}
}
// we're good. whew!
}
static string[] GetQueryLiterals(UriTemplate up, Dictionary<string, byte> queryVarNames)
{
string[] queryLitVals = new string[queryVarNames.Count];
int i = 0;
foreach (string queryVarName in queryVarNames.Keys)
{
Fx.Assert(up.queries.ContainsKey(queryVarName), "query doesn't have name");
UriTemplateQueryValue utqv = up.queries[queryVarName];
Fx.Assert(utqv.Nature == UriTemplatePartType.Literal, "query for name is not literal");
if (utqv == UriTemplateQueryValue.Empty)
{
queryLitVals[i] = null;
}
else
{
queryLitVals[i] = ((UriTemplateLiteralQueryValue)(utqv)).AsRawUnescapedString();
}
++i;
}
return queryLitVals;
}
static bool Same(string[] a, string[] b)
{
Fx.Assert(a.Length == b.Length, "arrays not same length");
for (int i = 0; i < a.Length; ++i)
{
if (a[i] != b[i])
{
return false;
}
}
return true;
}
class UriTemplateQueryComparer : IComparer<UriTemplate>
{
public int Compare(UriTemplate x, UriTemplate y)
{
// sort the empty queries to the front
return Comparer<int>.Default.Compare(x.queries.Count, y.queries.Count);
}
}
class UriTemplateQueryKeyComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return (string.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0);
}
public int GetHashCode(string obj)
{
if (obj == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("obj");
}
return obj.ToUpperInvariant().GetHashCode();
}
}
}
}
|