Moved Pithos data and logs from the GRNET folder under AppData to a GRNET\PITHOS...
[pithos-ms-client] / trunk / Pithos.Core / Agents / CollectionExtensions.cs
index 4138953..781631c 100644 (file)
@@ -40,6 +40,7 @@
  */\r
 #endregion\r
 using System.Collections.Concurrent;\r
+using System.Diagnostics.Contracts;\r
 using Pithos.Interfaces;\r
 \r
 namespace Pithos.Core.Agents\r
@@ -54,6 +55,21 @@ namespace Pithos.Core.Agents
     /// </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
@@ -84,17 +100,151 @@ namespace Pithos.Core.Agents
         }\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