root / trunk / Pithos.Core / Agents / CollectionExtensions.cs @ d78d765c
History | View | Annotate | Download (10.7 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 Pithos.Interfaces; |
45 |
using System; |
46 |
using System.Collections.Generic; |
47 |
using System.Linq; |
48 |
|
49 |
namespace Pithos.Core.Agents |
50 |
{ |
51 |
|
52 |
/// <summary> |
53 |
/// Extension methods for collections |
54 |
/// </summary> |
55 |
public static class CollectionExtensions |
56 |
{ |
57 |
public static IEnumerable<T> Replace<T>(this IEnumerable<T> list,Func<T,bool> match, Func<T,IEnumerable<T>> generate) |
58 |
{ |
59 |
foreach (var item in list) |
60 |
{ |
61 |
if (match(item)) |
62 |
foreach (var newItem in generate(item)) |
63 |
{ |
64 |
yield return newItem; |
65 |
} |
66 |
else |
67 |
yield return item; |
68 |
} |
69 |
} |
70 |
|
71 |
|
72 |
/// <summary> |
73 |
/// Remove the first message in a queue that matches the predicate |
74 |
/// </summary> |
75 |
/// <param name="queue">The queue</param> |
76 |
/// <param name="predicate">The condition to match</param> |
77 |
/// <remarks>Removes the first message that matches the predicate by dequeing all |
78 |
/// messages and re-enqueing all except the first matching message</remarks> |
79 |
public static void RemoveFirst<TMessage>(this ConcurrentQueue<TMessage> queue, Func<TMessage, bool> predicate) |
80 |
{ |
81 |
//Can this work? Dequeue all items |
82 |
//and then enqueue everything except the filtered items |
83 |
|
84 |
//Possible problems: |
85 |
//* A matching item may be dequeued between one TryDequeue and the next |
86 |
var temp = new Queue<TMessage>(); |
87 |
TMessage message; |
88 |
var alreadyFound = false; |
89 |
while (queue.TryDequeue(out message)) |
90 |
{ |
91 |
if (!predicate(message) || alreadyFound) |
92 |
temp.Enqueue(message); |
93 |
else |
94 |
{ |
95 |
alreadyFound = true; |
96 |
} |
97 |
} |
98 |
|
99 |
queue.AddFromEnumerable(temp); |
100 |
} |
101 |
|
102 |
|
103 |
/// <summary> |
104 |
/// Return only the info objects that are below one of the filter Uris |
105 |
/// </summary> |
106 |
/// <param name="infos">ObjectInfo items to filter</param> |
107 |
/// <param name="filterUris">List of filter Uris</param> |
108 |
/// <returns>An enumerable of ObjectInfo items. If the list of filter Uris is empty or null, the original list is returned</returns> |
109 |
public static IEnumerable<ObjectInfo> FilterBelow(this IEnumerable<ObjectInfo> infos,List<Uri> filterUris ) |
110 |
{ |
111 |
if (filterUris == null) |
112 |
return infos; |
113 |
if (filterUris.Count == 0) |
114 |
return infos; |
115 |
//Allow all objects whose Uris start with any of the filters |
116 |
var filteredUris = from info in infos |
117 |
where filterUris.Any(f => info.Uri.IsAtOrBelow(f)) |
118 |
select info; |
119 |
return filteredUris; |
120 |
} |
121 |
|
122 |
/// <summary> |
123 |
/// Return only the info objects that are directly below one of the filter Uris |
124 |
/// </summary> |
125 |
/// <param name="infos">ObjectInfo items to filter</param> |
126 |
/// <param name="filterUris">List of filter Uris</param> |
127 |
/// <returns>An enumerable of ObjectInfo items. If the list of filter Uris is empty or null, the original list is returned</returns> |
128 |
public static IEnumerable<ObjectInfo> FilterDirectlyBelow(this IEnumerable<ObjectInfo> infos, List<Uri> filterUris) |
129 |
{ |
130 |
if (filterUris == null) |
131 |
return infos; |
132 |
if (filterUris.Count == 0) |
133 |
return infos; |
134 |
//Allow all objects whose Uris start with any of the filters |
135 |
var filteredUris = from info in infos |
136 |
where filterUris.Any(f => info.Uri.IsAtOrDirectlyBelow(f)) |
137 |
select info; |
138 |
return filteredUris; |
139 |
} |
140 |
|
141 |
/// <summary> |
142 |
/// Checkes whether a Uri is below a root Uri |
143 |
/// </summary> |
144 |
/// <param name="target"></param> |
145 |
/// <param name="root"></param> |
146 |
/// <returns></returns> |
147 |
public static bool IsAtOrBelow(this Uri target,Uri root) |
148 |
{ |
149 |
if(root == null) |
150 |
throw new ArgumentNullException("root"); |
151 |
if(target == null) |
152 |
throw new ArgumentNullException("target"); |
153 |
Contract.EndContractBlock(); |
154 |
|
155 |
var targetSegments = target.Segments; |
156 |
var rootSegments = root.Segments; |
157 |
|
158 |
return InnerAtOrBelow(targetSegments, rootSegments); |
159 |
} |
160 |
|
161 |
private static bool InnerAtOrBelow(string[] targetSegments, string[] rootSegments) |
162 |
{ |
163 |
//If the uri is shorter than the root, no point in comparing |
164 |
if (targetSegments.Length < rootSegments.Length) |
165 |
return false; |
166 |
//If the uri is below the root, it should match the root's segments one by one |
167 |
//If there is any root segment that doesn't match its corresponding target segment, |
168 |
//the target is not below the root |
169 |
var mismatch = rootSegments |
170 |
.Where((t, i) => !String.Equals(targetSegments[i], t)) |
171 |
.Any(); |
172 |
return !mismatch; |
173 |
} |
174 |
|
175 |
|
176 |
/// <summary> |
177 |
/// Checks whether a Uri is directly below a root Uri |
178 |
/// </summary> |
179 |
/// <param name="target"></param> |
180 |
/// <param name="root"></param> |
181 |
/// <returns></returns> |
182 |
public static bool IsAtOrDirectlyBelow(this Uri target,Uri root) |
183 |
{ |
184 |
if (root==null) |
185 |
throw new ArgumentNullException("root"); |
186 |
if (target==null) |
187 |
throw new ArgumentNullException("target"); |
188 |
Contract.EndContractBlock(); |
189 |
|
190 |
return |
191 |
//If the target is directly below the root, it will have exactly |
192 |
//one segment more than the root |
193 |
target.Segments.Length == root.Segments.Length + 1 |
194 |
//Ensure that the candidate target is indeed below the root |
195 |
&& target.IsAtOrBelow(root); |
196 |
} |
197 |
|
198 |
/// <summary> |
199 |
/// Checkes whether a file path is below a root path |
200 |
/// </summary> |
201 |
/// <param name="targetPath"></param> |
202 |
/// <param name="rootPath"></param> |
203 |
/// <returns></returns> |
204 |
public static bool IsAtOrBelow(this string targetPath, string rootPath) |
205 |
{ |
206 |
if(String.IsNullOrWhiteSpace(targetPath)) |
207 |
throw new ArgumentNullException("targetPath"); |
208 |
if(String.IsNullOrWhiteSpace(rootPath)) |
209 |
throw new ArgumentNullException("rootPath"); |
210 |
Contract.EndContractBlock(); |
211 |
|
212 |
var targetSegments = targetPath.Split('\\'); |
213 |
var rootSegments = rootPath.Split('\\'); |
214 |
|
215 |
return InnerAtOrBelow(targetSegments, rootSegments); |
216 |
} |
217 |
|
218 |
/// <summary> |
219 |
/// Checks whether a file path is directly below a root path |
220 |
/// </summary> |
221 |
/// <param name="targetPath"></param> |
222 |
/// <param name="rootPath"></param> |
223 |
/// <returns></returns> |
224 |
public static bool IsAtOrDirectlyBelow(this string targetPath, string rootPath) |
225 |
{ |
226 |
if (String.IsNullOrWhiteSpace(targetPath)) |
227 |
throw new ArgumentNullException("targetPath"); |
228 |
if (String.IsNullOrWhiteSpace(rootPath)) |
229 |
throw new ArgumentNullException("rootPath"); |
230 |
Contract.EndContractBlock(); |
231 |
|
232 |
|
233 |
var targetSegments = targetPath.Split('\\'); |
234 |
var rootSegments = rootPath.Split('\\'); |
235 |
|
236 |
return |
237 |
//If the target is directly below the root, it will have exactly |
238 |
//one segment more than the root |
239 |
targetSegments.Length == rootSegments.Length + 1 |
240 |
//Ensure that the candidate target is indeed below the root |
241 |
&& InnerAtOrBelow(targetSegments, rootSegments); |
242 |
} |
243 |
|
244 |
/// <summary> |
245 |
/// Returns the part of the target string that comes after the prefix string. |
246 |
/// The target is not modified if it doesn't start with the prefix string |
247 |
/// </summary> |
248 |
/// <param name="target"></param> |
249 |
/// <param name="prefix"></param> |
250 |
/// <returns></returns> |
251 |
public static string After(this string target,string prefix) |
252 |
{ |
253 |
//If the prefix or the target are empty, return the target |
254 |
if (String.IsNullOrWhiteSpace(prefix) || String.IsNullOrWhiteSpace(target)) |
255 |
return target; |
256 |
if (target.StartsWith(prefix)) |
257 |
return target.Remove(0,prefix.Length); |
258 |
return target; |
259 |
} |
260 |
} |
261 |
|
262 |
|
263 |
} |