Extracted upload/download functionality from NetworkAgent to Uploader.cs and Download...
[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             var mismatch = rootSegments\r
170                 .Where((t, i) => !String.Equals(targetSegments[i], t))\r
171                 .Any();\r
172             return !mismatch;\r
173         }\r
174 \r
175 \r
176         /// <summary>\r
177         /// Checks whether a Uri is directly below a root Uri\r
178         /// </summary>\r
179         /// <param name="target"></param>\r
180         /// <param name="root"></param>\r
181         /// <returns></returns>\r
182         public static bool IsAtOrDirectlyBelow(this Uri target,Uri root)\r
183         {\r
184             if (root==null)\r
185                 throw new ArgumentNullException("root");\r
186             if (target==null)\r
187                 throw new ArgumentNullException("target");\r
188             Contract.EndContractBlock();\r
189 \r
190             return\r
191                 //If the target is directly below the root, it will have exactly \r
192                 //one segment more than the root            \r
193                 target.Segments.Length == root.Segments.Length + 1\r
194                 //Ensure that the candidate target is indeed below the root\r
195                 && target.IsAtOrBelow(root);\r
196         }\r
197 \r
198         /// <summary>\r
199         /// Checkes whether a file path is below a root path\r
200         /// </summary>\r
201         /// <param name="targetPath"></param>\r
202         /// <param name="rootPath"></param>\r
203         /// <returns></returns>\r
204         public static bool IsAtOrBelow(this string targetPath, string rootPath)\r
205         {\r
206             if(String.IsNullOrWhiteSpace(targetPath))\r
207                 throw new ArgumentNullException("targetPath");\r
208             if(String.IsNullOrWhiteSpace(rootPath))\r
209                 throw new ArgumentNullException("rootPath");\r
210             Contract.EndContractBlock();\r
211 \r
212             var targetSegments = targetPath.Split('\\');\r
213             var rootSegments = rootPath.Split('\\');\r
214 \r
215             return InnerAtOrBelow(targetSegments, rootSegments);\r
216         }\r
217 \r
218         /// <summary>\r
219         /// Checks whether a file path is directly below a root path\r
220         /// </summary>\r
221         /// <param name="targetPath"></param>\r
222         /// <param name="rootPath"></param>\r
223         /// <returns></returns>\r
224         public static bool IsAtOrDirectlyBelow(this string targetPath, string rootPath)\r
225         {\r
226             if (String.IsNullOrWhiteSpace(targetPath))\r
227                 throw new ArgumentNullException("targetPath");\r
228             if (String.IsNullOrWhiteSpace(rootPath))\r
229                 throw new ArgumentNullException("rootPath");\r
230             Contract.EndContractBlock();\r
231 \r
232 \r
233             var targetSegments = targetPath.Split('\\');\r
234             var rootSegments = rootPath.Split('\\');\r
235 \r
236             return\r
237                 //If the target is directly below the root, it will have exactly \r
238                 //one segment more than the root            \r
239                 targetSegments.Length == rootSegments.Length + 1\r
240                 //Ensure that the candidate target is indeed below the root\r
241                 && InnerAtOrBelow(targetSegments, rootSegments);\r
242         }\r
243 \r
244         /// <summary>\r
245         /// Returns the part of the target string that comes after the prefix string.\r
246         /// The target is not modified if it doesn't start with the prefix string\r
247         /// </summary>\r
248         /// <param name="target"></param>\r
249         /// <param name="prefix"></param>\r
250         /// <returns></returns>\r
251         public static string After(this string target,string prefix)\r
252         {            \r
253             //If the prefix or the target are empty, return the target\r
254             if (String.IsNullOrWhiteSpace(prefix) || String.IsNullOrWhiteSpace(target))\r
255                 return target;\r
256             if (target.StartsWith(prefix))\r
257                 return target.Remove(0,prefix.Length);\r
258             return target;\r
259         }\r
260     }\r
261 \r
262 \r
263 }\r