Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (10.7 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
            var mismatch = rootSegments
170
                .Where((t, i) => !String.Equals(targetSegments[i], t))
171
                .Any();
172
            return !mismatch;
173
        }
174

    
175

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

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

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

    
212
            var targetSegments = targetPath.Split('\\');
213
            var rootSegments = rootPath.Split('\\');
214

    
215
            return InnerAtOrBelow(targetSegments, rootSegments);
216
        }
217

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

    
232

    
233
            var targetSegments = targetPath.Split('\\');
234
            var rootSegments = rootPath.Split('\\');
235

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

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

    
262

    
263
}