Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / CollectionExtensions.cs @ dccd340f

History | View | Annotate | Download (10.2 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>> generate)
59
        {
60
            foreach (var item in list)
61
            {
62
                if (match(item))
63
                    foreach (var newItem in generate(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
        /// <summary>
232
        /// Returns the part of the target string that comes after the prefix string.
233
        /// The target is not modified if it doesn't start with the prefix string
234
        /// </summary>
235
        /// <param name="target"></param>
236
        /// <param name="remove"></param>
237
        /// <returns></returns>
238
        public static string After(this string target,string prefix)
239
        {            
240
            //If the prefix or the target are empty, return the target
241
            if (String.IsNullOrWhiteSpace(prefix) || String.IsNullOrWhiteSpace(target))
242
                return target;
243
            if (target.StartsWith(prefix))
244
                return target.Remove(0,prefix.Length);
245
            return target;
246
        }
247
    }
248

    
249

    
250
}