Fixed incorrect account key in PollAgent.cs
[pithos-ms-client] / trunk / Pithos.Core / Agents / CollectionExtensions.cs
1 #region\r
2 /* -----------------------------------------------------------------------\r
3  * <copyright file="CollectionExtensions.cs" company="GRNet">\r
4  * \r
5  * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
6  *\r
7  * Redistribution and use in source and binary forms, with or\r
8  * without modification, are permitted provided that the following\r
9  * conditions are met:\r
10  *\r
11  *   1. Redistributions of source code must retain the above\r
12  *      copyright notice, this list of conditions and the following\r
13  *      disclaimer.\r
14  *\r
15  *   2. Redistributions in binary form must reproduce the above\r
16  *      copyright notice, this list of conditions and the following\r
17  *      disclaimer in the documentation and/or other materials\r
18  *      provided with the distribution.\r
19  *\r
20  *\r
21  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
22  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
25  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
28  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
32  * POSSIBILITY OF SUCH DAMAGE.\r
33  *\r
34  * The views and conclusions contained in the software and\r
35  * documentation are those of the authors and should not be\r
36  * interpreted as representing official policies, either expressed\r
37  * or implied, of GRNET S.A.\r
38  * </copyright>\r
39  * -----------------------------------------------------------------------\r
40  */\r
41 #endregion\r
42 using System.Collections.Concurrent;\r
43 using System.Diagnostics.Contracts;\r
44 using Pithos.Interfaces;\r
45 using System;\r
46 using System.Collections.Generic;\r
47 using System.Linq;\r
48 \r
49 namespace Pithos.Core.Agents\r
50 {\r
51 \r
52     /// <summary>\r
53     /// Extension methods for collections\r
54     /// </summary>\r
55     public static class CollectionExtensions\r
56     {\r
57         public static IEnumerable<T> Replace<T>(this IEnumerable<T> list,Func<T,bool> match, Func<T,IEnumerable<T>> generate)\r
58         {\r
59             foreach (var item in list)\r
60             {\r
61                 if (match(item))\r
62                     foreach (var newItem in generate(item))\r
63                     {\r
64                         yield return newItem;\r
65                     }\r
66                 else\r
67                     yield return item;\r
68             }\r
69         }\r
70 \r
71 \r
72         /// <summary>\r
73         /// Remove the first message in a queue that matches the predicate\r
74         /// </summary>\r
75         /// <param name="queue">The queue</param>\r
76         /// <param name="predicate">The condition to match</param>\r
77         /// <remarks>Removes the first message that matches the predicate by dequeing all \r
78         /// messages and re-enqueing all except the first matching message</remarks>\r
79         public static void RemoveFirst<TMessage>(this ConcurrentQueue<TMessage> queue, Func<TMessage, bool> predicate)\r
80         {\r
81             //Can this work? Dequeue all items \r
82             //and then enqueue everything except the filtered items\r
83 \r
84             //Possible problems: \r
85             //* A matching item may be dequeued between one TryDequeue and the next\r
86             var temp = new Queue<TMessage>();\r
87             TMessage message;\r
88             var alreadyFound = false;\r
89             while (queue.TryDequeue(out message))\r
90             {\r
91                 if (!predicate(message) || alreadyFound)\r
92                     temp.Enqueue(message);\r
93                 else\r
94                 {\r
95                     alreadyFound = true;\r
96                 }\r
97             }\r
98 \r
99             queue.AddFromEnumerable(temp);\r
100         }\r
101 \r
102 \r
103         /// <summary>\r
104         /// Return only the info objects that are below one of the filter Uris\r
105         /// </summary>\r
106         /// <param name="infos">ObjectInfo items to filter</param>\r
107         /// <param name="filterUris">List of filter Uris</param>\r
108         /// <returns>An enumerable of ObjectInfo items. If the list of filter Uris is empty or null, the original list is returned</returns>\r
109         public static IEnumerable<ObjectInfo> FilterBelow(this IEnumerable<ObjectInfo> infos,List<Uri> filterUris  )\r
110         {\r
111             if (filterUris == null)\r
112                 return infos;\r
113             if (filterUris.Count == 0)\r
114                 return infos;\r
115             //Allow all objects whose Uris start with any of the filters            \r
116             var filteredUris = from info in infos\r
117                                   where filterUris.Any(f => info.Uri.IsAtOrBelow(f)) \r
118                                   select info;            \r
119             return filteredUris;\r
120         }\r
121 \r
122         /// <summary>\r
123         /// Return only the info objects that are directly below one of the filter Uris\r
124         /// </summary>\r
125         /// <param name="infos">ObjectInfo items to filter</param>\r
126         /// <param name="filterUris">List of filter Uris</param>\r
127         /// <returns>An enumerable of ObjectInfo items. If the list of filter Uris is empty or null, the original list is returned</returns>\r
128         public static IEnumerable<ObjectInfo> FilterDirectlyBelow(this IEnumerable<ObjectInfo> infos, List<Uri> filterUris)\r
129         {\r
130             if (filterUris == null)\r
131                 return infos;\r
132             if (filterUris.Count == 0)\r
133                 return infos;\r
134             //Allow all objects whose Uris start with any of the filters            \r
135             var filteredUris = from info in infos\r
136                                   where filterUris.Any(f => info.Uri.IsAtOrDirectlyBelow(f)) \r
137                                   select info;            \r
138             return filteredUris;\r
139         }\r
140 \r
141         /// <summary>\r
142         /// Checkes whether a Uri is below a root Uri\r
143         /// </summary>\r
144         /// <param name="target"></param>\r
145         /// <param name="root"></param>\r
146         /// <returns></returns>\r
147         public static bool IsAtOrBelow(this Uri target,Uri root)\r
148         {\r
149             if(root == null)\r
150                 throw new ArgumentNullException("root");\r
151             if(target == null)\r
152                 throw new ArgumentNullException("target");\r
153             Contract.EndContractBlock();\r
154 \r
155             var targetSegments = target.Segments;\r
156             var rootSegments = root.Segments;\r
157 \r
158             return InnerAtOrBelow(targetSegments, rootSegments);\r
159         }\r
160 \r
161         private static bool InnerAtOrBelow(string[] targetSegments, string[] rootSegments)\r
162         {\r
163             //If the uri is shorter than the root, no point in comparing\r
164             if (targetSegments.Length < rootSegments.Length)\r
165                 return false;\r
166             //If the uri is below the root, it should match the root's segments one by one\r
167             //If there is any root segment that doesn't match its corresponding target segment,\r
168             //the target is not below the root\r
169             //DON'T FORGET that Uri segments include the slashes. Must remove them to ensure proper checks\r
170             var mismatch = rootSegments\r
171                 .Where((t, i) => !String.Equals(targetSegments[i].TrimEnd('/'), t.TrimEnd('/')))\r
172                 .Any();\r
173             return !mismatch;\r
174         }\r
175 \r
176 \r
177         /// <summary>\r
178         /// Checks whether a Uri is directly below a root Uri\r
179         /// </summary>\r
180         /// <param name="target"></param>\r
181         /// <param name="root"></param>\r
182         /// <returns></returns>\r
183         public static bool IsAtOrDirectlyBelow(this Uri target,Uri root)\r
184         {\r
185             if (root==null)\r
186                 throw new ArgumentNullException("root");\r
187             if (target==null)\r
188                 throw new ArgumentNullException("target");\r
189             Contract.EndContractBlock();\r
190 \r
191             if (target.Equals(root))\r
192                 return true;\r
193             return\r
194                 //If the target is directly below the root, it will have exactly \r
195                 //one segment more than the root            \r
196                 target.Segments.Length == root.Segments.Length + 1\r
197                 //Ensure that the candidate target is indeed below the root\r
198                 && target.IsAtOrBelow(root);\r
199         }\r
200 \r
201         /// <summary>\r
202         /// Checkes whether a file path is below a root path\r
203         /// </summary>\r
204         /// <param name="targetPath"></param>\r
205         /// <param name="rootPath"></param>\r
206         /// <returns></returns>\r
207         public static bool IsAtOrBelow(this string targetPath, string rootPath)\r
208         {\r
209             if(String.IsNullOrWhiteSpace(targetPath))\r
210                 throw new ArgumentNullException("targetPath");\r
211             if(String.IsNullOrWhiteSpace(rootPath))\r
212                 throw new ArgumentNullException("rootPath");\r
213             Contract.EndContractBlock();\r
214 \r
215             var targetSegments = targetPath.Split('\\');\r
216             var rootSegments = rootPath.Split('\\');\r
217 \r
218             return InnerAtOrBelow(targetSegments, rootSegments);\r
219         }\r
220 \r
221         /// <summary>\r
222         /// Checks whether a file path is directly below a root path\r
223         /// </summary>\r
224         /// <param name="targetPath"></param>\r
225         /// <param name="rootPath"></param>\r
226         /// <returns></returns>\r
227         public static bool IsAtOrDirectlyBelow(this string targetPath, string rootPath)\r
228         {\r
229             if (String.IsNullOrWhiteSpace(targetPath))\r
230                 throw new ArgumentNullException("targetPath");\r
231             if (String.IsNullOrWhiteSpace(rootPath))\r
232                 throw new ArgumentNullException("rootPath");\r
233             Contract.EndContractBlock();\r
234 \r
235             if (targetPath==rootPath)\r
236                 return true;\r
237             var targetSegments = targetPath.Split('\\');\r
238             var rootSegments = rootPath.Split('\\');\r
239 \r
240             return\r
241                 //If the target is directly below the root, it will have exactly \r
242                 //one segment more than the root            \r
243                 targetSegments.Length == rootSegments.Length + 1\r
244                 //Ensure that the candidate target is indeed below the root\r
245                 && InnerAtOrBelow(targetSegments, rootSegments);\r
246         }\r
247 \r
248         /// <summary>\r
249         /// Returns the part of the target string that comes after the prefix string.\r
250         /// The target is not modified if it doesn't start with the prefix string\r
251         /// </summary>\r
252         /// <param name="target"></param>\r
253         /// <param name="prefix"></param>\r
254         /// <returns></returns>\r
255         public static string After(this string target,string prefix)\r
256         {            \r
257             //If the prefix or the target are empty, return the target\r
258             if (String.IsNullOrWhiteSpace(prefix) || String.IsNullOrWhiteSpace(target))\r
259                 return target;\r
260             if (target.StartsWith(prefix))\r
261                 return target.Remove(0,prefix.Length);\r
262             return target;\r
263         }\r
264     }\r
265 \r
266 \r
267 }\r