Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / CollectionExtensions.cs @ 8f44fd3a

History | View | Annotate | Download (11 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
            //DON'T FORGET that Uri segments include the slashes. Must remove them to ensure proper checks
170
            var mismatch = rootSegments
171
                .Where((t, i) => !String.Equals(targetSegments[i].TrimEnd('/'), t.TrimEnd('/'),StringComparison.InvariantCultureIgnoreCase))
172
                .Any();
173
            return !mismatch;
174
        }
175

    
176

    
177
        /// <summary>
178
        /// Checks whether a Uri is directly below a root Uri
179
        /// </summary>
180
        /// <param name="target"></param>
181
        /// <param name="root"></param>
182
        /// <returns></returns>
183
        public static bool IsAtOrDirectlyBelow(this Uri target,Uri root)
184
        {
185
            if (root==null)
186
                throw new ArgumentNullException("root");
187
            if (target==null)
188
                throw new ArgumentNullException("target");
189
            Contract.EndContractBlock();
190

    
191
            if (target.Equals(root))
192
                return true;
193
            return
194
                //If the target is directly below the root, it will have exactly 
195
                //one segment more than the root            
196
                target.Segments.Length == root.Segments.Length + 1
197
                //Ensure that the candidate target is indeed below the root
198
                && target.IsAtOrBelow(root);
199
        }
200

    
201
        /// <summary>
202
        /// Checkes whether a file path is below a root path
203
        /// </summary>
204
        /// <param name="targetPath"></param>
205
        /// <param name="rootPath"></param>
206
        /// <returns></returns>
207
        public static bool IsAtOrBelow(this string targetPath, string rootPath)
208
        {
209
            if(String.IsNullOrWhiteSpace(targetPath))
210
                throw new ArgumentNullException("targetPath");
211
            if(String.IsNullOrWhiteSpace(rootPath))
212
                throw new ArgumentNullException("rootPath");
213
            Contract.EndContractBlock();
214

    
215
            var targetSegments = targetPath.Split('\\');
216
            var rootSegments = rootPath.Split('\\');
217

    
218
            return InnerAtOrBelow(targetSegments, rootSegments);
219
        }
220

    
221
        /// <summary>
222
        /// Checks whether a file path is directly below a root path
223
        /// </summary>
224
        /// <param name="targetPath"></param>
225
        /// <param name="rootPath"></param>
226
        /// <returns></returns>
227
        public static bool IsAtOrDirectlyBelow(this string targetPath, string rootPath)
228
        {
229
            if (String.IsNullOrWhiteSpace(targetPath))
230
                throw new ArgumentNullException("targetPath");
231
            if (String.IsNullOrWhiteSpace(rootPath))
232
                throw new ArgumentNullException("rootPath");
233
            Contract.EndContractBlock();
234

    
235
            if (targetPath==rootPath)
236
                return true;
237
            var targetSegments = targetPath.Split('\\');
238
            var rootSegments = rootPath.Split('\\');
239

    
240
            return
241
                //If the target is directly below the root, it will have exactly 
242
                //one segment more than the root            
243
                targetSegments.Length == rootSegments.Length + 1
244
                //Ensure that the candidate target is indeed below the root
245
                && InnerAtOrBelow(targetSegments, rootSegments);
246
        }
247

    
248
        /// <summary>
249
        /// Returns the part of the target string that comes after the prefix string.
250
        /// The target is not modified if it doesn't start with the prefix string
251
        /// </summary>
252
        /// <param name="target"></param>
253
        /// <param name="prefix"></param>
254
        /// <returns></returns>
255
        public static string After(this string target,string prefix)
256
        {            
257
            //If the prefix or the target are empty, return the target
258
            if (String.IsNullOrWhiteSpace(prefix) || String.IsNullOrWhiteSpace(target))
259
                return target;
260
            if (target.StartsWith(prefix))
261
                return target.Remove(0,prefix.Length);
262
            return target;
263
        }
264
    }
265

    
266

    
267
}