#region /* ----------------------------------------------------------------------- * * * Copyright 2011-2012 GRNET S.A. All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and * documentation are those of the authors and should not be * interpreted as representing official policies, either expressed * or implied, of GRNET S.A. * * ----------------------------------------------------------------------- */ #endregion using System.Collections.Concurrent; using System.Diagnostics.Contracts; using Pithos.Interfaces; using System; using System.Collections.Generic; using System.Linq; namespace Pithos.Core.Agents { /// /// Extension methods for collections /// public static class CollectionExtensions { public static IEnumerable Replace(this IEnumerable list,Func match, Func> generate) { foreach (var item in list) { if (match(item)) foreach (var newItem in generate(item)) { yield return newItem; } else yield return item; } } /// /// Remove the first message in a queue that matches the predicate /// /// The queue /// The condition to match /// Removes the first message that matches the predicate by dequeing all /// messages and re-enqueing all except the first matching message public static void RemoveFirst(this ConcurrentQueue queue, Func predicate) { //Can this work? Dequeue all items //and then enqueue everything except the filtered items //Possible problems: //* A matching item may be dequeued between one TryDequeue and the next var temp = new Queue(); TMessage message; var alreadyFound = false; while (queue.TryDequeue(out message)) { if (!predicate(message) || alreadyFound) temp.Enqueue(message); else { alreadyFound = true; } } queue.AddFromEnumerable(temp); } /// /// Return only the info objects that are below one of the filter Uris /// /// ObjectInfo items to filter /// List of filter Uris /// An enumerable of ObjectInfo items. If the list of filter Uris is empty or null, the original list is returned public static IEnumerable FilterBelow(this IEnumerable infos,List filterUris ) { if (filterUris == null) return infos; if (filterUris.Count == 0) return infos; //Allow all objects whose Uris start with any of the filters var filteredUris = from info in infos where filterUris.Any(f => info.Uri.IsAtOrBelow(f)) select info; return filteredUris; } /// /// Return only the info objects that are directly below one of the filter Uris /// /// ObjectInfo items to filter /// List of filter Uris /// An enumerable of ObjectInfo items. If the list of filter Uris is empty or null, the original list is returned public static IEnumerable FilterDirectlyBelow(this IEnumerable infos, List filterUris) { if (filterUris == null) return infos; if (filterUris.Count == 0) return infos; //Allow all objects whose Uris start with any of the filters var filteredUris = from info in infos where filterUris.Any(f => info.Uri.IsAtOrDirectlyBelow(f)) select info; return filteredUris; } /// /// Checkes whether a Uri is below a root Uri /// /// /// /// public static bool IsAtOrBelow(this Uri target,Uri root) { if(root == null) throw new ArgumentNullException("root"); if(target == null) throw new ArgumentNullException("target"); Contract.EndContractBlock(); var targetSegments = target.Segments; var rootSegments = root.Segments; return InnerAtOrBelow(targetSegments, rootSegments); } private static bool InnerAtOrBelow(string[] targetSegments, string[] rootSegments) { //If the uri is shorter than the root, no point in comparing if (targetSegments.Length < rootSegments.Length) return false; //If the uri is below the root, it should match the root's segments one by one //If there is any root segment that doesn't match its corresponding target segment, //the target is not below the root //DON'T FORGET that Uri segments include the slashes. Must remove them to ensure proper checks var mismatch = rootSegments .Where((t, i) => !String.Equals(targetSegments[i].TrimEnd('/'), t.TrimEnd('/'))) .Any(); return !mismatch; } /// /// Checks whether a Uri is directly below a root Uri /// /// /// /// public static bool IsAtOrDirectlyBelow(this Uri target,Uri root) { if (root==null) throw new ArgumentNullException("root"); if (target==null) throw new ArgumentNullException("target"); Contract.EndContractBlock(); if (target.Equals(root)) return true; return //If the target is directly below the root, it will have exactly //one segment more than the root target.Segments.Length == root.Segments.Length + 1 //Ensure that the candidate target is indeed below the root && target.IsAtOrBelow(root); } /// /// Checkes whether a file path is below a root path /// /// /// /// public static bool IsAtOrBelow(this string targetPath, string rootPath) { if(String.IsNullOrWhiteSpace(targetPath)) throw new ArgumentNullException("targetPath"); if(String.IsNullOrWhiteSpace(rootPath)) throw new ArgumentNullException("rootPath"); Contract.EndContractBlock(); var targetSegments = targetPath.Split('\\'); var rootSegments = rootPath.Split('\\'); return InnerAtOrBelow(targetSegments, rootSegments); } /// /// Checks whether a file path is directly below a root path /// /// /// /// public static bool IsAtOrDirectlyBelow(this string targetPath, string rootPath) { if (String.IsNullOrWhiteSpace(targetPath)) throw new ArgumentNullException("targetPath"); if (String.IsNullOrWhiteSpace(rootPath)) throw new ArgumentNullException("rootPath"); Contract.EndContractBlock(); if (targetPath==rootPath) return true; var targetSegments = targetPath.Split('\\'); var rootSegments = rootPath.Split('\\'); return //If the target is directly below the root, it will have exactly //one segment more than the root targetSegments.Length == rootSegments.Length + 1 //Ensure that the candidate target is indeed below the root && InnerAtOrBelow(targetSegments, rootSegments); } /// /// Returns the part of the target string that comes after the prefix string. /// The target is not modified if it doesn't start with the prefix string /// /// /// /// public static string After(this string target,string prefix) { //If the prefix or the target are empty, return the target if (String.IsNullOrWhiteSpace(prefix) || String.IsNullOrWhiteSpace(target)) return target; if (target.StartsWith(prefix)) return target.Remove(0,prefix.Length); return target; } } }