root / trunk / Pithos.Core / Agents / CollectionExtensions.cs @ 99e6329f
History | View | Annotate | Download (9.4 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 |
|
46 |
namespace Pithos.Core.Agents |
47 |
{ |
48 |
using System; |
49 |
using System.Collections.Generic; |
50 |
using System.Linq; |
51 |
using System.Text; |
52 |
|
53 |
/// <summary> |
54 |
/// Extension methods for collections |
55 |
/// </summary> |
56 |
public static class CollectionExtensions |
57 |
{ |
58 |
public static IEnumerable<T> Replace<T>(this IEnumerable<T> list,Func<T,bool> match, Func<T,IEnumerable<T>> replace) |
59 |
{ |
60 |
foreach (var item in list) |
61 |
{ |
62 |
if (match(item)) |
63 |
foreach (var newItem in replace(item)) |
64 |
{ |
65 |
yield return newItem; |
66 |
} |
67 |
else |
68 |
yield return item; |
69 |
} |
70 |
} |
71 |
|
72 |
|
73 |
/// <summary> |
74 |
/// Remove the first message in a queue that matches the predicate |
75 |
/// </summary> |
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 |
Contract.Requires(root != null); |
150 |
Contract.Requires(target != null); |
151 |
|
152 |
var targetSegments = target.Segments; |
153 |
var rootSegments = root.Segments; |
154 |
|
155 |
return InnerAtOrBelow(targetSegments, rootSegments); |
156 |
} |
157 |
|
158 |
private static bool InnerAtOrBelow(string[] targetSegments, string[] rootSegments) |
159 |
{ |
160 |
//If the uri is shorter than the root, no point in comparing |
161 |
if (targetSegments.Length < rootSegments.Length) |
162 |
return false; |
163 |
//If the uri is below the root, it should match the root's segments one by one |
164 |
//If there is any root segment that doesn't match its corresponding target segment, |
165 |
//the target is not below the root |
166 |
var mismatch = rootSegments |
167 |
.Where((t, i) => !String.Equals(targetSegments[i], t)) |
168 |
.Any(); |
169 |
return !mismatch; |
170 |
} |
171 |
|
172 |
|
173 |
/// <summary> |
174 |
/// Checks whether a Uri is directly below a root Uri |
175 |
/// </summary> |
176 |
/// <param name="target"></param> |
177 |
/// <param name="root"></param> |
178 |
/// <returns></returns> |
179 |
public static bool IsAtOrDirectlyBelow(this Uri target,Uri root) |
180 |
{ |
181 |
Contract.Requires(root!=null); |
182 |
Contract.Requires(target!=null); |
183 |
|
184 |
return |
185 |
//If the target is directly below the root, it will have exactly |
186 |
//one segment more than the root |
187 |
target.Segments.Length == root.Segments.Length + 1 |
188 |
//Ensure that the candidate target is indeed below the root |
189 |
&& target.IsAtOrBelow(root); |
190 |
} |
191 |
|
192 |
/// <summary> |
193 |
/// Checkes whether a file path is below a root path |
194 |
/// </summary> |
195 |
/// <param name="targetPath"></param> |
196 |
/// <param name="rootPath"></param> |
197 |
/// <returns></returns> |
198 |
public static bool IsAtOrBelow(this string targetPath, string rootPath) |
199 |
{ |
200 |
Contract.Requires(!String.IsNullOrWhiteSpace(targetPath)); |
201 |
Contract.Requires(!String.IsNullOrWhiteSpace(rootPath)); |
202 |
|
203 |
var targetSegments = targetPath.Split('\\'); |
204 |
var rootSegments = rootPath.Split('\\'); |
205 |
|
206 |
return InnerAtOrBelow(targetSegments, rootSegments); |
207 |
} |
208 |
|
209 |
/// <summary> |
210 |
/// Checks whether a file path is directly below a root path |
211 |
/// </summary> |
212 |
/// <param name="targetPath"></param> |
213 |
/// <param name="rootPath"></param> |
214 |
/// <returns></returns> |
215 |
public static bool IsAtOrDirectlyBelow(this string targetPath, string rootPath) |
216 |
{ |
217 |
Contract.Requires(!String.IsNullOrWhiteSpace(targetPath)); |
218 |
Contract.Requires(!String.IsNullOrWhiteSpace(rootPath)); |
219 |
|
220 |
var targetSegments = targetPath.Split('\\'); |
221 |
var rootSegments = rootPath.Split('\\'); |
222 |
|
223 |
return |
224 |
//If the target is directly below the root, it will have exactly |
225 |
//one segment more than the root |
226 |
targetSegments.Length == rootSegments.Length + 1 |
227 |
//Ensure that the candidate target is indeed below the root |
228 |
&& InnerAtOrBelow(targetSegments, rootSegments); |
229 |
} |
230 |
} |
231 |
} |