2 /* -----------------------------------------------------------------------
\r
3 * <copyright file="CollectionExtensions.cs" company="GRNet">
\r
5 * Copyright 2011-2012 GRNET S.A. All rights reserved.
\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
11 * 1. Redistributions of source code must retain the above
\r
12 * copyright notice, this list of conditions and the following
\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
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
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
39 * -----------------------------------------------------------------------
\r
42 using System.Collections.Concurrent;
\r
43 using System.Diagnostics.Contracts;
\r
44 using Pithos.Interfaces;
\r
46 using System.Collections.Generic;
\r
49 namespace Pithos.Core.Agents
\r
53 /// Extension methods for collections
\r
55 public static class CollectionExtensions
\r
57 public static IEnumerable<T> Replace<T>(this IEnumerable<T> list,Func<T,bool> match, Func<T,IEnumerable<T>> generate)
\r
59 foreach (var item in list)
\r
62 foreach (var newItem in generate(item))
\r
64 yield return newItem;
\r
73 /// Remove the first message in a queue that matches the predicate
\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
81 //Can this work? Dequeue all items
\r
82 //and then enqueue everything except the filtered items
\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
88 var alreadyFound = false;
\r
89 while (queue.TryDequeue(out message))
\r
91 if (!predicate(message) || alreadyFound)
\r
92 temp.Enqueue(message);
\r
95 alreadyFound = true;
\r
99 queue.AddFromEnumerable(temp);
\r
104 /// Return only the info objects that are below one of the filter Uris
\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
111 if (filterUris == null)
\r
113 if (filterUris.Count == 0)
\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
119 return filteredUris;
\r
123 /// Return only the info objects that are directly below one of the filter Uris
\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
130 if (filterUris == null)
\r
132 if (filterUris.Count == 0)
\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
138 return filteredUris;
\r
142 /// Checkes whether a Uri is below a root Uri
\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
150 throw new ArgumentNullException("root");
\r
152 throw new ArgumentNullException("target");
\r
153 Contract.EndContractBlock();
\r
155 var targetSegments = target.Segments;
\r
156 var rootSegments = root.Segments;
\r
158 return InnerAtOrBelow(targetSegments, rootSegments);
\r
161 private static bool InnerAtOrBelow(string[] targetSegments, string[] rootSegments)
\r
163 //If the uri is shorter than the root, no point in comparing
\r
164 if (targetSegments.Length < rootSegments.Length)
\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
177 /// Checks whether a Uri is directly below a root Uri
\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
185 throw new ArgumentNullException("root");
\r
187 throw new ArgumentNullException("target");
\r
188 Contract.EndContractBlock();
\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
199 /// Checkes whether a file path is below a root path
\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
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
212 var targetSegments = targetPath.Split('\\');
\r
213 var rootSegments = rootPath.Split('\\');
\r
215 return InnerAtOrBelow(targetSegments, rootSegments);
\r
219 /// Checks whether a file path is directly below a root path
\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
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
233 var targetSegments = targetPath.Split('\\');
\r
234 var rootSegments = rootPath.Split('\\');
\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
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
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
253 //If the prefix or the target are empty, return the target
\r
254 if (String.IsNullOrWhiteSpace(prefix) || String.IsNullOrWhiteSpace(target))
\r
256 if (target.StartsWith(prefix))
\r
257 return target.Remove(0,prefix.Length);
\r