*/\r
#endregion\r
using System.Collections.Concurrent;\r
+using System.Diagnostics.Contracts;\r
using Pithos.Interfaces;\r
\r
namespace Pithos.Core.Agents\r
/// </summary>\r
public static class CollectionExtensions\r
{\r
+ public static IEnumerable<T> Replace<T>(this IEnumerable<T> list,Func<T,bool> match, Func<T,IEnumerable<T>> generate)\r
+ {\r
+ foreach (var item in list)\r
+ {\r
+ if (match(item))\r
+ foreach (var newItem in generate(item))\r
+ {\r
+ yield return newItem;\r
+ }\r
+ else\r
+ yield return item;\r
+ }\r
+ }\r
+\r
+\r
/// <summary>\r
/// Remove the first message in a queue that matches the predicate\r
/// </summary>\r
}\r
\r
\r
+ /// <summary>\r
+ /// Return only the info objects that are below one of the filter Uris\r
+ /// </summary>\r
+ /// <param name="infos">ObjectInfo items to filter</param>\r
+ /// <param name="filterUris">List of filter Uris</param>\r
+ /// <returns>An enumerable of ObjectInfo items. If the list of filter Uris is empty or null, the original list is returned</returns>\r
public static IEnumerable<ObjectInfo> FilterBelow(this IEnumerable<ObjectInfo> infos,List<Uri> filterUris )\r
{\r
if (filterUris == null)\r
return infos;\r
if (filterUris.Count == 0)\r
return infos;\r
+ //Allow all objects whose Uris start with any of the filters \r
var filteredUris = from info in infos\r
- where !filterUris.Any(s => info.Uri.ToString()\r
- .StartsWith(s.ToString()))\r
- select info;\r
+ where filterUris.Any(f => info.Uri.IsAtOrBelow(f)) \r
+ select info; \r
return filteredUris;\r
}\r
+\r
+ /// <summary>\r
+ /// Return only the info objects that are directly below one of the filter Uris\r
+ /// </summary>\r
+ /// <param name="infos">ObjectInfo items to filter</param>\r
+ /// <param name="filterUris">List of filter Uris</param>\r
+ /// <returns>An enumerable of ObjectInfo items. If the list of filter Uris is empty or null, the original list is returned</returns>\r
+ public static IEnumerable<ObjectInfo> FilterDirectlyBelow(this IEnumerable<ObjectInfo> infos, List<Uri> filterUris)\r
+ {\r
+ if (filterUris == null)\r
+ return infos;\r
+ if (filterUris.Count == 0)\r
+ return infos;\r
+ //Allow all objects whose Uris start with any of the filters \r
+ var filteredUris = from info in infos\r
+ where filterUris.Any(f => info.Uri.IsAtOrDirectlyBelow(f)) \r
+ select info; \r
+ return filteredUris;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Checkes whether a Uri is below a root Uri\r
+ /// </summary>\r
+ /// <param name="target"></param>\r
+ /// <param name="root"></param>\r
+ /// <returns></returns>\r
+ public static bool IsAtOrBelow(this Uri target,Uri root)\r
+ {\r
+ Contract.Requires(root != null);\r
+ Contract.Requires(target != null);\r
+\r
+ var targetSegments = target.Segments;\r
+ var rootSegments = root.Segments;\r
+\r
+ return InnerAtOrBelow(targetSegments, rootSegments);\r
+ }\r
+\r
+ private static bool InnerAtOrBelow(string[] targetSegments, string[] rootSegments)\r
+ {\r
+ //If the uri is shorter than the root, no point in comparing\r
+ if (targetSegments.Length < rootSegments.Length)\r
+ return false;\r
+ //If the uri is below the root, it should match the root's segments one by one\r
+ //If there is any root segment that doesn't match its corresponding target segment,\r
+ //the target is not below the root\r
+ var mismatch = rootSegments\r
+ .Where((t, i) => !String.Equals(targetSegments[i], t))\r
+ .Any();\r
+ return !mismatch;\r
+ }\r
+\r
+\r
+ /// <summary>\r
+ /// Checks whether a Uri is directly below a root Uri\r
+ /// </summary>\r
+ /// <param name="target"></param>\r
+ /// <param name="root"></param>\r
+ /// <returns></returns>\r
+ public static bool IsAtOrDirectlyBelow(this Uri target,Uri root)\r
+ {\r
+ Contract.Requires(root!=null);\r
+ Contract.Requires(target!=null);\r
+\r
+ return\r
+ //If the target is directly below the root, it will have exactly \r
+ //one segment more than the root \r
+ target.Segments.Length == root.Segments.Length + 1\r
+ //Ensure that the candidate target is indeed below the root\r
+ && target.IsAtOrBelow(root);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Checkes whether a file path is below a root path\r
+ /// </summary>\r
+ /// <param name="targetPath"></param>\r
+ /// <param name="rootPath"></param>\r
+ /// <returns></returns>\r
+ public static bool IsAtOrBelow(this string targetPath, string rootPath)\r
+ {\r
+ Contract.Requires(!String.IsNullOrWhiteSpace(targetPath));\r
+ Contract.Requires(!String.IsNullOrWhiteSpace(rootPath));\r
+\r
+ var targetSegments = targetPath.Split('\\');\r
+ var rootSegments = rootPath.Split('\\');\r
+\r
+ return InnerAtOrBelow(targetSegments, rootSegments);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Checks whether a file path is directly below a root path\r
+ /// </summary>\r
+ /// <param name="targetPath"></param>\r
+ /// <param name="rootPath"></param>\r
+ /// <returns></returns>\r
+ public static bool IsAtOrDirectlyBelow(this string targetPath, string rootPath)\r
+ {\r
+ Contract.Requires(!String.IsNullOrWhiteSpace(targetPath));\r
+ Contract.Requires(!String.IsNullOrWhiteSpace(rootPath));\r
+\r
+ var targetSegments = targetPath.Split('\\');\r
+ var rootSegments = rootPath.Split('\\');\r
+\r
+ return\r
+ //If the target is directly below the root, it will have exactly \r
+ //one segment more than the root \r
+ targetSegments.Length == rootSegments.Length + 1\r
+ //Ensure that the candidate target is indeed below the root\r
+ && InnerAtOrBelow(targetSegments, rootSegments);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Returns the part of the target string that comes after the prefix string.\r
+ /// The target is not modified if it doesn't start with the prefix string\r
+ /// </summary>\r
+ /// <param name="target"></param>\r
+ /// <param name="remove"></param>\r
+ /// <returns></returns>\r
+ public static string After(this string target,string prefix)\r
+ { \r
+ //If the prefix or the target are empty, return the target\r
+ if (String.IsNullOrWhiteSpace(prefix) || String.IsNullOrWhiteSpace(target))\r
+ return target;\r
+ if (target.StartsWith(prefix))\r
+ return target.Remove(0,prefix.Length);\r
+ return target;\r
+ }\r
}\r
+\r
+\r
}\r