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